Skip to content
Ameen Abozaid
PublishedBEG-016-L04

Hand-tracing with a variable table

  • Lesson 4 of 6
  • 12 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

You have a plan in pseudocode, or you have Java code that gives the wrong answer and you cannot see why. In both cases the most reliable move is the same: stop guessing and run the program on paper.

Hand-tracing means executing a program yourself, one line at a time, and recording the value of every variable after each step in a variable table. It sounds slow, yet it is often the fastest way to find a bug, because it forces you to see what the code does instead of what you believe it does. This is the trip analogy again: you check the route on the map, town by town, before you spend fuel on it.

Why this matters: your eyes skip over code, and your brain fills in what you meant. A table does not. It also teaches you to think like the computer, the skill the debugger later automates.

In the previous lesson, "Pseudocode and flowcharts", you wrote plans. Here you learn to check a plan or a program by executing it.

Learning Objectives

By the end of this lesson you will be able to:

  • Build a variable table with one column per variable and one row per step.
  • Execute a loop on paper, recording values after each change.
  • Compare the table with your expected values and find the first row that differs.
  • Trace code that uses an array, a condition, and a method call.
  • Use a trace to find and fix a bug.

Written Explanation

The variable table

A variable table has:

  • one column for each variable that matters (and for any condition worth recording),
  • one row for each moment you want to record, usually after each pass of a loop or each change of a variable.

Here is the procedure:

  1. List the variables of the code you are tracing and make the columns.
  2. Pick a small input. Small means three or four values, so the table stays short.
  3. Write the starting row with the values the variables have before the loop.
  4. Execute one line at a time, exactly as written, and write down every value that changes. Do not skip ahead and do not "fix" things as you go.
  5. When the loop condition becomes false, stop. Write the result.
  6. Compare the table to what you expected.

The rule that makes tracing work is step 4: act like the computer, which does what the code says, not what you intended.

A first table

Take sum = 0 then FOR i FROM 1 TO 3: sum = sum + i * i.

stepisum
start-0
111
225
3314

Each row is the state after one pass. The final 14 matches the hand calculation 1 + 4 + 9.

Finding the bug: the first row that differs

Write an expected value next to what you get. A bug sits at the first place where the actual value differs from the expected one. Later rows may also be wrong, but they are only consequences. So: write down what you expect in the key rows, trace what the code does, find the first row where they differ, and look at the single line that produced it.

Tracing conditions and arrays

When a loop contains an if, add a column for the condition result, so you can see which branch ran. With arrays, add a column for the index i and one for the element a[i]; if the array is changed by the code, record the array contents too. Write arrays in braces, for example {3, 8, 2}.

Tracing method calls

When the code calls a method, treat the call as one step if you trust the method, or step inside it with a table for its variables: the parameters take the argument values and the locals start fresh, as with scope and the call stack. Write the returned value into the caller's table.

Tips for good traces

  • Small inputs first. If the trace is long, the input is too big.
  • Record, do not remember. Anything not in the table does not exist.
  • Use the unusual example. A tie, a zero, or a single element often reveals what the typical example hides.
  • A debugger does the same thing automatically, as you will see later in the course.

Examples

Example 1: a loop with a condition (simple)

Trace this code and say the final value:

java
int sum = 0;
for (int i = 1; i <= 5; i++) {
    if (i % 2 == 0) sum += i; else sum -= 1;
}
stepii % 2 == 0sum
start--0
11false-1
22true1
33false0
44true4
55false3

When i becomes 6 the test i <= 5 fails and the loop ends. The final value of sum is 3. Printing sum inside the loop gave exactly -1, 1, 0, 4, 3.

Example 2: tracing finds a bug (practical)

Problem: "count how many times the largest value appears in an array". A beginner writes:

java
static int countLargest(int[] a) {
    int max = a[0];
    int count = 0;
    for (int i = 1; i < a.length; i++) {
        if (a[i] > max) { max = a[i]; count = 1; }
        else if (a[i] == max) { count++; }
    }
    return count;
}

