3.2The debugging process

Last updated June 11, 2026

Something you believed about your program is false. That's what a bug is, underneath: the program is doing exactly what the code says, and the code doesn't say what you think it says. Debugging is the process of locating the false belief, and while inspiration is welcome, you can't schedule it. A process, you can schedule.

The six steps

  1. Reproduce the problem. Find inputs or steps that make the bug appear on demand. A bug you can't summon is a bug you can't study, and "can't reproduce it anymore" is not the same as "fixed it."
  2. Locate the root cause. Narrow down where in the code reality and belief diverge. Usually the hard step, and the one most of this chapter's tools serve.
  3. Understand it. Don't just stare at the guilty line; figure out why it misbehaves, or your fix will be a guess wearing a lab coat.
  4. Plan the fix. Decide the change, and consider what else that change could disturb.
  5. Fix it. Usually the easy part, pleasantly.
  6. Retest. Confirm the original problem is gone and poke the neighbors: did the fix break anything nearby? (Chapter 14 eventually automates this step, which is most of why tests exist.)

An everyday calibration, no computer required: the reading lamp won't turn on. Reproduce: flip the switch twice, dead both times. Locate: is it the bulb, the lamp, or the outlet? Move the lamp to a working outlet (still dead), swap in a known-good bulb (lights up). Root cause located: the bulb. Understand: burned out, presumably; if the new bulb dies in a week, revisit (maybe the lamp is eating them). Fix: done already. Retest: lamp works in its original outlet too. You've debugged hardware this way your whole life; the discipline is just doing it in order instead of swapping parts at random.

On code

Here's yesterday's lab rat, plus a report from its user: "your adding program said 5 plus 3 is 2."

fn add(x: i32, y: i32) -> i32 {
    x - y
}

fn main() {
    println!("{}", add(5, 3));
}

Walk the steps. Reproduce: run it; 2 every time. Reliable bugs are a gift. Locate: the program has two parts, the printing in main and the math in add; the printed number is a result, just the wrong one, so suspicion lands on the math. (With only one suspect function, locating is instant; lesson 3.3 covers narrowing when there are forty.) Understand: read add's body with fresh eyes: x - y. The function subtracts; 5 minus 3 is 2. The false belief was "this function adds," planted by its name and the author's intention. Plan: change - to +; the function touches nothing else, so collateral risk is nil. Fix. Retest: add(5, 3) now prints 8; try a couple more pairs (negatives, zeros) for the neighbors check.

fn add(x: i32, y: i32) -> i32 {
    x + y
}
8

Trivial example, real anatomy. Every debugging session you'll ever run, including the multi-day epics, is these six steps; the epics just spend longer trapped in step 2.

Best practice

When a bug surfaces, reproduce it before theorizing about it. The reflex to start changing code immediately, mid-hunch, is strong and almost always counterproductive: change the code before you can summon the bug, and you can no longer tell whether you fixed the problem or merely scared it into hiding.

Author's note

Step 3 deserves its reputation as the step everyone skips. A fix applied without understanding ("I flipped something and it works now") leaves the false belief in place, and false beliefs are repeat offenders. I've shipped that kind of fix. It came back. They come back.

Quiz time

Question #1

A user reports "the program crashes sometimes." What's wrong with starting to edit code right away, per this lesson's process?

Show solution

"Sometimes" means there's no reproduction yet (step 1). Without a reliable way to trigger the crash, you can't locate the cause, and worse, you can't ever confirm a fix: if it stops crashing, was it your edit or luck? First find inputs/steps that crash it on demand.

Question #2

You found the guilty line and a one-character fix makes the symptom vanish. Which steps remain, and why aren't you done?

Show solution

Understanding (if you haven't truly explained why the line misbehaved) and retesting: confirm the original case, then check the surrounding behavior the change could have disturbed. A symptom vanishing is necessary evidence, not sufficient.

The process tells you what to do. The next lesson is about the hard middle of it: strategies for locating a bug when the program is bigger than one suspect.