Nobody in this course will write a program that works the first time, and after a few weeks nobody expects to. The difference between a frustrating hour and a productive one is not talent — it is having a method instead of a mood. Debugging is a procedure you can follow when you are tired.

Three kinds of wrong

KindWhen it shows upExampleHow Python behaves
Syntax errorbefore anything runsa missing : after ifrefuses to start, points at the line
Run-time errorpart-way throughint("seven")stops there, prints a traceback
Logic errornever< where you meant <=runs happily, answers wrongly

Only the third one is genuinely dangerous, because the program looks fine and the wrong number goes home in somebody’s report. Syntax and run-time errors announce themselves; logic errors have to be hunted.

Read the traceback from the bottom

Traceback (most recent call last):
  File "/home/student/tally.py", line 2, in <module>
    count = int(answer)
ValueError: invalid literal for int() with base 10: 'seven'

Bottom line: what went wrong. Above it: where. The cause is often one line earlier than the crash — here, an input() whose result nobody checked. Error messages use a small, learnable vocabulary, and Reading an Error Message takes a traceback apart piece by piece; Name That Error is the drill that makes it fast.

Trace it by hand

When there is no error message, become the computer. Write the variables across the top of a page and fill in a row per pass.

marks = [46, 52, 78]
 
for mark in marks:
    below = 0
    if mark < 60:
        below = below + 1
 
print(below)

Two of those marks are below 60, and the program prints 0.

Passmarkbelow at start of passbelow at end
14601
25201
37800

The table exposes it: below = 0 is inside the loop, so every pass throws away the previous count. Move that line above the for and the program prints 2. Tracing found it in three rows; guessing could have cost the afternoon.

Two tools do the same job faster once the habit is there: temporary print statements showing the state of a variable each pass, and the step-through debugger in Using the Debugger, which shows you every variable without your having to ask.

A test plan is a table

Testing is not “I ran it and it seemed fine”. It is a written list of scenarios chosen so that every branch of the program gets used at least once — including the ugly inputs. Here is the plan that was run against an early version of the library return-desk helper in Branching Programs:

ScenarioInputExpectedActualPass?
On time0on-time messageon-time messagepass
Boundary of “week”7under-a-week messageunder-a-week messagepass
Just past it8reminder messagereminder messagepass
Over a month45speak-in-person messagespeak-in-person messagepass
Not a numbersoona clear complaint, no crashcrashfail

One row failed, which is the plan doing its job: the try/except block in the finished program exists because of that row, not because somebody remembered a rule.

Pick the boundaries on purpose: the values at each cutoff and one on either side, the empty case, the impossible case, and whatever a real person might plausibly type by mistake. Write the expected column before running the program — otherwise you will find yourself agreeing with whatever it prints, which is not a test.

When you are stuck

  1. Read the error message out loud. All of it.
  2. Find the smallest input that still fails.
  3. Comment out half the program. Still broken? The bug is in the other half.
  4. Explain the code, line by line, to a person or an object. This works embarrassingly often.
  5. Take the break. Ten minutes away has solved more bugs than an hour of staring.

None of this is a sign that something has gone wrong with you — bugs are information about the difference between what you said and what you meant. That is the argument in Mistakes Are Data, and the practical version is in Getting Unstuck. Sharpen the reading half in Spot the Bug and Predict the Output.

Curriculum connection

A4.1

demonstrate the ability to identify and correct syntax, logic, and run-time errors in computer programs;

Link to original

A4.3

demonstrate the ability to interpret error messages displayed by programming tools (e.g., compiler, debugging tool), at different times during the software development process (e.g., writing, compilation, testing);

Link to original

A4.4

use a tracing technique to understand program flow and to identify and correct logic and run-time errors in computer programs;

Link to original

A4.5

demonstrate the ability to validate a program using a full range of test cases.

Link to original