Concepts / Writing Code That Is Easy to Debug

Writing Code That Is Easy to Debug

Reading is especially useful for a simple bug. Before adding temporary display code or making experimental changes, check whether the code says what you think it says.

  • Programming

When Investigation Begins

Debugging is the process of finding the cause of an error in your code. You need it when Python reports an error message or when your program runs but produces an incorrect result. A program does not have to crash to contain a bug: incorrect output is also a reason to investigate.

Debugging is not a random hunt in which you change several lines and hope the result improves. It is a systematic process. You gather information, compare the program with your intention, test focused changes, interpret what happens, and restore clarity when your experiments become confusing.

Error message orincorrect resultRead the codeCause identifiedRun an experimentAnalyze the evidenceinvestigateif unclearinterpret resultsdeduce
What happens from the moment an error message or incorrect result appears until its cause is identified?

Reading the Intended Logic

Reading is often the first and fastest debugging strategy. Examine the code carefully and compare what it actually does with what you intended it to do. Read it aloud or trace it line by line. Simple mistakes such as a misspelled variable name, a reversed comparison operator, a missing colon, or logic that differs from your intention can become visible during this comparison.

Worked example

Comparing an Algorithm with Its Implementation

A programmer intends an algorithm to check each item, keep the items that meet a condition, and then report the resulting collection. The output does not match the expectation.

  1. 1
    State the intended steps

    Write down the logic in plain language: examine each item, test the condition, keep matching items, and report them.

  2. 2
    Trace the actual code

    Follow the program line by line and note what each operation actually does. Do not assume that a line expresses the intended step merely because it looks similar.

  3. 3
    Compare the two descriptions

    Look for a difference such as a reversed condition, a skipped item, or a result being reported before all items have been examined.

  4. 4
    Correct the mismatch

    Change the part that differs from the intended logic, then verify the behavior again.

Result

The reading strategy finds a logic error by comparing intention with behavior, rather than by changing several parts of the program at once.

Examine each itemExamine each itemTest the conditionDifferent conditionKeep matchesKeep matchesthenif matchedthenif matched
How do the intended steps of the algorithm map to the code, and where can they diverge?
Key point

Reading is especially useful for a simple bug. Before adding temporary display code or making experimental changes, check whether the code says what you think it says.

Running Focused Experiments

When careful reading does not reveal the problem, use the running strategy. Make a small, deliberate change and observe the result. Temporary display code, sometimes called scaffolding, can show the values held by variables or the intermediate results produced at different points. This evidence helps narrow down where the problem occurs.

Worked example

Narrowing Down an Incorrect Result

A program produces an incorrect final result, but reading the code has not shown the cause.

  1. 1
    Observe an intermediate value

    Add temporary display code after an early calculation to see whether the first stage produces the expected value.

  2. 2
    Interpret the location

    If the intermediate value is already wrong, investigate the earlier calculation. If it is correct, leave that part alone and examine a later stage.

  3. 3
    Test one focused change

    Change one calculation or condition and run the program again. Record whether the output changes and how it changes.

  4. 4
    Remove or revise scaffolding

    Use the temporary information to locate the problem, then keep the useful correction rather than leaving unrelated experimental code in place.

Result

Running turns an unclear problem into a sequence of observations. Each experiment supplies information about where the behavior begins to differ from what was expected.

Incorrect resultIntermediate valueOne code changeNew resultProblem locationinspectfocusrunnarrow down
How does changing one part of the code affect the program's behavior and help isolate the problem?

Ruminating on Evidence

Ruminating means stepping back and thinking analytically about the problem. Use your understanding of Python, the observed behavior, the error message, and the changes you made recently. The goal is to connect the symptom with a plausible cause instead of reacting to it randomly.

Error typeWhat it meansUseful question
Syntax errorThe code does not follow Python's grammar rules.Which part of the code violates the language's required form?
Runtime errorThe code is grammatically correct, but something goes wrong while Python executes it.What operation fails during execution?
Semantic errorThe code runs without crashing but produces the wrong result because the logic is incorrect.Which part of the logic differs from the intended result?

Error categories guide the questions you ask during debugging.

Observed symptomSyntax errorLikely root causeRuntime errorSemantic errorclassifyclassifyclassifygrammar mismatchexecution problemlogic mismatch
How does an observed error lead through possible causes to the underlying root cause?
Best practice

Read error messages carefully. They often indicate what went wrong and where. Also review your most recent change, because a bug often appears shortly after a modification. Ask: What kind of error could produce this exact symptom? What did I change that could cause it?

Retreating to Clarity

