3.6Finding issues before they become problems

Last updated June 11, 2026

This chapter has armed you for bug hunts. The closing lesson is about needing fewer of them, because the cheapest bug to find is the one that never got written, and the second-cheapest is the one caught minutes after it was. A famous line from Brian Kernighan (co-author of the first great programming book) sets the stakes: debugging is twice as hard as writing the code in the first place, so code written at the limit of your cleverness is, by definition, beyond your ability to debug.

The habits, in the order you can adopt them:

Write less per step. You've had this one since lesson 1.12: build incrementally, compile constantly, test each function as it lands (the todo!() skeleton from lesson 2.6 exists for exactly this rhythm). The math is brutal and friendly: a bug introduced in the last four lines has a four-line search space; a bug introduced somewhere in the last two hundred has the chapter you just read.

Keep functions small and single-jobbed. Chapter 2's advice, now wearing its debugging hat: small functions are inspectable at a glance (lesson 3.3's "holdable" unit), their seams give probes and breakpoints natural homes, and one-job functions can be individually proven right, which is where this lesson is headed.

Don't mix behavior changes with cleanup. When improving code structure (renaming, splitting a function, the lesson 1.12 tidy pass), change no behavior; when changing behavior, leave the furniture alone. Each kind of change is easy to verify by itself: cleanup should produce identical output, a behavior change should produce exactly the intended difference. Interleaved, the two un-verify each other, and "it's cleaner now, but the totals are different" is a sentence you only have to say once to believe this paragraph.

Think defensively. Ask of each function: what's the worst its caller could hand it? The guessing game of chapter 7 and the input-handling lessons of chapter 12 will give you tools for acting on the answers (and the grand version of the idea, designing types so wrong states can't even be expressed, is half of what chapters 11 and 16 are about). The habit to start now is merely asking the question while you write, because the asking is what surfaces the assumption you didn't know you were making.

Let the tools nag you. You set this up in chapter 0 and it's been paying out since: warnings on and heeded (a compiler warning is a bug report filed early; the unused-variable warning in lesson 3.4's hunt had the answer before we started hunting), and cargo clippy run now and then, whose extra lints catch the suspicious-but-legal tier. This habit costs nearly nothing, which makes it the best deal on the page.

Write down what "correct" means, runnably. The chapter's closing idea, and the next big one in your future. Today, "test get_number" means running the program and typing things, and the knowledge of what-should-happen lives in your head, checked by eyeball, forgotten by Friday. Rust ships machinery for writing that knowledge into the project, where it re-checks itself on demand, forever:

For advanced readers

A two-line taste of chapter 14, legal in any project today:

#[test]
fn adding_works() {
    assert_eq!(add(5, 3), 8);
}

cargo test finds every function marked #[test], runs them all, and reports any whose assertions fail. The chapter 3.1 lab rat (add that subtracts) dies instantly under this test, and keeps dying on every future edit that breaks addition again, with no human remembering to check. That's retest-the-neighbors (step 6) automated. Chapter 14 builds the full practice; consider this the trailer.

Key insight

Every habit above is the same trade: a small constant cost while writing, in exchange for shrinking the search space when (not if) something's wrong. Debugging skill is partly the hunting of chapters like this one, and partly arranging your code so the hunts stay short. The second part compounds.

Quiz time

Question #1

You're about to rename some variables for clarity and fix an off-by-one in the same sitting. What does this lesson tell you to do instead, and what does it buy?

Show solution

Two passes: cleanup first (verify output unchanged), then the fix (verify exactly the intended change). Each pass is independently checkable; combined, neither is, and a new wrong result could be from either edit.

Question #2

Which existing habit from chapter 0 is also a bug-prevention habit, per this lesson, and why?

Show solution

Treating warnings as signal (plus the occasional cargo clippy run). Warnings are the compiler reporting suspicious code before it misbehaves; lesson 3.4's wiring bug was flagged by an unused-variable warning before any debugging began.

That's the toolkit and the temperament. Summary and the chapter's bug-hunting quiz are next, and the quiz hands you real broken programs.