It looks fine, and for {3, 8, 2, 8} it prints 2, which is right. Tracing an unusual input tells a different story. Take {8, 3, 8} and expect 2. Write the expected values first: after the whole array, max is 8 and count is 2.

stepia[i]maxcountexpected count
start--801
113801
228812

The method returns 1 but the answer should be 2. The first row that differs from expected is the start row: before the loop, max is 8 and the value 8 has been seen once, so count should already be 1. The bug is the line int count = 0;, which forgets the first element. The fix is int count = 1;. Running the buggy code on {3, 8, 2, 8}, {8, 3, 8}, and {8, 3, 2} printed 2, 1, and 0. The last one is clearly wrong, since 8 appears once.

The loop is fine. Without an expected column you would have stared at it.

Example 3: a method call with a condition (realistic)

Here is the grade report from the decomposition lesson, traced on {80, 105, 70} through its steps for the sum and count of valid grades. We combine them in one table to see the whole run.

stepigvalid?sumcount
start---00
1080true801
21105false801
3270true1502

After the loop the average is 150 divided by 2, which is 75.0; a program that prints each row gave the same values. In this table valid? is the result of calling isValid(g); because we trust that method, we recorded one value instead of stepping inside it. If isValid were suspect, we would trace it separately on 105: 105 >= 0 is true, 105 <= 100 is false, so the result is false.

Backend Insight

Tracing is how experienced developers read unfamiliar code and diagnose faulty behaviour. When a production report shows a wrong total, nobody guesses; they pick a small failing input, follow the data through each step, and find the first place the data differs from what the business expects. Tools such as logs and debuggers show the values of variables at each step, which is the same variable table produced by a machine.

The habit also helps in code review, where a tricky condition is often traced on one or two examples, including a boundary value.

Common Mistakes

Mistake 1: Tracing what you meant, not what the code says

  • Incorrect: writing count = 1 in the start row because that is what you intended.
  • Correct: writing count = 0 because the code says int count = 0;.

Mistake 2: Skipping the table and "just reading"

The brain fills gaps. Make the table, even a small one.

Mistake 3: Not writing expected values

Without an expected column you only see numbers, with nothing to compare them to.

Mistake 4: Forgetting the last pass

Many bugs live in the final row of a loop: whether the last element is included, and whether the test stops one pass too early or too late. Always write the row in which the test fails.

Exercises

Try each exercise yourself before opening the solutions. Run the code afterwards to check your trace.

Level A: Foundation

A1. What are the columns and rows of a variable table?

Show solution A1

A1. One column for each variable that matters (and any condition worth recording), and one row for each moment you record, such as after each pass of a loop.

A2. Trace and give the final values of a and b:

java
int a = 3;
int b = 4;
a = a + b;
b = a - b;
a = a - b;
Show solution A2

A2.

stepab
start34
174
273
343

Final: a is 4 and b is 3. The three lines swap the two values without a third variable. Running it printed 4 3.

A3. Where in a trace do you look for a bug?

Show solution A3

A3. At the first row that differs from the expected value. That row points to the line that caused it.

Level B: Application

B1. Trace this loop and give the final value of c, with a table:

java
int x = 20;
int c = 0;
while (x > 3) {
    x = x - 5;
    c++;
}
Show solution B1

B1.

stepxx > 3 before the passc
start20-0
115true1
210true2
35true3
40true4

Now x > 3 is false (0 is not above 3), so the loop ends. Final c is 4, and x is 0 (the run printed 0 4).

B2. Trace countLargest from this lesson with the fix int count = 1; on {8, 3, 2} and {5, 5, 5}. What does each call return?

Show solution B2

