In this chapter
We'll learn to find logical errors like a detective: dry runs (playing the computer with pen and paper), tracing what happens step by step, the most common beginner mistakes, and a simple method for writing any algorithm.
The Problem in Real Life
Anna turns version 2 into code. In her first test, a fan holds a seat, waits two minutes, and pays. The system says: "Your hold has expired."
Two minutes, not ten. She reads the code five times. It looks right every time. "I don't understand," she says. "Every line makes sense."
John slides a blank sheet of paper across the desk. "Then stop reading it like a human. Read it like the computer — one line at a time, writing down every value."
Don't guess what the code does. Follow it, step by step, and watch where reality and your expectation split apart.
John
Staring at Code vs. Tracing It Step by Step
Your brain autocorrects
When you read your own code, you see what you meant to write, not what's actually there.
Play the computer
Following the code line by line on paper, writing every value, reveals what really happens.
Find where it splits
The bug is at the first step where the real value differs from the value you expected.
The Debugging Mindset
The detective analogy: a good detective doesn't guess who did it and then look for proof. They collect facts, follow the timeline step by step, and look for the exact moment the story doesn't add up. Debugging is the same. A bug is the gap between what you expected the code to do and what it actually did. Your job is to find the exact step where that gap appears.
The detective's most powerful tool is surprisingly simple: pen and paper.
- Dry run — playing the computer: you run the code in your head, on paper, without a computer. Go line by line, exactly as the computer would, and never skip a line because "it's obviously fine." It's called "dry" because nothing actually runs — like a rehearsal before the real show.
- Tracing — keeping a trace table: while you dry-run, write a small table: one column for each variable, one row for each step. After every line, write down the new values. This is called tracing the execution. The table makes the invisible visible.
- Finding the logical error: compare each row with what you expected. The first row where they differ is where the bug is. You don't need to understand the whole program — just find the first step where reality splits from your expectation.
| Step | Line of code | heldUntil | Expected | Match? |
|---|---|---|---|---|
| 1 | now = 10:00:00.000 | — | — | ✓ |
| 2 | heldUntil = now + 10 | 10:00:00.010 | 10:10:00.000 | ✗ — 10 ms, not 10 min! |
| 3 | (fan pays) now = 10:02:00.000 | 10:00:00.010 | 10:10:00.000 | — |
| 4 | IF now is before heldUntil | false → "expired" | true → take payment | ✗ |
The first ✗ is the bug. Everything after it is just a consequence.
| Mistake | Example | Fix |
|---|---|---|
| Off-by-one | count > 5 instead of count >= 5 | Test the exact boundary values |
| Wrong units | + 10 (milliseconds) meant as 10 minutes | Name the unit: HOLD_DURATION_MS |
| = instead of === | if (state = "SOLD") puts a value in | Use === to compare |
| Conditions in the wrong order | Group discount checked before student | Most specific / best case first |
| Forgetting the empty case | An empty cart crashes the total | Always test zero items |
| Not updating a value | Seat stays HELD after payment | Trace every variable |
| Infinite loop | A while condition that never becomes false | Make sure each loop moves toward its end |
| Mixing text and numbers | "5" + 1 gives "51" | Convert with Number() first |
| Step | What you do | For the seat hold |
|---|---|---|
| 1. Understand | Say it in your own words; list input and output | Seat, fan, time in → held / taken out |
| 2. Examples | Work examples by hand, including edges | Pay at minute 2, at minute 9, at minute 11 |
| 3. Steps | Break it down; pseudocode or flowchart | Versions 1 and 2 |
| 4. Dry run | Trace your steps on paper | The trace table above |
| 5. Code and test | Write code; test the same examples | Fix the units bug, test again |
// Before: Date.now() counts in MILLISECONDS — this is 10 ms, not 10 minutesseat.heldUntil = Date.now() + 10;// After: say the unit out loud in the nameconst HOLD_DURATION_MS = 10 * 60 * 1000; // 10 minutes × 60 seconds × 1000 msseat.heldUntil = Date.now() + HOLD_DURATION_MS;
Putting the unit in the name (MS for milliseconds) makes this whole family of bugs much harder to write.
Anna's trace: she writes a trace table for her test: the fan holds the seat at 10:00 and pays at 10:02. Line by line: now is 10:00. heldUntil = now + 10. She writes down the value... and stops. In JavaScript, time is counted in milliseconds — thousandths of a second. So + 10 added ten milliseconds, not ten minutes. The hold really did expire almost instantly; the payment page just loaded slowly enough that she didn't notice.
The fix is one line: heldUntil = now + 10 * 60 * 1000 (10 minutes × 60 seconds × 1000 milliseconds) — or better, a named constant HOLD_DURATION_MS. She runs the test again: hold at 10:00, pay at 10:02, and the seat is SOLD. The bug wasn't in a hard part at all. It was a hidden assumption about units — exactly the kind of thing your eyes skip and a trace table catches.
The beginner's mistake list: John shares a list he wishes he'd had on his first day. Almost every early bug is one of these: off-by-one (> vs >=, like BUG-142), wrong units (milliseconds vs. minutes), using = (put a value in) when you meant === (compare), conditions in the wrong order (the first true one wins — Act 07), forgetting the empty case (zero items, empty text), forgetting to reset or update a value, a loop that never ends, and mixing text and numbers ("5" + 1). When you're stuck, check this list first.
A simple method for writing any algorithm: John sums up the whole Act as five steps. 1. Understand: say the problem in your own words and write the input and output. 2. Examples: work a few examples by hand, including the edges. 3. Steps: break it down and write pseudocode or a flowchart. 4. Dry run: trace your steps with your examples on paper. 5. Code and test: only now write the code — and test it with the same examples, especially the edges.
Key Takeaway
A bug is the gap between what you expected and what actually happened. Don't stare and guess — dry-run the code like the computer, keep a trace table of every value, and find the first step where reality differs from your expectation. Then check the classic beginner mistakes, and plan every algorithm in five steps: understand, examples, steps, dry run, code and test.
Why This Matters
Every developer spends a large part of their time debugging, and the difference between a junior and a senior is often just method: guessing versus tracing. The habits from this chapter — dry runs, trace tables, the mistake checklist, the five-step method — will save Anna hours every week. In Act 17 she'll get real tools for this (debuggers and tests), but they all do what her pen and paper just did.
The seat hold works: picked, held for exactly ten minutes, sold or released, every "what if" answered. Samantha plans to turn it on for the next big sale. Before that, John wants to see Anna do the whole thing again on her own — understand, examples, steps and dry run.
