Tracing and debugging
Become your own code stepper. Trace a program by hand with a trace table, find an off-by-one bug, add debugging prints, and follow a calm routine for fixing any bug.

In this lesson you will
- Trace a short program by hand with a trace table
- Find and fix an off-by-one error in a loop
- Use temporary print statements to see what a program is doing
- Follow a step-by-step routine for debugging
Now that your programs make decisions and go round in loops, they can surprise you. A program can run without a single error message and still do the wrong thing. Finding out why is called debugging, and it’s a skill like any other: there are techniques, and they get easier with practice. The most important one is tracing: following the program yourself, line by line, exactly as Python would.
A bug with no error message
This program is meant to count down 3, 2, 1 and then lift off. Step through it and keep an eye on the output. Something is missing.
This countdown should print three, two, one, then lift off. Let's trace it and see what really happens.
The countdown printed 3 and 2, then lifted off. 1 never appeared. No error, just a wrong answer.
Trace tables
The stepper did the tracing for us. You can do the same thing on paper with a trace table: one column per variable, one column for the condition, one for output, and a new row every time something changes.
| Line | n | n > 1? | Output |
|---|---|---|---|
| 2 | 3 | ||
| 3 | 3 | True | |
| 4 | 3 | 3 | |
| 5 | 2 | ||
| 3 | 2 | True | |
| 4 | 2 | 2 | |
| 5 | 1 | ||
| 3 | 1 | False | |
| 6 | 1 | Lift off! |
The table points straight at the problem: when n is 1, the question n > 1 is False, so
the loop stops one round too early. The fix is to change the condition to n >= 1 (or
n > 0). Make that change and check it:
n = 3
while n > 1:
print(n)
n = n - 1
print("Lift off!")This kind of bug, where a loop runs one time too many or one time too few, is so common it
has a name: an off-by-one error. Whenever a loop has a < or a >, ask yourself: should it
be <= or >=? Check the first and last iterations specifically, because that’s where
off-by-one bugs hide. The same goes for range: range(1, 10) stops at 9, not 10.
Debugging prints
Tracing by hand is great for short programs. For longer ones, let Python tell you what’s going
on by adding temporary print lines that show the values you’re unsure about. This program
should add up the even numbers from 2 to 10 (2 + 4 + 6 + 8 + 10 = 30), but it prints something
else:
total = 0
for n in range(2, 10, 2):
total = total + n
print("Total:", total)Add a debugging print inside the loop, run it, and look:
total = 0
for n in range(2, 10, 2):
print("DEBUG n =", n, "total before =", total)
total = total + n
print("Total:", total)The debug lines show n going 2, 4, 6, 8 and never reaching 10. It’s range stopping before
its stop value again. Change the 10 to 11 (or 12) and the total becomes 30. Then delete the
debugging print: it’s scaffolding, not part of the finished program.
A debugging routine
When a program misbehaves, resist the urge to change things at random. Work through this instead:
- Reproduce it. Find exact values that cause the problem. “It’s wrong when
nis 1” is much more useful than “it’s sometimes wrong”. - Predict. Write down what the program should do with those values.
- Trace or print to see what it actually does. Find the first line where reality and your prediction part ways.
- Fix one thing, then run again. If you change three things at once, you won’t know which one mattered.
- Test the edges: the first and last iteration, zero, empty strings, the biggest values.
A surprisingly good extra trick is rubber duck debugging: explain your code out loud, line by line, to someone, or to a rubber duck on your desk. Saying what each line does often reveals the line that doesn’t do what you thought.
Tracing a decision
Tracing works for if statements too. Without running it, trace this program for
hours = 45 and write down what it prints. Then run it to check.
hours = 45
rate = 10
if hours > 40:
overtime = hours - 40
pay = 40 * rate + overtime * rate * 1.5
else:
pay = hours * rate
print("Pay:", pay)You should get 475.0: 40 normal hours at 10, plus 5 overtime hours at 15. Now trace it for
hours = 40. Which branch runs? Is that the right answer? These boundary values, exactly on the
edge of a condition, are the ones most worth checking.
Your turn
This exercise has a bug of the off-by-one kind. Trace it, find it, fix it.
Exercise · Python
Fix the off-by-one
Solved ✓This program should print the 7 times table from 1 × 7 up to 10 × 7 and add up all ten
answers in total (which should come to 385). It runs without an error, but it’s wrong.
Trace it (or add a debugging print), find the off-by-one bug, and fix it with the smallest change
you can.
total = 0
for n in range(1, 10):
answer = n * 7
print(n, "x 7 =", answer)
total = total + answer
print("Total:", total)
Check yourself
Self-check · 4 questions