Systematic Debugging
Debug methodically instead of randomly changing code.
The Process
1. Reproduce
Before anything else, reproduce the bug reliably:
- Get the exact steps to trigger the issue
- Note the expected vs actual behavior
- Confirm it happens consistently (not intermittent)
- Record the environment (OS, Node version, browser, etc.)
If you can't reproduce it, you can't fix it. Ask for more details.
2. Isolate
Narrow down where the bug lives:
Binary search the codebase:
- Comment out half the system → does the bug persist?
- If yes, the bug is in the remaining half → repeat
- If no, the bug is in the commented-out half → repeat
Git bisect:
git bisect start
git bisect bad # current commit is broken
git bisect good <sha> # this commit was working
# Git checks out the midpoint — test it
git bisect good # or git bisect bad
# Repeat until it finds the first bad commit
git bisect reset # when done
Isolate by layer:
- Is it frontend or backend? (Check network tab)
- Is it the database? (Query directly)
- Is it the API? (curl the endpoint)
- Is it the component? (Render it in isolation)
3. Hypothesize
Form a specific, testable hypothesis:
- "The bug is caused by X because Y"
- Not "something is wrong with the data"
- Good: "The
userId is null because the auth middleware doesn't run on this route"
4. Test the Hypothesis
Write the smallest possible test that proves/disproves your hypothesis:
- Add a
console.log or breakpoint at the suspected location
- Check the value of the suspected variable
- If your hypothesis is wrong, go back to step 3 with new information
- If it's right, you've found the bug
5. Fix and Verify
- Apply the minimal fix
- Verify the original reproduction steps no longer trigger the bug
- Check for regressions — did the fix break anything else?
- Write a test that would have caught this bug
Debugging Tools
| Scenario |
Tool |
| "It worked before" |
git bisect |
| "I don't know where this runs" |
Add logging at entry/exit of suspect functions |
| "The data looks wrong" |
Inspect at each transformation step |
| "It only fails in production" |
Compare env vars, check logs, try to reproduce with prod data locally |
| "It's intermittent" |
Look for race conditions, timing issues, or uninitialized state |
| "The error message is useless" |
Search the codebase for where that error is thrown |
Common Bug Patterns
- Off-by-one: Array indices, pagination, date ranges
- Null/undefined: Missing optional chaining, uninitialized state
- Race condition: Async operations completing in unexpected order
- Stale closure: React useEffect/useCallback capturing old values
- Type coercion:
== vs ===, string vs number comparisons
- Missing await: Forgetting
await on async functions
- Environment mismatch: Works locally, fails in CI/prod due to different env vars or versions
Rules
- Never guess — always verify with evidence
- Fix the root cause, not the symptom
- If you've spent 15 minutes without progress, step back and re-isolate
- Document what you tried so you don't repeat failed approaches
1---2name: systematic-debugging3description: Structured debugging methodology — reproduce, isolate, hypothesize, verify. Covers git bisect, binary search, logging, and minimal reproduction.4---5
6# Systematic Debugging
7
8Debug methodically instead of randomly changing code.
9
10## The Process
11
12### 1. Reproduce
13
14Before anything else, reproduce the bug reliably:
15- Get the exact steps to trigger the issue
16- Note the expected vs actual behavior
17- Confirm it happens consistently (not intermittent)
18- Record the environment (OS, Node version, browser, etc.)
19
20If you can't reproduce it, you can't fix it. Ask for more details.
21
22### 2. Isolate
23
24Narrow down where the bug lives:
25
26**Binary search the codebase:**
27- Comment out half the system → does the bug persist?
28- If yes, the bug is in the remaining half → repeat
29- If no, the bug is in the commented-out half → repeat
30
31**Git bisect:**
32```bash
33git bisect start
34git bisect bad # current commit is broken
35git bisect good <sha> # this commit was working
36# Git checks out the midpoint — test it
37git bisect good # or git bisect bad
38# Repeat until it finds the first bad commit
39git bisect reset # when done
40```
41
42**Isolate by layer:**
43- Is it frontend or backend? (Check network tab)
44- Is it the database? (Query directly)
45- Is it the API? (curl the endpoint)
46- Is it the component? (Render it in isolation)
47
48### 3. Hypothesize
49
50Form a specific, testable hypothesis:
51- "The bug is caused by X because Y"
52- Not "something is wrong with the data"
53- Good: "The `userId` is null because the auth middleware doesn't run on this route"
54
55### 4. Test the Hypothesis
56
57Write the smallest possible test that proves/disproves your hypothesis:
58- Add a `console.log` or breakpoint at the suspected location
59- Check the value of the suspected variable
60- If your hypothesis is wrong, go back to step 3 with new information
61- If it's right, you've found the bug
62
63### 5. Fix and Verify
64
65- Apply the minimal fix
66- Verify the original reproduction steps no longer trigger the bug
67- Check for regressions — did the fix break anything else?
68- Write a test that would have caught this bug
69
70## Debugging Tools
71
72| Scenario | Tool |
73|----------|------|
74| "It worked before" | `git bisect` |
75| "I don't know where this runs" | Add logging at entry/exit of suspect functions |
76| "The data looks wrong" | Inspect at each transformation step |
77| "It only fails in production" | Compare env vars, check logs, try to reproduce with prod data locally |
78| "It's intermittent" | Look for race conditions, timing issues, or uninitialized state |
79| "The error message is useless" | Search the codebase for where that error is thrown |
80
81## Common Bug Patterns
82
83- **Off-by-one**: Array indices, pagination, date ranges
84- **Null/undefined**: Missing optional chaining, uninitialized state
85- **Race condition**: Async operations completing in unexpected order
86- **Stale closure**: React useEffect/useCallback capturing old values
87- **Type coercion**: `==` vs `===`, string vs number comparisons
88- **Missing await**: Forgetting `await` on async functions
89- **Environment mismatch**: Works locally, fails in CI/prod due to different env vars or versions
90
91## Rules
92
93- Never guess — always verify with evidence
94- Fix the root cause, not the symptom
95- If you've spent 15 minutes without progress, step back and re-isolate
96- Document what you tried so you don't repeat failed approaches