Predict the State Before Running the Code
Before running cart.add("laptop"); cart.add("laptop"); cart.add("mouse"), write what the cart will contain. Then run it. The gap between predicted and actual is where your model of the code is wrong — and a prediction about state, not just about the outcome, tells you which line.
The situation, the reflex, and why it stalls
Every lesson starts where being stuck starts: someone has a problem, and the first move that comes to mind feels like progress.
The code is written. What do you do right before pressing run so that the run teaches you something instead of just reassuring you?
You have addItem in the editor and a script that adds a laptop, a laptop and a mouse. Your finger is on run. If it prints something plausible you will move on; if it prints something odd you will start changing lines. Either way you will not have learned whether you understood the code.
Run it and look. The output is the truth, and reading it is faster than thinking about what it should be.
The output is [Laptop × 1, Laptop × 1, Mouse × 1] and it looks plausible — three things were added, three things are there. Without a prediction there is nothing for the duplicate to contradict, and it ships.
- The output is [Laptop × 1, Laptop × 1, Mouse × 1] and it looks plausible — three things were added, three things are there. Without a prediction there is nothing for the duplicate to contradict, and it ships.
- The output is wrong in a way you notice, and the reflex becomes editing until it looks right. The line that changes is whichever one was under the cursor, and the fix that lands is the first one that produces the expected picture, whether or not it encodes the rule.
- The run confirms the outcome — "it printed a cart" — and says nothing about the state that produced it. A quantity of 2 on one entry and two entries of 1 print similar totals; only a prediction about entries would have distinguished them.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- Before running, write the state you expect after each step, not just the final output: after the first add, after the second, after the third. A prediction about state names entries and quantities; a prediction about outcome names a total or a line count, and cannot tell a duplicate from an increment.
- Write the prediction where you cannot quietly revise it — a comment above the script, a line in the notebook. The point is to be wrong on record, because a wrong prediction is the only evidence you have that your model of the code differs from the code.
- Run, and compare step by step. The first step where predicted and actual differ is where the model is wrong, and the operation, branch and assumption behind that step are the things to inspect — not the whole function, and not the last line that ran.
- When they match, the run has proven something: that you understood the code well enough to predict its state. When they differ, do not edit yet. Explain the difference first — which branch ran that you thought would not — and only then decide whether the code or the prediction was wrong.
The prediction, per step, as state
The script and the prediction, written before the run. Each step names entries and quantities. Notice that the second step predicts what does not change — the number of entries — because that is precisely what a buggy add would violate, and what a total would hide.
Cart = [ Laptop × 1 ]
Predicted: [ Laptop × 2 ] Actual: [ Laptop × 1, Laptop × 1 ]
1// predicted, before running:2// after add laptop: [laptop × 1] — one entry3// after add laptop: [laptop × 2] — still one entry; quantity changed4// after add mouse: [laptop × 2, mouse × 1] — two entries5const cart = createCart()6addItem(cart, 'laptop'); addItem(cart, 'laptop'); addItem(cart, 'mouse')7console.log(cart.items)The comment is the falsifiable part. If the run prints three entries, step two is where the model and the code parted.
The trace that explains the mismatch
A mismatch at step 2 points at one operation. Tracing that operation through its five stops shows which branch ran and which assumption was wrong; in the buggy version the lookup stop is empty, and that emptiness is the bug. The trace is what you write before editing.
- inputproductId = laptop, quantity = 1; cart = [ Laptop × 1 ]
- lookupNone — the code does not look for an existing entry. This is the stop the prediction assumed and the code skipped.
- branchNone; every call takes the same path.
- mutationappend { laptop, 1 } to cart.items
- output[ Laptop × 1, Laptop × 1 ] — two entries. The assumption "the code looks before it writes" was wrong; the rule "one entry per product" is not encoded.
Predict, run, compare, explain
The loop, as Cart Lab runs it: it will not execute a script until the prediction is entered, and it shows predicted and actual side by side per step. The differs-from note: Prediction Before Execution in the experiments module predicts what an experiment will *show* — a measurement, a direction. This lesson predicts what the state will *be* after each operation. The first tells you whether a hypothesis held; the second tells you which line does not do what you thought.
- 1Predict
Write the state after each step — entries, quantities, what does not change — before the run.
fails by A prediction about the outcome ("three items") that two different states satisfy.
- 2Run
Execute the script once, without editing anything.
fails by Running, glancing, editing — the prediction never gets compared.
- 3Compare per step
Find the first step where predicted and actual differ.
fails by Comparing only the final state and losing where the divergence began.
- 4Explain
Name the operation, the branch that ran, and the assumption that was wrong.
fails by Changing a line before the explanation exists — the fix that makes the picture right may not encode the rule.
When every step matches, the run was still worth it: it is evidence that your model of the code is the code.
The implementation ladder
Concept, examples, pseudocode, code, tests, production — for the concept this lesson is about. Code is the fourth tab, not the first.
Shopping Cart = A temporary collection of products the user intends to purchase, held between browsing and checkout.
- Does a cart have identity? Yes, weakly. Two carts with the same items are still two carts, because each belongs to someone and will become a different order. It needs an id once it leaves memory; in memory the variable is the identity.
- Who owns it? A shopper — a logged-in user or an anonymous session. The owner is part of the state because "my cart" has to be findable again.
- How long does it exist? From the first add until checkout or abandonment. Whether it survives a reload, a closed browser or a login is not a property of the concept; it is a persistence decision made later, and each answer changes where the cart lives.
- Should it survive reload? Usually yes for a store, usually no for a demo. V1 in memory says no; V2 browser storage says yes on one device; V3 server storage says yes everywhere the user is logged in.
- Should it survive login? Only if an anonymous cart and a logged-in cart are merged — a rule that does not exist in V1 and appears as a modification later.
- itemscollection of CartItemkeepThe cart is its items; without them nothing else means anything.
- items[].productIdidkeepThe reference to what is being bought. The catalog owns the product; the cart only points at it.
- items[].quantityinteger > 0keepTwo laptops is one entry with quantity 2, not two entries — the rule "one entry per product" needs a quantity to hold.
- owneruser id or session iddependsSo the cart can be found again by the person it belongs to.
- items[].productNamestringderiveIt would be convenient to render the cart without a catalog lookup.
- items[].pricemoneydependsThe total needs a price per item.
- totalmoneydropEvery screen shows the total.
- currencycodedependsPrices need a currency to be added.
- createdAttimestampdropAbandoned carts might be expired or emailed about.
- update Add item — the updated cart
- delete Remove item — the updated cart
- update Change quantity — the updated cart
- read View items — the list of entries — product id and quantity — for rendering
- domain Calculate total — the sum of price × quantity over the entries
- delete Clear cart — the empty cart
- • Every quantity is greater than zero.
- • One logical entry per product.
- • The total is never negative.
- • An unknown product cannot be added.
- • Quantity cannot exceed available stock — if inventory is enforced here.
How to do it
Most important first.
- Predict per step, as state: "after step 2: [Laptop × 2], one entry". A per-run prediction hides where the divergence began (Before, Operation, After — and Exactly What Changed).
- Predict the changed list too — "quantity changes, entries do not" — because that is what a duplicate violates (Input → Lookup → Branch → Mutation → Output).
- Record the prediction before the run in a place you will see afterwards; the sim in Cart Lab refuses to run until you have.
- On a mismatch, name the operation, the branch and the assumption before touching a line (Expected, Actual, Which Operation, Which Branch, Which Assumption).
- Do this for received code especially: predicting a stranger's cart before running it is the fastest test of whether you have understood it or merely read it.
Worked on a concrete problem
The move has to produce something. This is what it produced.
- Script: add laptop; add laptop; add mouse. Prediction, per step: [Laptop × 1]; [Laptop × 2] with one entry; [Laptop × 2, Mouse × 1] with two entries. Run against the concept record's implementation: matches at every step. The run has confirmed the model, including the branch — not just that "something was added".
- Same script against a version that appends without a find. Prediction unchanged. Actual after step 2: [Laptop × 1, Laptop × 1] — two entries. The divergence is at step 2, the operation is addItem, the branch that ran was the append, and the assumption that was wrong is "the code looks before it writes". A prediction about the total would have missed it: 2000 either way.
- A prediction about outcome only: "three items in the cart". The buggy version prints three entries; the correct one prints two. Which is "three items"? The question has no answer, which is why the prediction must be about state.
How you know it worked
What now exists that did not before, and what question you can now ask.
- There is a written prediction, per step and in terms of entries and quantities, that predates the run.
- A mismatch is followed by a sentence naming the step, the operation and the branch — before any edit.
- Runs that match feel like evidence, because they were falsifiable.
The questions you can now ask
The field this whole domain exists for. After this lesson, these are the questions to put to an unfamiliar problem.
- ?After each step of this script, what will the state be — entries and quantities, not just totals?
- ?What would the output look like if the code were wrong in the way I most suspect?
- ?At which step does my prediction first differ from what ran, and which branch does that implicate?
- ?Have I written the prediction somewhere the run cannot make me forget it?
What can go wrong
- The prediction is written after a glance at the output and then "confirmed". It has to be recorded before the run, or it is a description.
- Predictions are made for the outcome and the state is inferred back from it, so a duplicate with the right total passes.
- Every run gets a prediction, including the tenth run of a script that has matched nine times. The move is for runs that could teach something — new code, changed code, received code — not a ritual.
- Predicting per step takes longer than pressing run, and on a script that has matched many times the prediction has nothing left to teach.
- A prediction commits you to a model, and a wrong model can make correct output look like a bug — the time goes into reconciling, not fixing.
- Predictions about state need the state to be inspectable; code that only exposes a total forces you to add a view before you can predict anything useful.
- "So this is the same as writing a test." A test is a prediction that runs automatically and forever; this is a prediction you make once, by hand, to check your understanding — and it works on code that has no tests yet, including code you did not write.
- "If the output looks right, the code is right." Falsifiable: [Laptop × 1, Laptop × 1, Mouse × 1] looks right to anyone who did not predict two entries. Output is only evidence against a prediction.
- "Predict the outcome" is the same as this lesson. It is the experiments module's move, and it predicts what an experiment will show. This one predicts what the state will be after each operation — the difference is that a state prediction locates the wrong line, and an outcome prediction only says a line is wrong.
Where this applies
Problem-solving advice is stated as universal far more often than it is. These labels say what each method is specific to — and where CONTESTED appears, the note gives the strongest form of the opposing view.
- GENERALPredicting state per step applies to any code that mutates something — a queue, a session store, a file — and the move is identical: entries and values, per step, recorded before the run.
- TEAM-SPECIFICA learner writes the prediction out; an experienced engineer does it silently for their own code and out loud for code they are reviewing, where the prediction is the review question — "what does this hold after the second call?"
- ILLUSTRATIVEThe script, the products and the totals (2000 either way) are invented to show why an outcome prediction cannot distinguish the two carts.
Where the depth lives
This domain asks the question and hands the answer off by name.