Testing Your Code Systematically
Debugging is the process of finding the cause of an error in your code, triggered by error messages or incorrect results.
When Debugging Begins
Every programmer encounters bugs. Debugging is the process of finding the cause of an error in your code. You need it when Python reports an error or when the program runs but produces an incorrect result. A program that does not crash is not necessarily correct: its logic may still produce an answer different from the one you intended.
| Observed behavior | What it may mean | Debugging response |
|---|---|---|
| An error message appears | Python has identified a problem during interpretation or execution | Read the message and locate the reported part of the code |
| The program runs but gives the wrong answer | The program may contain a semantic error: its logic does not match the intended behavior | Compare the actual result with the intended result and trace the logic |
| The result matches the intended behavior | The observed behavior is currently expected | No debugging is needed for that behavior |
Different observations call for different debugging decisions.
Reading the Intended Logic
Reading is usually the first strategy to try because it is fast and often exposes simple mistakes. Examine the code carefully and compare what it actually does with what you intended it to do. Read the code aloud or trace it line by line. Look for a misspelled variable name, a reversed comparison operator, a missing colon, or logic that does not express the intended rule.
Tracing a decision rule
A program is intended to report that a value is eligible only when the value is at least 18. The program instead reports eligibility for a value of 16.
- 1State the intention
The intended rule is that eligibility begins at 18.
- 2Trace the condition
Read the comparison from the program's point of view. Ask which values the condition actually accepts, rather than assuming it expresses the intended rule.
- 3Compare outcomes
Use the value 16 as a check. If the condition accepts 16, the code and the intended rule do not match.
- 4Locate the mismatch
Inspect the comparison operator and its direction. A reversed comparison can make the program choose the opposite set of values.
Reading identifies a mismatch between the condition's actual behavior and the intended eligibility rule before additional changes are made.
Begin with the smallest comparison between intention and behavior that can expose the bug. Reading is not passive inspection: it is a deliberate trace of the values, decisions, and actions the code produces.
Running Controlled Experiments
When reading does not reveal the problem, move to running. Make a small, deliberate change and observe what happens. Temporary display code, sometimes called scaffolding, can show the values held by variables or the intermediate results produced at different points. These observations help narrow down where the problem occurs.
Using intermediate results
A program produces an incorrect final total, but the error message does not identify which part of the calculation is responsible.
- 1Choose an observation point
Temporarily display an intermediate value before the final total is produced.
- 2Run the unchanged version
Record the intermediate result and the final result. This establishes what the program currently does.
- 3Make one small change
Change only the suspected calculation or input handling, then run the program again.
- 4Compare results
If the intermediate value changes in the expected direction, the experiment provides evidence about that part of the calculation. If it does not, focus attention elsewhere.
- 5Remove temporary scaffolding
Once the relevant location is understood, remove temporary display code so it does not become part of the finished program.
The experiment narrows the search by showing where the actual values diverge from the intended values.
Ruminating on Symptoms
Ruminating means stepping back and thinking analytically about the problem. Use your understanding of Python, the error message, the observed symptom, and the changes made recently. The goal is not to stare at the code indefinitely; it is to form a better explanation of what kind of error could produce the exact behavior you observed.
| Error type | Meaning | Useful question |
|---|---|---|
| Syntax error | The code does not follow Python's grammar rules | Which part of the code violates the language's required form? |
| Runtime error | The code is grammatically correct, but something goes wrong while Python executes it | What operation fails when execution reaches this point? |
| Semantic error | The code runs without crashing but produces the wrong result because its logic is incorrect | Which part of the logic differs from the intended behavior? |
Classifying the error narrows the set of possible causes.
Ask two questions: What kind of error could produce this exact symptom? What did I change that could cause this? Error messages often tell you what went wrong and where, while recent changes provide a focused place to begin looking.
Retreating to Clarity
Retreating is the strategy to use when many attempted changes have made the program difficult to understand. Undo the recent changes and return to a version you know works. This is not giving up. It restores a clear starting point from which you can make and test changes deliberately.
Recovering a clear starting point
A programmer has changed several calculations and added temporary displays while trying to fix an incorrect result. The program is now different from the version that originally worked, and the programmer no longer knows which changes helped or caused new problems.
- 1Recognize confusion
The number of changes makes it impossible to connect the current behavior reliably to one specific experiment.
- 2Return to the working version
Undo the recent changes until the program reaches a state known to work.
- 3Re-establish the baseline
Observe and understand the working behavior before making another change.
- 4Repeat carefully
Make one deliberate change, observe the result, and keep track of what happened.
Retreating restores clarity and makes later experiments easier to interpret.
Choosing the Next Move
Reading, running, ruminating, and retreating are tools, not fixed stages. You may use them in combination and move between them as you gather information. A good starting pattern is to read first, run a controlled experiment if reading is inconclusive, ruminate when the evidence is difficult to interpret, and retreat when accumulated changes have removed your clear baseline.
| Situation | Strategy to emphasize | Action |
|---|---|---|
| The problem looks like a simple mistake | Reading | Trace the code and compare it with the intended logic |
| The location of the problem is unclear | Running | Add temporary display code and change one thing at a time |
| The symptom is difficult to interpret | Ruminating | Classify the error, inspect the message, and consider recent changes |
| Many changes have made the program confusing | Retreating | Return to a known working version and restart carefully |
Choose the strategy that gives you the clearest next piece of evidence.
Practice the Four Strategies
A program runs without an error message, but its final result is incorrect. You read the code and cannot immediately find the mismatch. You then add temporary displays at two intermediate points, change two calculations at once, and receive a different result but still do not know which change mattered. Finally, the program is much harder to understand than the original working version. Describe the best next actions using reading, running, ruminating, and retreating.
Hints
- An incorrect result is a reason to debug even when the program does not crash.
- Controlled running changes one thing at a time and uses observations as evidence.
- When accumulated changes destroy clarity, return to a known working state.
A systematic response
Apply the four strategies to the practice situation.
- 1Recognize the need
The incorrect result means debugging is needed even though execution completed.
- 2Retreat
Because two changes were made together and the program has become confusing, return to the last known working version.
- 3Read
Trace the working baseline against the intended logic, looking for a simple mismatch.
- 4Run
If reading is inconclusive, add temporary displays and make one controlled change at a time.
- 5Ruminate
Use the observed values, the incorrect result, and the most recent change to form and test a possible explanation.
The strongest response is not another random modification. Restore clarity, then gather evidence through reading, controlled running, and analytical reasoning.
Common Debugging Mistakes
Common mistake Randomly changing code without a hypothesis
What it looks likeSeveral unrelated lines are modified simply to see whether the output changes.
Why it failsYou lose the connection between a change and its effect, so the result provides little useful evidence.
Do this insteadUse reading, running, or ruminating to choose one deliberate change and observe its result.
Common mistake Changing multiple things during one experiment
What it looks likeTwo calculations are changed before the program is run again.
Why it failsYou cannot determine which change fixed the problem or caused a new one.
Do this insteadChange one thing at a time and keep track of what you tried.
Common mistake Assuming no crash means no bug
What it looks likeThe program completes normally, so its incorrect result is ignored.
Why it failsA semantic error can let the program run while producing the wrong result.
Do this insteadCompare the result with the intended behavior and debug whenever they differ.
Common mistake Ignoring the error message
What it looks likeThe message is dismissed without examining what went wrong or where.
Why it failsThe message may provide a direct clue about the error and its location.
Do this insteadRead the message carefully and use it while reasoning about possible causes.
Common mistake Continuing to modify a confused version
What it looks likeMany experimental changes have accumulated, but more changes are added instead of restoring the baseline.
Why it failsYou no longer know which changes helped, hurt, or introduced the current behavior.
Do this insteadRetreat to a version known to work, then proceed with smaller, trackable experiments.
Key Takeaways
- 1Debugging finds the cause of an error, whether Python reports a problem or the program produces an incorrect result.
- 2Reading compares the code's actual logic with the intended logic and is often the fastest first strategy.
- 3Running uses small experiments and temporary display code to reveal values and narrow down the problem.
- 4Ruminating classifies the error, studies messages and symptoms, and considers recent changes.
- 5Retreating restores a known working state when too many changes have made the program confusing.
At a glance
- Debugging is a systematic search for the cause of an error or incorrect result.
- Start by reading the code and comparing its behavior with your intention.
- Run controlled experiments with temporary displays, changing one thing at a time.
- Ruminate on error types, messages, symptoms, and recent changes to form useful hypotheses.
- Retreat to a known working version when accumulated changes make the program difficult to understand.