Mastering Code Debugging: The Power of Dry Runs and Trace Tables
tl;dr: Dry runs and trace tables are manual debugging techniques that help programmers verify the logical flow of their code by tracking variable values step-by-step. Dry runs are informal and can be used at any development stage to find logic errors, while trace tables provide a structured way to identify where the code deviates from expected behavior. These methods are particularly useful for debugging complex logic, loops, arrays, recursive functions, and mathematical algorithms but have limitations in detecting runtime errors, handling large datasets, concurrency issues, and performance bottlenecks.
What exactly is Dry Run? Is it a software?
A dry run is a way for programmers to manually check their code by following the value of variables step-by-step without using any software tools.
Traditionally, this process involved printing out the code and using a pen and paper to manually trace the values of variables to ensure they are being updated correctly. Nowadays, programmers often do this directly on the screen.
When a programmer finds a value that is incorrect, they can identify which part of the code caused the error. Here are some key points about dry runs:
When to Use: During design, implementation, testing, or maintenance.
Purpose: To find logic errors.
Limitations: Cannot detect execution errors.
Dry runs are informal and can be done whenever the programmer feels it is necessary, at any stage of the development process.
Implementing a Dry Run Using a Trace Table
A trace table helps programmers track the values of variables line-by-line as the code runs. This table shows the variable values at each step, helping to identify any errors.
Example
Consider an algorithm designed to count the number of potential astronauts based on height requirements (between 1.57 and 1.91 meters, inclusive).
Line
1. SET total TO 0
2. SET all_heights TO [1.87, 1.48, 1.57, 1.91, 2.01]
3. FOR EACH height FROM all_heights DO
4. IF height ˃ 1.57 AND height ≤ 1.91 THEN
5. SET total TO total + 1
6. END IF
7. END FOR EACH
There is a deliberate error on line 4: the condition should be height ≥ 1.57
instead of height > 1.57
.
This will allow us to look at a trace table to identify the point at which the program failed to return an expected value. Three values from the all_heights array should be accepted as they fall within the desired range. The values that should trigger an increment to the total are 1.87, 1.57 and 1.91.
You will notice that after the first iteration of the loop the program will return to line 3. Lines 3 to 7 are repeated for each height in the 1-D array all_heights. The trace table shows a complete run through of the values stored by each variable during the execution of the algorithm.
Line | total | all_heights | height |
1 | 0 | ||
2 | (1.87, 1.48, 1.57, 1.91, 2.01) | ||
3 | 1.87 | ||
5 | 1 | ||
3 | 1.48 | ||
5 | 1 | ||
3 | 1.57 | ||
5 | 1 | ||
3 | 1.91 | ||
5 | 2 | ||
3 | 2.01 | ||
5 | 2 |
The highlighted row on the trace table shows where the program failed to return the value that the programmer would have expected. The height of 1.57 is within the desired range.
However, the programmer’s logic error on line 4 of the algorithm has caused the program to exclude 1.57 as a value within range. As the program is designed to only add 1 to the total if the value of height is greater than 1.57, the total remains at 1 instead of 2.
Without using a trace table the algorithm would return the value 2 for total but the programmer would not be able to clearly identify the problem in the code. This exemplifies the purpose and usefulness of a trace table to track the value of variables.
Now that you know how you can create a table of variables and write their corresponding values for each line of the code, heres a step by step summary:
Steps | How to do it |
Preparing for a Dry Run | 1. Choose a segment of code you want to analyze. |
2. Set up a table with columns for each variable and each step of the code. |
| Performing a Manual Dry Run | 1. Write down the initial values of all variables.
2. Move through the code line-by-line.
3. As you trace through the code, fill in the table with the values of the variables at each step.
4. Update the values of variables as they change.
(Pay attention to loops and conditional statements to ensure the correct path is followed.) |
| Identifying Errors | 1. Compare Expected vs. Actual: Compare the values in your trace table to what you expect them to be.
2. Locate Errors: Identify any discrepancies and trace them back to the line of code that caused the issue. |
When to Use Dry Runs
Complex logical conditions: When dealing with multiple if-statements or nested conditions, trace running can help ensure all paths are correctly handled.
Loop debugging: For identifying issues within loops, especially when dealing with boundary conditions or off-by-one errors.
Array manipulations: When working with arrays or lists, trace tables can help visualize how elements are accessed and modified.
Recursive functions: Tracing can help understand the flow of recursive calls and ensure base cases are handled correctly.
Mathematical algorithms: For verifying calculations and ensuring correct order of operations.
Recognizing the Limitations
Runtime errors: Dry runs and trace tables focus on logic and won't catch runtime errors or issues related to the execution environment.
Large datasets: Manual tracing becomes impractical with very large input sets or long-running programs.
Concurrency issues: Problems related to multi-threading or parallel processing are difficult to identify through manual tracing.
External dependencies: Issues arising from interactions with external systems, APIs, or databases may not be apparent in a trace run.
Performance bottlenecks: While trace running can help with logical correctness, it's not effective for identifying performance issues.
Dynamic typing issues: In languages with dynamic typing, certain type-related errors might only become apparent during runtime.
Memory-related bugs: Issues like memory leaks or buffer overflows are challenging to detect through manual tracing alone.
In these cases, other debugging techniques such as using debuggers, logging, profiling tools, or automated testing might be more effective.
However, trace running remains a valuable tool in a programmer's toolkit, especially for understanding and verifying logical flow in algorithms.
Subscribe to my newsletter
Read articles from Asesh Basu directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by