█
LastWrite
  • > Curriculum
  • > Pricing
  • > For Educators
  • > About
  • > Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
LastWrite

Structured computer science lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 LastWrite. All rights reserved.
Curriculum/Software Engineering/Engineering Fundamentals/Debugging Systematically
50 minBeginner

Debugging Systematically

After this lesson, you will be able to: Read a stack trace, set breakpoints in VS Code, reproduce bugs reliably, and find root causes instead of patching symptoms.

Debugging is the most under-taught skill in software. Most engineers debug by trial and error; senior engineers debug by hypothesis. This lesson teaches the methodical approach.

Prerequisites:Code Quality and Readability

Reproduce first, fix later

If you can't reproduce a bug reliably, you can't be sure you fixed it. Step one is always: what's the minimum sequence of actions that triggers the bug? If the reproduction is flaky, the bug is usually in shared state, timing, or external dependencies (databases, APIs, files).

Read the stack trace, top to bottom

The TOP of the stack is where the error happened. The BOTTOM is where the program started. Most useful frames are the ones in YOUR code, not in node_modules or framework internals. Look at the line, the variable values (if available), and the call chain. Most stack traces tell you 80% of what you need.

VS Code debugger: launch.json for Node

Drop this in .vscode/launch.json. Hit F5. Set breakpoints by clicking left of the line numbers.

json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug current file",
"program": "${file}",
"runtimeArgs": ["-r", "ts-node/register"],
"skipFiles": ["<node_internals>/**"]
},
{
"type": "node",
"request": "attach",
"name": "Attach to running app on :9229",
"port": 9229
}
]
}
// For an existing Next.js dev server:
// Run with: NODE_OPTIONS='--inspect' npm run dev
// Then attach via the second config above. Breakpoints in your code halt the server.

Binary search the bug

Git bisect: when a bug appeared between commit X (works) and HEAD (broken), `git bisect` narrows it to the single commit that introduced it in log2(n) steps. Code bisect: comment out half the code, see if the bug remains. Repeat on whichever half still breaks. Both are how senior engineers find bugs they don't understand: they don't think harder, they isolate.

Root cause vs symptom

Patching the symptom: 'add a null check, the error goes away.' Finding the root cause: 'why was that value null in the first place? Because the API returned a 204 No Content and our parser silently fell through. Fix the parser to throw on 204.' Five whys: ask 'why' five times in a row. The fifth answer is usually the real bug. Symptom-patching multiplies bugs over time; root-causing eliminates them.

The methodical debugging loop

Use this for every non-trivial bug.

  1. 1

    Reproduce the bug at least twice in a row deliberately

  2. 2

    Read the stack trace and identify the exact line

  3. 3

    Form a hypothesis: WHY would this line behave this way?

  4. 4

    Design a single test that would confirm or deny the hypothesis (a logged value, a breakpoint, a unit test)

  5. 5

    Run the test, observe, update the hypothesis

  6. 6

    Repeat until the hypothesis is confirmed

  7. 7

    Fix the root cause, not the symptom

  8. 8

    Add a regression test so the bug can never silently come back

  9. 9

    If the root cause exposed a class of bugs, search the codebase for similar patterns

Common mistakes only experienced engineers avoid

Guessing fixes without forming a hypothesis. Each random change is an experiment with no learning. Trusting console.log forever. The VS Code debugger inspects variables, walks the stack, and changes values inline. Learn it. Skipping the regression test. Without it, the next deploy can silently reintroduce the same bug. Treating 'works on my machine' as a fix. The next environment exposes the missing case. Hiding the bug behind a try/catch. Now you have two bugs, the original and the silent failure.

Quick Check

Your test passes locally but fails in CI. What's the FIRST thing you investigate?

Pick the highest-leverage diagnostic.

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←Code Quality and Readability
Back to Engineering Fundamentals
Testing Fundamentals→