DebuggingGENERALCONTESTEDILLUSTRATIVE

Predict Before You Look

Before changing code or making an observation, say what you expect to see if your hypothesis is right and what you expect if it is wrong. The prediction is what turns a change into an experiment and stops "it looks fine" from meaning anything you like.

The moveWorked exampleNext questions

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 question

You are about to make a change or check a log. What sentence should you write first, and what does writing it protect you from?

The situation

You think the cart refactor broke checkout. You are about to add a log line, or revert the commit, or patch the handler — any of them is a minute away. Nothing stops you from just doing it and seeing.

The reflex

Do it and see. Looking is cheap; the prediction feels like a formality that delays the looking.

Why it stalls

Whatever you see is explained after the fact as consistent with the hypothesis. Humans are very good at this; a log line that says "cart loaded" confirms the refactor theory if you believed it and the provider theory if you believed that.

What the reflex produces — and fails to produce
  • Whatever you see is explained after the fact as consistent with the hypothesis. Humans are very good at this; a log line that says "cart loaded" confirms the refactor theory if you believed it and the provider theory if you believed that.
  • Without a stated prediction, a negative result is not recognised as one. The change is made, the symptom persists, and instead of "hypothesis dead" the conclusion is "must need another change too".
  • The observation is made at the wrong place because nobody said what it was for. A log line added "to see what's happening" is placed where it is easy, not where the hypothesis says the data goes wrong.
  • A change that works cannot be distinguished from a change that coincided with something else working. Without a prediction of *what else* should change, the only evidence is the symptom going away, which a restart also achieves.
ProblemUnderstandRequirementsConstraintsUnknownsDecompositionSmallest StepModelExperimentObserveDebugLearnIterate

The move

Precisely enough to apply it to a problem you have never seen — not a slogan.

  • Before any change or observation, write two sentences. If the hypothesis is right, I will see X. If it is wrong, I will see Y. Then make the change or observation and compare. This is Prediction Before Execution applied where it matters most, because debugging is where the temptation to explain after the fact is strongest.
  • Make the prediction about something other than the symptom. "The error goes away" is a prediction every fix and every restart satisfies; "the log shows the cart as a map and the handler reads it as a list" is a prediction only one cause satisfies. Predict the mechanism, then check the symptom.
  • When the prediction fails, take the failure seriously: the hypothesis is wrong or incomplete. Do not add a second hypothesis to rescue the first; go back to the evidence.
  • When the prediction succeeds, ask what else the hypothesis predicts and check one more thing before committing the fix. One confirmation is a coincidence; two independent ones are a cause.

The same observation, with and without a prediction

Nothing about the observation changes. What changes is whether its result can update your knowledge, and that depends entirely on whether you said in advance what it would mean.

Logging the cart at handler entry
Look and see
Add `console.log(cart)`, re-run, read a large object, notice it "looks a bit different from before", decide that supports the refactor theory, and keep the theory you already had.
Predict, then look
Write: "if the refactor is the cause, `items` is an object keyed by product id and `Array.isArray(items)` is false; if not, it is an array." Add the log for those two facts only, re-run, read two values, compare.

The prediction chose what to log (two facts, not the whole object), where (handler entry, where the assumption is made), and what each outcome means. The look-and-see version produces an impression that any prior belief can absorb.

Hypotheses as unknowns with predictions attached

A hypothesis is an unknown that has already been sharpened to a question; the prediction is what the experiment column has to contain for the experiment to count. The board below is the store's bug mid-investigation, with each candidate carrying the observation that would confirm it and the one that would kill it.

Payment failed — candidates with predictions
known
  • The pay request returns 500 quickly; the provider dashboard shows no attempt; the log has no outbound call.
  • A cart refactor and a provider SDK bump were both deployed in the last day.
unknown → question → experiment
  1. ? The refactor broke something.

    becomes Does the handler receive cart.items in a shape it was not written for?

    experiment Log Array.isArray(cart.items) at handler entry. Right: false, and the error follows. Wrong: true, and the undefined value is produced elsewhere.

  2. ? The SDK bump broke the provider call.

    becomes Does the handler reach the SDK call at all, and does the call fail?

    experiment Revert the bump and re-run. Right: an outbound call appears in the log. Wrong: still no outbound call — the failure is before the SDK is reached.

  3. ? Something about the database.

    becomes Is any database error raised before the 500?

    experiment Filter the log by request id for database errors. Right: a query error precedes the 500. Wrong: the only error is the TypeError, and the database is not involved in this failure.

Each experiment says what "right" and "wrong" look like before it runs. Two of the three die on the first observation; that is what a good board does.

What to predict about the fix

A fix is the last experiment, and the one most often run without a prediction, because the symptom disappearing feels like enough. The decision below is about which effect to predict; the symptom alone is the weakest option on the list.

Confirming the fix

Beyond the symptom disappearing, what should I predict will change when the fix is applied?

The mechanism reverses

when You can observe the intermediate value or log line that was wrong — items now read as a list, outbound call now present.

cost Needs the observation point from the earlier experiment to still be in place.

A downstream effect appears

when The fix should make later steps happen — a provider attempt in the dashboard, a paid row in the table.

cost Involves external systems and may be slow to check; strongest evidence when it holds.

A related bug stays

when You believe the fix is narrow and another known failure is independent — the double-click test should still fail.