Sometimes experimentation creates a second problem: you have made so many changes that you no longer know what changed, which changes helped, or which changes caused new behavior. At that point, retreating is the sensible strategy. Undo the recent changes and return to a version of the program that you know works.

Retreating is not giving up. A known working state gives you a clear starting point. From there, make changes carefully, test each one separately, and keep track of what happens. This reduces the confusion created by layers of experimental changes and makes it more likely that you will find the real problem.

Before
Known working version
        |
        v
Several untracked changes
        |
        v
Confusing broken state

The program has accumulated changes and its behavior is difficult to interpret.

After
Confusing broken state
        |
        v
Undo recent changes
        |
        v
Known working version
        |
        v
One deliberate change at a time

The program is restored to a known working state so new changes can be tested deliberately.

Known working versionUndo recent changesConfusing brokenstateKnown working versionOne deliberate changemany changesretreatrestorerestart
How does the program move from a broken state back to a known working version?

Choosing the Next Strategy

Reading, running, ruminating, and retreating are not fixed steps that every bug must pass through. They are tools. A simple bug may be solved by reading alone. A complex bug may require reading, experiments, analysis, and then retreating if the experiments become confusing. You can move between the strategies as new information appears.

  • Start with reading because it is fast and often reveals a simple mismatch between intention and code.
  • Use running when reading does not show where the behavior changes. Add temporary display code and test one change at a time.
  • Use ruminating when experiments produce information but the cause is still unclear. Classify the error, inspect its message, and consider recent changes.
  • Use retreating when accumulated changes have made the program difficult to understand. Return to a known working state and begin again more deliberately.
  • Common mistake

    Changing several parts of the program at once

    What it looks like

    A programmer changes a calculation, a condition, and the surrounding control logic before running the program again.

    Why it fails

    The programmer cannot tell which change fixed the problem or caused a new one.

    Do this instead

    Change one thing at a time and observe the result.

  • Common mistake

    Assuming that a program is correct because it does not crash

    What it looks like

    The program finishes, but its answer is wrong.

    Why it fails

    A semantic error can let code run while producing an incorrect result.

    Do this instead

    Treat an incorrect result as a debugging signal and compare the logic with the intended behavior.

  • Common mistake

    Ignoring the error message

    What it looks like

    A programmer immediately rewrites code without examining what the message says or where it points.

    Why it fails

    The message often provides information about what went wrong and where.

    Do this instead

    Read the message carefully and use the error type to guide the investigation.

  • Common mistake

    Continuing to add changes after losing track of the program

    What it looks like

    Several experiments have made the code substantially different from the last known working version.

    Why it fails

    Layers of untracked changes make the cause harder to identify.

    Do this instead

    Retreat to a known working state, then test new changes deliberately.

Practice the Debugging Cycle

MEDIUM

Imagine that a program produces an incorrect result and no obvious mistake appears during the first reading. Describe what you would do next using the four strategies. Include the temporary information you would seek, the error questions you would ask, and the point at which you would retreat.

Hints
  • Begin with a small experiment rather than several unrelated changes.
  • Use temporary display code to inspect an intermediate value.
  • Consider whether the symptom suggests a syntax, runtime, or semantic error.
  • Retreat if you can no longer explain which changes produced the current behavior.
Worked example

A Complete Strategy Choice

A program runs but reports an incorrect answer. The programmer has already made several experimental changes and no longer knows which version was reliable.

  1. 1
    Retreat

    Return to the known working version instead of adding more untracked changes.

  2. 2
    Read

    Compare the intended algorithm with the restored code line by line.

  3. 3
    Run

    Make one focused change or add temporary display code to inspect an intermediate result.

  4. 4
    Ruminate

    Interpret the observed result, consider that an incorrect answer may be a semantic error, and review the most recent change.

Result

The strategies work together: retreat restores clarity, reading checks the logic, running gathers evidence, and ruminating connects that evidence with a likely cause.

Key Takeaways

  1. 1
    Debugging finds the cause of an error, whether the program reports a message or produces an incorrect result.
  2. 2
    Reading compares the code's actual logic with the programmer's intended logic.
  3. 3
    Running uses small experiments and temporary display code to narrow down where a problem occurs.
  4. 4
    Ruminating uses error types, messages, and recent changes to reason about root causes.
  5. 5
    Retreating restores a known working state when accumulated changes have made the program confusing.

At a glance

  • Debugging is a systematic investigation triggered by an error message or an incorrect result.
  • Begin by reading the code against the intended logic, then run focused experiments when necessary.
  • Use error categories, messages, and recent changes to reason about possible causes.
  • If experimentation creates confusion, retreat to a known working state and restart carefully.

More concepts starting with W