Skip to content

Debugging Guide

A hypothesis-driven methodology for isolating root causes. This approach replaces random code changes with a structured elimination process.


Every debugging session follows this loop until the root cause is confirmed:

1. Form hypotheses (what could cause this?)
2. Rank hypotheses (most likely first)
3. Design the smallest test for the top hypothesis
4. Run the test
5. If confirmed → you have the root cause → fix it
6. If ruled out → cross off the hypothesis, move to the next
7. If inconclusive → refine or decompose the hypothesis

Never move to fixing until the root cause is confirmed. Fixing a symptom wastes time and often makes the next debugging session harder.


Before forming any hypotheses, read the full error output.

What to extract from an error:

Error ComponentWhat It Tells You
Error typeCategory of problem (TypeError, NetworkError, AuthError, etc.)
Error messageWhat specifically failed
Stack traceCall chain that led to the error — read from top (most specific) to bottom (most general)
Line number / fileWhere to start reading
TimestampWhether this is intermittent or consistent

Common error types and where they point:

Error PatternLikely Location
Cannot read property of undefinedNull check missing upstream
ECONNREFUSED / ENOTFOUNDNetwork config, wrong URL, service not running
401 UnauthorizedAuth token missing, expired, or incorrectly set
404 Not FoundRoute/endpoint mismatch, wrong ID, data doesn’t exist
500 Internal Server ErrorServer-side bug — check server logs
CORS errorMissing CORS header on the API or wrong origin
SyntaxErrorMalformed JSON, incorrect template literal

After reading the error, list plausible root causes. Good hypotheses are:

  • Specific: “The userId is undefined when getUser() is called” not “something is wrong with auth”
  • Falsifiable: There’s a test that can rule it in or out
  • Bounded: Points to a specific code path, not “the whole system”

Hypothesis sources:

  1. The stack trace — Follow the call chain. Where does control enter broken code?
  2. Recent changes — If this is a regression, what changed recently? git log --oneline -20 and git diff are your friends.
  3. Environmental differences — Does it only happen in prod? In a specific browser? With specific data?
  4. Data flow — Trace the data from its source to where the error occurs. Where could it become invalid?

Limit to 2-4 hypotheses. If you have 10 hypotheses, you don’t understand the bug well enough yet. Re-read the error and trace the call chain more carefully.


For each hypothesis, design the smallest possible test to rule it in or out.

Test TypeWhen to UseExample
Log/inspectQuick data validationconsole.log(userId) just before the failing call
Isolate in REPLTest a function in isolationRun the function with the suspect input in a test or REPL
Add a unit testReproduce the exact failing scenarioWrite a test with the suspect input; confirm it fails on current code
Bisect with gitFind which commit introduced a regressiongit bisect to narrow down the breaking change
Simplify the inputRule out data-specific behaviorUse a known-good minimal input (e.g., hardcoded test user)
Remove a layerIsolate which layer contains the bugComment out middleware, skip a transformation step, mock an external call

Rule for elimination tests:

  • If the test passes → hypothesis is ruled out
  • If the test fails in the expected way → hypothesis is confirmed, or at least narrowed
  • If the test fails in an unexpected way → new information — update your hypotheses

The root cause is confirmed when you can answer all three:

  1. “Why does the bug occur?” — A specific, mechanical explanation: “The session token is stripped by the CORS preflight middleware before the auth middleware reads it.”
  2. “Why does it only occur under these conditions?” — Explains the reproduction steps: “Only on POST requests from a different origin, because GET requests are not preflighted.”
  3. “How would fixing X make the bug disappear?” — Mechanically: “Moving the CORS middleware after the auth middleware means the token is present when auth reads it.”

If you cannot answer all three, you have not found the root cause yet.


Trace backward from the error: what was expected to be defined but wasn’t? Trace data flow from its origin — where could it become null/undefined? Check: optional chaining, conditional rendering, async initialization timing.

Check in order: (1) Is the request being made? (2) Is it going to the right URL? (3) Is the payload correct? (4) Is the response what you expect? Use browser DevTools Network tab or server logs to inspect the actual request and response.

If the bug is intermittent and timing-related: look for shared mutable state, missing await, parallel async operations that depend on each other’s results, or event handlers that fire in unexpected order.

If the data displayed doesn’t match what’s in the database: trace the data flow from persistence to display. Check caching layers, in-memory state, optimistic updates, and query invalidation.

Diff the environments: environment variables, feature flags, database state, browser version, OS. Reproduce in a clean environment if possible.


Investigation phaseTime box
Reading the error + initial hypotheses5 minutes
Testing one hypothesis10-15 minutes
Full debugging session before escalating45-60 minutes

If you’ve spent 45 minutes without confirming a root cause, stop and reassess:

  • Are the reproduction steps correct? Can you actually reproduce it?
  • Is the hypothesis list correct? What assumption might be wrong?
  • Do you need more information (logs, environment access, a different reproduction case)?

Escalating early is not failure. Debugging in circles for hours is.