cost Counter-intuitive to want a test to fail; if it unexpectedly passes, the fix is doing more than you think.

Only the symptom

when Never as the sole prediction; acceptable as an extra when the others are in place.

cost A restart satisfies it; so does a fix for a different bug with the same message.

How to do it

Most important first.

  • Write the hypothesis in a sentence that names a mechanism: "the handler reads cart.items as an array but the refactor made it a map".
  • Derive one prediction about an observation you have not made: "logging typeof cart.items at handler entry will print object and Array.isArray will be false for the failing request".
  • Derive one prediction about the fix: "converting the map to a list at the boundary makes the double-click test pass *and* the provider dashboard shows an attempt" — two effects, not one.
  • Make the observation or the change. Compare with the two sentences. Record the outcome in the notebook whether it confirmed or killed the hypothesis (The Engineering Notebook).
  • If confirmed, check the second prediction before declaring victory; if killed, return to Debugging Is Problem Solving with one fewer candidate.

Worked on a concrete problem

The move has to produce something. This is what it produced.

  • Hypothesis: the cart refactor changed items from a list to a map and the handler still indexes as a list. Prediction if right: the cart logged at handler entry has items as an object with product ids as keys; items[0] is undefined. Prediction if wrong: items is an array and the undefined value is somewhere else. Observation: an object keyed by product id. Confirmed — and note that the observation was at handler entry, chosen by the prediction, not at line 41 where the error was.
  • Prediction about the fix, before making it: after converting the map to a list at the handler boundary, the failing request will produce an outbound provider call in the log and a paid order in the table, and the double-click test will still fail, because that is a separate bug. All three came true. Had the double-click test unexpectedly passed, the fix would have been doing more than its hypothesis said — and that is worth knowing before merging.
  • A failed prediction: hypothesis that the provider SDK bump broke the charge call; prediction that reverting the bump makes the outbound call appear in the log. Revert, re-run: still no outbound call. The hypothesis is dead cleanly, in one experiment, and the SDK is never touched again. Without the prediction, "still failing after revert" would have become "probably need to clear the cache too".

How you know it worked

What now exists that did not before, and what question you can now ask.

  • Every change and every observation is preceded by a written expectation, and you compare rather than interpret.
  • Your predictions are about mechanism — a value, a log line, a row — not only about the symptom.
  • A failed prediction ends a hypothesis instead of extending it.
  • The fix is confirmed by at least one effect beyond the symptom disappearing.

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.

Next questions
  • ?If my hypothesis is right, what exactly will I see — and if it is wrong, what will I see instead?
  • ?Is my prediction about the mechanism, or only about the symptom going away?
  • ?What else does this hypothesis predict that I could check before I commit the fix?
  • ?Did the observation match the prediction, or am I explaining it after the fact?

What can go wrong

How the move itself fails
  • Predictions so vague that anything satisfies them: "something will be different in the log". A prediction must be able to fail.
  • Predictions made and then not compared, because the observation was interesting and the prediction forgotten. Write it where you will see it when the result comes in.
  • Rescuing a dead hypothesis with an auxiliary one after a failed prediction — "the revert didn't help because the cache". Sometimes true, usually a way to avoid updating.
  • Treating a confirmed prediction as proof. Confirmation supports; only the second, independent prediction makes the cause hard to doubt.
What the move costs
  • Writing the two sentences takes a minute per experiment, and on a bug with an obvious cause the minute buys nothing.
  • Predicting the mechanism requires having a mechanism in mind, which for an unfamiliar system may not be possible yet; the first experiments there are exploratory, and the prediction is "I do not know", honestly stated.
  • The habit slows down the fastest debuggers slightly and speeds up everyone else considerably; a senior who already does it implicitly gains little from doing it aloud.
Misreads
  • "So never make an exploratory observation." Exploration is fine when labelled as such: "I do not have a hypothesis; I am looking at the cart to form one". The failure is exploration that pretends afterwards to have been a test.
  • "The prediction has to be precise." It has to be able to fail. "The items field will not be an array" is enough; "the items field will be a Map with exactly three keys" is precision with no extra value.
  • "A confirmed prediction means the fix is right." It means the hypothesis survived one test. The fix is right when the mechanism is understood and the second prediction also holds.

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.

  • GENERALPrediction before observation is how any experiment is kept honest; debugging is where it is most often skipped because the observations are so cheap that looking feels free.
  • CONTESTEDSome experienced engineers argue that for a well-understood system, fast unguided probing — a debugger, a REPL, watching values — builds a hypothesis faster than writing predictions for hypotheses you do not have yet, and that formalising every look is ceremony that slows an expert down. The strong form of that view: prediction is for testing a hypothesis, and in the first minutes of an unfamiliar bug you have none, so explore first and predict once something is worth testing. This lesson accepts that and draws the line at labelling: exploration is fine when called exploration, and a change is not an experiment until a prediction precedes it.
  • ILLUSTRATIVEThe map-versus-list cart, the handler-entry log and the SDK revert are invented to show predictions succeeding and failing; no real code is described.

Where the depth lives

This domain asks the question and hands the answer off by name.

Software Designcharacterization-tests
Further
  • The manifesto's "review the LLM's answer" at /manifesto/review is this move applied to a suggested fix: before applying it, say what it should change and what it should not — then check both.