post · Debugging · Skills
Debug Like a Working Engineer, Not a Panicked Student
Most students debug by adding print statements at random and praying. Working engineers don't, because we've spent enough hours on a single bug to know that praying is slow. The method below is what I use on real code at work and on every assignment that lands in my inbox.
It works in any language. It isn't glamorous. It works.
Step 1, Read the error message
Sounds obvious. Roughly half the time when a student sends me code that "doesn't work", the error message tells you exactly what is wrong, in plain English, with a line number.
Traceback (most recent call last):
File "lab3.py", line 14, in <module>
print(students[5])
IndexError: list index out of range
The error says: line 14, list index out of range. Look at line 14. Look at students. Count its elements. The list has fewer than 6 elements. You tried to access index 5. Done.
Students will read past this and start changing unrelated code. Don't. Read the error fully, twice.
Step 2, Form a hypothesis
Before you change anything, write down (on paper, in a comment, in your head) exactly what you think is happening and what you expect to happen instead.
Bad: "It isn't working." Good: "I expect the loop to run 5 times because the list has 5 items. The print statement runs 6 times. So the loop is running one extra iteration. Off-by-one error somewhere in the bounds."
This step is what separates fast debugging from slow debugging. If you can't articulate what is broken, your changes are random and won't converge on a fix.
Step 3, Reduce until you have a minimal reproduction
This is the single highest-leverage debugging skill. Take the code that fails and start cutting it down. Remove everything that isn't strictly required to reproduce the bug. Keep cutting until you've the smallest possible program that still misbehaves.
You do this for two reasons:
- Most bugs become obvious when the surrounding code is gone.
- If they aren't obvious, you now have a tiny reproduction you can show to a tutor, a friend, or me.
Example. Your 200-line assignment is throwing a KeyError. Cut everything except the dictionary creation and the lookup. If the error still happens, the bug is in those few lines. If the error goes away, add code back in chunks until it returns.
This is called bisection. Engineers use it daily.
Step 4, Use the debugger, not print statements
Print statements are the slowest debugging tool we have. Every time you change one, you save the file, run the program, and read the output. That's slow.
In Python use breakpoint() (Python 3.7+):
def calculate(items):
total = 0
breakpoint() # execution stops here, you get an interactive prompt
for x in items:
total += x
return total
When you run the program, you can inspect any variable, step through line by line (n), step into functions (s), and continue (c). It takes 10 minutes to learn and saves you hours forever.
In Java, use IntelliJ's debugger, set a breakpoint, run in Debug mode. In JavaScript, use Chrome DevTools. Every editor has one. Use it.
If you absolutely must use prints, log the types and values of your variables, not just messages:
print(f"DEBUG students={students!r} type={type(students)} len={len(students)}")
!r calls repr(), which shows you the structure including quotes around strings. type() reveals the actual type. This is much more informative than print(students).
A concrete example
Student sends me this Python code, says it crashes:
def average_grades(students):
total = 0
for s in students:
total += s["grade"]
return total / len(students)
result = average_grades([{"name": "A", "grade": 80}, {"name": "B", "grade": 90}])
Student reports: KeyError: 'grade'.
Step 1, read the error: KeyError on key 'grade'. So somewhere in the dict, 'grade' is missing.
Step 2, hypothesis: maybe one of the dicts in the list doesn't have 'grade'.
Step 3, reduce: comment out the loop, run again. No error. So it is in the loop. Add print(s) before the access. Run.
Output:
{'name': 'A', 'grade': 80}
{'name': 'B', 'grade': 90}
Both dicts have 'grade'. So the error must come from a different call. Search the file. Find another call earlier:
result = average_grades([{"name": "C"}])
There it is. One of the test cases passes a dict without a 'grade' key. Two minutes of focused debugging instead of 30 minutes of guessing.
Bugs you'll see again and again
After enough debugging sessions, you start recognising patterns. The most common in student code:
- Off-by-one errors: you iterate from 0 to len(list) inclusive instead of exclusive, or you use
<=where you meant<. - Type confusion: a string when you expected an int, or vice versa. Print
type(x)early. - Mutability surprises: see the Python mistakes post.
- Scope problems: the variable you think is global is actually shadowed inside a function.
- Reference vs value: passing a list into a function and the function modifies it. The caller sees the change.
When you've seen each of these once and understood why, the second time becomes 10 seconds of recognition.
When to stop debugging and ask for help
There is a healthy ratio of struggle to help. Roughly:
- 0 to 30 minutes stuck: keep debugging. You learn most here.
- 30 minutes to 2 hours: take a break, come back fresh. Read your code out loud.
- More than 2 hours on the same bug: ask. You're not getting smarter, just tired.
When you do ask, send a minimal reproduction, the error message, and what you've already tried. That triples the chance of getting a useful answer fast.
If you have a bug you've stared at for too long, send me the code on Telegram. I find the bug, explain why it happened, and show you the pattern to recognise it next time.
Keep reading
More from the blog
-
· Exam Prep · Revision
How to Revise for a Programming Exam in 7 Days
A focused 7-day revision plan for any Singapore programming module exam. Practical, what-to-do-each-day, no fluff.
-
· Group Projects · Soft Skills
Group Project Survival Guide for Singapore CS Modules
How to survive group projects in CS2103T, IS200, INF1002, and every other Singapore module that loves them. Without becoming the person doing 80% of the work.
-
· FYP · Capstone
Picking a Tech Stack for Your FYP, Without Regret
How to choose the right tech stack for your final-year project or capstone in a Singapore university or polytechnic, from someone who has rescued plenty of bad picks.
Related services
Need help with this directly?
Stuck on something specific?
Send your brief and I will reply with a fixed price, usually within the hour.