B2. For {8, 3, 2}: start max 8, count 1; i=1: 3 is not above 8 and not equal, no change; i=2: same. Returns 1. For {5, 5, 5}: start max 5, count 1; i=1: equal, count 2; i=2: equal, count 3. Returns 3. (Traced by hand; the buggy version's outputs shown in the lesson were run.)

B3. A student traces a method and writes the table below for sum of {2, 4} with a loop for (int i = 1; i < a.length; i++) sum += a[i];. The student expected 6. Which row is the first to differ, and what is the bug?

stepisumexpected sum
start-00
1142
Show solution B3

B3. The first differing row is row 1: the table shows sum 4 where 2 was expected. The loop starts at index 1, so the first element, 2, is skipped. The fix is to start at i = 0. The final answer would be 4 instead of 6.

Level C: Challenge

C1. Trace this nested loop with a table that has columns i, j, and count, and give the final count:

java
int count = 0;
for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= i; j++) {
        count++;
    }
}
Show solution C1

C1.

ijcount
start-0
111
212
223
314
325
336

Final count is 6 (the run printed 6). The inner loop runs i times for each i: 1 + 2 + 3.

C2. This code is meant to reverse an array in place. Trace it on {1, 2, 3, 4} (columns: i, the swapped pair, and the array) and explain the bug.

java
for (int i = 0; i < r.length; i++) {
    int t = r[i];
    r[i] = r[r.length - 1 - i];
    r[r.length - 1 - i] = t;
}
Show solution C2

C2. Pairs for {1, 2, 3, 4}: i=0 swaps positions 0 and 3: {4, 2, 3, 1}; i=1 swaps positions 1 and 2: {4, 3, 2, 1}; i=2 swaps positions 2 and 1 again: {4, 2, 3, 1}; i=3 swaps positions 3 and 0 again: {1, 2, 3, 4}. The first row that differs from the expected reversed array is i=2, where an already-swapped pair is swapped back. The loop must stop at the middle (i < r.length / 2). Running the buggy code printed [1, 2, 3, 4], the original array.

Mini Challenge

Take this code and the input {4, 7, 7, 2}:

java
int best = a[0];
int index = 0;
for (int i = 1; i < a.length; i++) {
    if (a[i] > best) {
        best = a[i];
        index = i;
    }
}
  1. Write what you expect best and index to be at the end for this input, before tracing.
  2. Trace it with columns i, a[i], a[i] > best, best, and index.
  3. Say what happens with the tie, and whether that agrees with "the first largest value".
  4. Change the code so that it returns the position of the last largest, and trace your change on the same input.

Check your work: your table has a start row and a row for every pass, expected values were written first, and you identified the line that decides the tie.

Summary & Next Lesson

Key takeaways

  • Hand-tracing means executing code one line at a time and recording variable values in a table: columns for variables, rows for steps.
  • Act like the computer: follow what the code says, not what you meant.
  • Write expected values. The bug is at the first row that differs.
  • Trace small inputs, and include a column for any condition and the row where a loop ends.
  • A debugger does this automatically later, but tracing builds the skill.

Next lesson

You can now check a plan on one input. The next lesson, "Thinking in edge cases", is about choosing which inputs to check: the unusual ones at the limits where code tends to break.

Self-check

  1. What does each row of a variable table represent?
  2. Where is the bug in a trace?
  3. Why write expected values?
  4. Why should the last pass of a loop get its own row?
  5. When you trace a call to a method you trust, what can you write instead of stepping inside it?
Show answers
  1. The values of the variables at one moment, usually after one pass of a loop or one change.
  2. At the first row that differs from what you expected.
  3. They give you something to compare with, so you can find the first row that differs.
  4. Many bugs concern whether the loop stops too early or too late, and whether the last element is included.
  5. The value the method returns, in one step.

References

  • Oracle, The Java Tutorials: The for Statement. Supports how the three parts of a for loop are evaluated, which hand-tracing follows.
  • Oracle, The Java Tutorials: The while and do-while Statements. Supports that a while loop tests its condition before each pass, as used in B1.
  • Every Java snippet in this lesson was compiled and run with JDK 21, and the values quoted as "ran" are real output. The variable-table method is a standard teaching technique and no single source is cited for it.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.