Concepts / Debugging Techniques for Control Flow

Debugging Techniques for Control Flow

While loops check their condition before each iteration; a boolean flag controls when the loop exits.

  • Programming
Interactive lab

Try it: Debug by Tracing Execution

How to debug a program that runs but gives the wrong answer: compare expected and actual output, trace the lines in the order they really run with a variable watch table (or an inserted debug print), find the first line where a value goes wrong, then choose a fix and re-run to check it.

How it works

  1. State the expected output from the specification, run the program and compare: a difference is the evidence you debug from.
  2. Trace in execution order, not file order: loops repeat lines, if/elif picks one branch, and the watch table records every variable before each line runs.
  3. An inserted print('debug: ...', value) shows the same state in the output; it must not change what the program itself prints.
  4. Look for the first place a value differs from what it should be (an off-by-one range, > instead of >=, a total reset inside the loop, two names for one list, a str from input()). Where the wrong value shows up is only the symptom.
  5. Change one thing with a reason, re-run, and compare again: a fix that makes an error disappear can still leave the output wrong.

Default run (14 steps): Sum 1..n: Add up the whole numbers from 1 to n (n included) and print the sum. Press Play to trace it line by line. … Finished, but the output is wrong: got sum of 1 to 5 is 10, expected sum of 1 to 5 is 15.

Simplified: The lab's own small Python interpreter runs fixed, hand-written programs (you pick the program, the fix and a value; your text is never executed). Every program the lab can show was run in CPython 3.12 and the lab reproduces its output, its traceback text and its line-by-line trace. Six short programs, one editable value each; input() receives the value you type in the lab. Lines starting with "debug:" are ignored when comparing the output with the expected output.

Educational simulation

Loading the simulation…

Close the Gap Between Intention and Execution

When a loop prints unexpected output or appears to run forever, the important question is not only what you intended the loop to do. You must determine what actually happened at each condition check, variable update, iteration, and branch. Loop tracing means following that exact sequence of state changes. Recording variable states step by step is the most reliable way to find where execution diverged from your mental picture.

Key point

Debug a loop as a sequence of states, not as one large block of code. At each step, ask what the condition is, what the variables contain, and which statement runs next.

Flag Checks Before Each Iteration

A while loop checks its condition before every iteration. When a boolean variable acts as a flag, the loop continues while that flag is True. The loop body can change the flag to False. That change does not erase the current iteration; it affects the next condition check. After the body finishes, the loop checks the flag again. If the flag is now False, the loop exits instead of starting another iteration.

runningTrueLoop bodyrunning remains TruerunningTrueLoop bodyset running to FalserunningFalseLoop exitTruecheck againTruecheck againFalse
What happens to the flag and loop execution each time the while condition is checked?
Worked example

Tracing a Flag-Controlled Loop

Suppose a loop starts with running equal to True and number equal to 1. During each loop body, number is processed. When number reaches 3, the body changes running to False. Trace the condition checks and state changes.

  1. 1
    Initial check

    running is True, so the while loop begins its first iteration with number equal to 1.

  2. 2
    First body execution

    The body processes number 1. The flag is not yet changed to False, so the next condition check still permits another iteration.

  3. 3
    Second body execution

    The body processes number 2. The loop continues to the next condition check.

  4. 4
    Third body execution

    The body processes number 3 and changes running to False.

  5. 5
    Final check

    The body has finished, so the while condition is checked again. running is now False, so no fourth iteration begins.

Result

The flag change ends the loop at the next condition check. It does not prevent the body that changed the flag from finishing.

Find the First Divergence

A useful debugging trace records the state before a condition check, the result of that check, the action taken in the loop body, and the state after the body. Compare this actual trace with your expected trace one step at a time. The first mismatch is the most useful location to investigate. Later unexpected behavior may simply be a consequence of that earlier mismatch.

  1. 1
    Write down the initial values of the loop variables and boolean flag.
  2. 2
    Record the condition before the first iteration.
  3. 3
    Record which branch or loop body runs.
  4. 4
    Write down every variable update made by that body.
  5. 5
    Check the condition again using the updated values.
  6. 6
    Compare the actual sequence with the sequence you expected and mark the first difference.
Condition checkexpected resultVariable updateexpected valueFirst mismatchinvestigate hereCondition checkactual resultVariable updateactual valueexpected pathactual pathcompare with expected
At which condition check, update, iteration, or branch does actual execution first differ from the expected path?

Natural Completion and Early Exit

