B2.1.4 Construct and use common debugging techniques.
• Debugging techniques may include trace tables, breakpoint debugging, print statements and step-bystep code execution.
The Big Idea
Debugging is the process of identifying, isolating, and fixing faults (also known as bugs) in a program. It is an essential part of the software development lifecycle. Even well-written code often fails the first time it runs due to incorrect logic, typos, unexpected input, or edge cases.
The goal of debugging is not just to fix errors, but to understand why they happen. This understanding helps developers improve their reasoning, prevent future mistakes, and write more reliable code.
Types of Errors That Need Debugging
Before diving into techniques, it’s helpful to distinguish between types of errors:
- Syntax errors: Violations of the rules of the programming language (e.g. missing parentheses).
- Runtime errors: Errors that occur during program execution (e.g. division by zero).
- Logical errors: Code runs without crashing, but produces incorrect results (e.g. wrong formula).
Logical errors are the hardest to catch and require careful debugging techniques.
Common Debugging Techniques
Let’s examine four widely used debugging strategies, suitable for first-year students and professional developers alike.
1. Trace Tables
A trace table is a manual tool used to track how variables change as the program runs, especially inside loops and conditionals.
When to use:
- To understand how a piece of code executes line by line.
- To simulate what the computer “sees” during execution.
Example (Python):
total = 0
for i in range(3):
total += i
print(total)
Trace Table:
| i | total |
|---|---|
| 0 | 0 |
| 1 | 0 |
| 1 | 1 |
| 2 | 1 |
| 2 | 3 |
| - | 3 |
Tip: Use trace tables when learning or analyzing algorithms manually. They’re also useful for designing test cases.
2. Print Statements
Using print() in Python or System.out.println() in Java is the most common and beginner-friendly debugging method.
When to use:
- To check values of variables at specific points.
- To verify if a function is being called.
- To trace the execution flow.
Example:
def divide(x, y):
print("x:", x, "y:", y) # debug print
return x / y
result = divide(10, 0) # Will show values before crashing
Pros:
- Easy to use.
- Works in all environments.
Cons:
- Not scalable in large programs.
- May clutter output and must be removed later.
Best practice: Use clear debug messages and print variable names along with values.
3. Breakpoint Debugging
Breakpoints are markers that pause execution at specific lines so you can inspect the program state.
How it works:
- Set a breakpoint in an IDE (e.g. Thonny, PyCharm, VS Code for Python; Eclipse, IntelliJ for Java).
- Run the program in debug mode.
- When the program hits the breakpoint, it pauses.
- You can inspect variable values, step line-by-line, and observe branching logic.
Advantages:
- Visual and interactive.
- Excellent for understanding how control flows through branches and loops.
- Doesn’t require modifying code (like with
print).
Example (Python):
- Place a breakpoint on a suspicious line.
- Run in Debug mode.
- Watch variables in the “Variables” pane.
- Step through the loop or conditional.
Example (Java with IntelliJ):
- Right-click on a line and select "Toggle Breakpoint."
- Run in Debug.
- Use F8 (step over) or F7 (step into) to control flow.
4. Step-by-Step Execution (Stepping)
Stepping through code allows you to execute one line at a time and watch what happens at each step.
Two main types:
- Step Over: Executes the current line and moves to the next (useful in loops).
- Step Into: Enters into the function being called (useful for checking nested function behavior).
- Step Out: Finishes the current function and returns to the caller.
When to use:
- To follow logic precisely.
- To debug nested or recursive functions.
- To find where the flow deviates from expectations.
Example: Combining Techniques
def compute_total(items):
total = 0
for price in items:
total += price
print("Running total:", total) # print-based debug
return total
prices = [10, 20, "30", 40]
compute_total(prices)
Problem: A TypeError occurs when trying to add an integer to a string.
- Trace table helps see at which iteration it fails.
- Print statements show partial output.
- Breakpoint debugger reveals the exact error type and location.
- Stepping pinpoints when the program moves from working to broken state.
Summary Table
| Technique | Best For | Tools Needed |
|---|---|---|
| Trace Table | Manual variable tracking in loops | Pen and paper / IDE |
| Print Statements | Fast inspection of values or function calls | Any editor |
| Breakpoint Debugging | Interactive variable and flow inspection | IDE with debugger |
| Step-by-Step | Understanding control flow and function calls | IDE with debugger |
Final Thoughts
Learning how to debug systematically is as important as learning how to write code. Good debugging helps you build confidence, reduce frustration, and approach problems analytically.
A skilled programmer does not write perfect code on the first try. Instead, they build tools and habits that let them understand and fix their mistakes quickly and thoughtfully. Debugging is how that process becomes both repeatable and reliable.