B2.1.4 · Programming fundamentals
Debugging techniques: finding a bug that doesn't crash anything
B2.1.3 was about failures a program can name and catch. This statement is about the other kind: code that runs to completion without a single error, and simply produces the wrong answer.
Explainer
Fairbank Canteen's order till is supposed to add up the price of everything a student buys and print the total. It runs perfectly, no crash, no error message, prints a confident-looking number. That number is simply wrong. There is nothing here for try/except to catch, since Python never objects to what the code is doing, it just does the wrong thing correctly. Finding a bug like this needs a different toolkit entirely.
A bug is an error in a program's logic that produces incorrect behaviour without necessarily causing a crash. Debugging is the systematic process of locating exactly where a program's real behaviour diverges from what it was intended to do, then fixing it.
Changing lines at random until the output happens to look right is not debugging, it's guessing, and it tends to leave a second bug behind for every one it accidentally fixes. Every technique below works the same basic way: form a prediction about what a variable should hold at some point, then check it against what the program actually holds there, and narrow in on the exact line where the two stop matching.
Explainer
Four named techniques
The syllabus names four. Each answers the same question, "what does this variable actually hold right now", by a different route.
Print statements. Temporarily insert print() calls at suspicious points in the code, run it, and read what actually gets printed. Cheap and needs no tool beyond the language itself, but the code has to be edited to add them, and edited again to remove them once the bug is found.
Trace tables. A table with one column per variable, filled in by hand line by line as the program is worked through. It's the disciplined, written-down record of tracing, and it's exactly the technique B2.1.1 already built one of, for a completely different reason (there, to check a scope trace was complete; here, to catch a WRONG value the moment it first appears).
Step-by-step execution. Running the program one line at a time, mentally or with a tool, and checking each line's actual effect against what you predicted before running it. A trace table is what you get when you WRITE DOWN what step-by-step execution shows you as you go.
Breakpoint debugging. Using a real debugger tool to pause a program's execution at one chosen line, then inspecting every variable's actual current value before choosing to continue. This page's own click-through stepper below is doing exactly that: pausing at a line you choose and showing you what every variable holds at that instant, which is the whole idea a breakpoint debugger automates for real running code.
Real example: adding print(price, total) inside a suspect loop to see it change on every pass.
Advantage
- Needs no separate tool (works anywhere the program itself runs, including inside this page's own console)
Disadvantage
- Requires editing the source code twice (once to add the prints, once to remove them, and a forgotten one is now shipped debug output)
Real example: pausing execution right before a suspect line and inspecting every variable's live value, without changing a single character of the code.
Advantage
- The source code is never touched (nothing to remember to remove afterward, and the exact same run you're debugging is the one that eventually ships)
Disadvantage
- Needs a debugger tool available and set up (not always possible, e.g. a locked-down school machine or a bare command line)
Worked example
Hunting a silent total that's always wrong
Four prices should add up to 14. The till prints 4 instead, every single time, with no error anywhere. Four techniques applied to the same bug, one after another.
Step 1. The bug, and why it's hard to spot by reading. The code reads perfectly reasonably: for every price, update the total. Nothing here looks obviously wrong on a first read.
Step 2. Technique 1: print statements. Add a temporary print inside the loop to see what's actually happening on every pass, not just at the end.
Step 3. Technique 2: a trace table. The same insight, written down in the disciplined format from B2.1.1, one column per variable, filled by hand.
| Line | price | total |
|---|---|---|
| before loop | 0 | |
| pass 1 | 3 | 3 |
| pass 2 | 5 | 5 |
| pass 3 | 2 | 2 |
| pass 4 | 4 | 4 |
The total column never accumulates, each row simply overwrites the last. That pattern, visible at a glance in the table, is the bug.
Step 4. Technique 3: step-by-step execution. Narrate each line as it runs and compare it against what SHOULD happen. The exact moment prediction and reality diverge is the bug.
Pass 1 hides the bug by accident (0 + 3 happens to equal 3). Pass 2 is where step-by-step execution catches it for certain.
Step 5. Technique 4: breakpoint debugging. A real debugger would pause execution right before the total = price line on pass 2, showing total = 3, price = 5 in its variables panel. Stepping to the next line, without editing any code, shows total become 5, not 8. Same discovery as step 4, without narrating it by hand.
Step 6. The fix, confirmed, and the temporary prints removed. total = price should have been total = total + price. Once found, ALL the diagnostic print statements from step 2 get removed too, they were only ever there to help find this.
Try it yourself
Run the buggy till, then fix it
The diagnostic print statements from step 2 are already in place, run it and watch total get overwritten instead of growing. Then apply the one-line fix yourself.
Running in offline fallback mode. The full Python engine couldn't load (blocked school network, or no internet), so this console switched automatically to a small hand-written engine. It runs this page's loop and print statements exactly, but it isn't full Python.
Check your understanding
A program crashes with no error message useful for working out what went wrong, and you want to inspect one variable's value at one exact line, without editing the source code at all. Which technique fits best?
Common pitfall
B2.1.4-style debugging questionsRecurringThe most common weak answer names all four techniques with no distinction between them, or names only one, when the question actually asks WHICH technique suits a described situation and WHY. Listing the four terms is not the same as choosing between them.
The second common gap is treating a trace table and step-by-step execution as interchangeable synonyms. They are closely related but distinct: step-by-step execution is the ACT of working through the code one line at a time, a trace table is the WRITTEN RECORD of doing so, one column per variable. A strong answer names both correctly rather than folding them into one idea.
A third real mistake, this time a coding habit rather than an exam one: leaving temporary print() debugging statements inside code that gets submitted or shipped. They were only ever meant to be diagnostic, and should be removed once the bug they were added to find has actually been fixed.
Answer frame that scores: name the SPECIFIC technique that fits the described symptom → state what makes it suited to that symptom rather than another one → if code is shown, apply the technique and state the exact line where the bug is found.
What a weak answer looks like: "You could use print statements, trace tables, breakpoints or step through the code to debug the program." True, but answers nothing about which one this situation actually calls for.
What it should look like: "Breakpoint debugging suits this best, since it lets you inspect the variable's value at that exact line without editing the source code, unlike print statements which require adding and then removing temporary lines." Names the technique, and justifies the choice against the actual symptom.
Revision questions
Past-paper style. Write an answer, then reveal the marking points and mark yourself.
[2]Distinguish between a trace table and step-by-step execution.
Marking points
评分要点
- A trace table is filled in by hand: you predict and record every variable's value at each step on paper.
- Step-by-step execution runs the program one line at a time in a debugger, showing the real values as it goes.
- 追踪表是手工填写的:你在纸上预测并记录每一步每个变量的值。
- 逐步执行在调试器中一次运行一行程序,边运行边显示真实的值。
[3]A program runs with no error but produces the wrong output. Explain why exception handling cannot help find this kind of bug, and identify a technique that could.
Marking points
评分要点
- Exception handling only reacts to errors that are actually raised; a logic error raises none, the program runs to completion and just gives a wrong answer.
- A technique that could find it: a trace table (predict then compare), step-by-step execution in a debugger, or adding print statements to show intermediate values.
- 异常处理只对实际引发的错误作出反应;逻辑错误不引发任何异常,程序运行到结束,只是给出错误答案。
- 能找到它的技术:追踪表(先预测再比较)、在调试器中逐步执行,或添加打印语句显示中间值。
[5]Construct a trace table for a short faulty program, identify the line where the fault first appears, and state the fix.
Marking points
评分要点
- Faulty program: prices = [3, 5, 2, 4]; total = 0; for price in prices: total = price; print(total).
- Trace: total starts 0; after price 3 total = 3; after 5 total = 5; after 2 total = 2; after 4 total = 4; final output 4.
- Expected output is the sum, 14, so the values diverge from the prediction at the second iteration (predicted 8, actual 5).
- The faulty line is total = price; it overwrites the running total instead of adding to it.
- The fix is total = total + price.
- 有缺陷的程序:prices = [3, 5, 2, 4];total = 0;for price in prices: total = price;print(total)。
- 追踪:total 从 0 开始;经过 3 后 total = 3;经过 5 后 total = 5;经过 2 后 total = 2;经过 4 后 total = 4;最终输出 4。
- 预期输出是总和 14,所以值在第二次迭代时开始与预测分歧(预测 8,实际 5)。
- 有缺陷的行是 total = price;它覆盖了累计总和,而不是加到其上。
- 修正是 total = total + price。
Flashcards
Active recall for this statement’s key terms.
Keep this statement’s practice with you
Free account: unlock every revision question and flashcard for B2.1.4, and every other IB CS 2027 statement.