A for loop iterates over the values in a sequence. Python also allows a for loop to have an else clause. The else block runs when the loop has gone through all of its iterations naturally. If a break statement exits the loop early, the else block is skipped. Therefore, reaching the end of the loop body repeatedly is different from leaving the loop through break.

Loop iterationsNatural completionall values exhaustedelse blockrunsbreakearly exitAfter loopcontinuesno breakbreak encounteredyesthenskip else
How does control flow differ when a loop finishes all iterations naturally versus when break stops it early?
1, 2, 3, 4range(1, 5)Each valueloop bodybreakencountered?elseruns after all valuesAfter loopnext valuechecknono break and values exhaustedyesthen
When does execution move from the for loop to else, and what changes if break is encountered first?
Worked example

Predicting the Else Clause

A for loop processes the values generated by range(1, 5). It prints each value, and its else block prints a completion message. Predict the order of events.

  1. 1
    Sequence creation

    range(1, 5) generates 1, 2, 3, and 4. The upper boundary 5 is not included.

  2. 2
    Iteration

    The loop processes and prints 1, then 2, then 3, then 4.

  3. 3
    Completion decision

    All values have been processed and no break has exited the loop.

  4. 4
    Else execution

    Because the loop completed naturally, control moves to the else block.

Result

The values 1, 2, 3, and 4 are processed first, followed by the else block.

Mistakes That Distort a Trace

  • Common mistake

    Treating a while condition as if it were checked only once

    What it looks like

    Expecting the loop to stop immediately when the flag changes inside the body

    Why it fails

    The current body execution continues. The changed flag is used at the next condition check.

    Do this instead

    Record the state after the body, then trace the next condition check explicitly.

  • Common mistake

    Assuming the for loop else block always runs

    What it looks like

    Expecting else to run even after a break statement

    Why it fails

    The else block runs only after natural completion, not after break exits early.

    Do this instead

    Mark whether the loop exhausted its values or left through break.

  • Common mistake

    Misreading range boundaries

    What it looks like
    Expecting range(1, 5) to include 5
    Why it fails

    The source example specifies that range(1, 5) generates integers from 1 up to but not including 5.

    Do this instead

    Write out the generated values before tracing the loop body.

  • Common mistake

    Skipping variable states between iterations

    What it looks like

    Recording only the final value instead of each update

    Why it fails

    The first unexpected result may arise earlier, during a condition check or update.

    Do this instead

    Record the relevant variables before and after every loop body execution.

Loop endingWhat happenedDoes for else run?
Natural completionAll loop iterations finished without breakYes
break terminationbreak exited the loop before all iterations finishedNo
while flag becomes FalseThe next condition check prevents another iterationNot applicable to the for else rule

Use the ending mechanism, not just the fact that the loop stopped, to predict the next control-flow step.

Trace Before You Change

EASY

A while loop begins with running equal to True and count equal to 0. Each iteration increases count by 1. When count becomes 2, the loop body changes running to False. Trace the initial condition check, every body execution, the value of count, and the final condition check. Then decide whether a third iteration begins.

Hints
  • The condition is checked before the first iteration and again after each body execution.
  • The body that changes running to False still completes.
  • Use the updated value of running for the next condition check.
MEDIUM

A for loop processes the values from range(1, 5). It contains a break that runs when the current value is 3, followed by an else clause. Predict which values are processed and whether the else clause runs. Explain your answer using the difference between natural completion and break termination.

Hints
  • range(1, 5) produces 1, 2, 3, and 4.
  • The break is reached while the value is 3.
  • A loop that exits through break has not completed naturally.

A Reliable Trace Routine

  1. 1
    A while loop checks its condition before every iteration.
  2. 2
    A boolean flag can keep a while loop running until the loop body changes the flag to False.
  3. 3
    A for loop else clause runs after natural completion and is skipped when break exits early.
  4. 4
    range(1, 5) includes 1, 2, 3, and 4, not 5.
  5. 5
    The first difference between the expected trace and the actual trace is the best place to begin debugging.

Key Takeaways

  • Trace loop execution by recording condition results, variable states, body actions, and the next control-flow step.
  • A while loop checks its boolean condition before each iteration, so a flag changed inside the body affects the next check.
  • A for loop with else runs the else block only when all iterations finish naturally.
  • A break exits a for loop early and skips its else block.
  • When debugging, locate the first point where actual execution differs from the expected trace.

Related concepts

More concepts starting with D