Skip to main content

Debugging with Competing Explanations

What this concept is​

A debugging experiment chooses an input or observation that distinguishes plausible explanations of a failure. Changing many lines until an example passes loses information about the cause.

Why it matters here​

The original debugging lesson repairs an accumulation error. This lesson asks you to choose evidence before editing. It prepares you to inspect unfamiliar code and later generated code without trusting an attractive explanation.

Concrete example​

A report says that the “longest qualifying streak” of [30,10,30] at target 30 is reported as 2; the contract says 1. Two hypotheses are plausible: the program counts all qualifying sessions, or it carries its current run across a failed session. One observation does not distinguish those hypotheses.

Inspect this candidate only after predicting the state:

def candidate(sessions, target):
current = 0
best = 0
for minutes in sessions:
if minutes >= target:
current += 1
best = max(best, current)
return best

The running state is (1,1), (1,1), (2,2). The failed session does not reset current. This code observation identifies a mechanism; the symptom alone did not. The two hypotheses can agree on every output for this candidate, so merely collecting more examples is not guaranteed to distinguish their internal mechanisms. Inspection or tracing can be necessary.

Now consider a different report: [30] at target 30 gives 0. Competing explanations include an exclusive comparison and always returning zero. Test [31] at target 30: the first explanation predicts 1, the second predicts 0. This is a discriminating experiment. Record the prediction before running it.

Common confusion / misconception​

A failed assertion establishes a mismatch, not its cause. A passing repair on one input does not establish the full contract. Minimizing an input means retaining the property that reveals the failure: reducing [30,10,30] to [30] removes the reset defect. Not every hypothesis is distinguishable through outputs alone.

How to use it​

Record input, expected result, actual result, competing explanations, proposed observation and predicted outcomes. Run the observation. Trace or inspect the smallest relevant section when needed. Change one mechanism, rerun the original failure and relevant neighboring cases, then preserve the regression. If your own program has no discovered failure, label the supplied defect as seeded; never invent a personal incident.

For the missing-reset repair, check empty input, one failure, equality, a run ending at the final entry, and two separated runs. Verify that the input remains unchanged. Keep exploratory prints out of the final function; retain the investigation note.

Check yourself​

Why does [30,30] fail to test reset behavior? No nonqualifying session occurs. Why is [30,10,30] informative? It has qualifying sessions on both sides of a reset boundary. Which experiment distinguishes > from >=? A value exactly equal to the target, with an above-target positive control.

Mini drill or application​

Complete the design and diagnosis clinic. Before fixing the candidate, preserve an explanation that the evidence disproved or explicitly state that your first hypothesis survived the available tests. Honesty is more important than a dramatic story.

Read this only if stuck​

Read the scientific-method portion of The Debugging Book introduction. You do not need its full notebook environment for this clinic. Use Python errors and exceptions for traceback syntax. These local examples and assessment tasks are original.