| name | debugging-mastery |
| description | Systematic debugging methodology covering root cause analysis, log-based debugging, production debugging, memory leak diagnosis, deadlock detection, performance anomaly investigation, and reproducible bug reproduction. Distilled from real experience at Google, DeepMind, ByteDance, and Huawei.
USE WHEN: investigating production incidents, debugging hard-to-reproduce bugs, analyzing crash dumps, finding race conditions, diagnosing memory leaks, debugging performance regressions, or any situation requiring systematic root cause analysis. Triggers on "debugging", "root cause", "bug", "crash", "segfault", "deadlock", "race condition", "memory leak".
|
Debugging Mastery
Source: "Debugging" (David Agans) + Google/DeepMind/ByteDance production
debugging experience + years of midnight production incidents
Core Philosophy: "Debugging is the art of systematically testing hypotheses
until the root cause is found. It is NOT randomly changing things hoping."
The Nine Indispensable Rules
From David Agans' "Debugging" โ the debugging bible:
1. Understand the system
2. Make it fail
3. Quit thinking and look
4. Divide and conquer
5. Change one thing at a time
6. Keep an audit trail
7. Check the plug
8. Get a fresh view
9. If you didn't fix it, it ain't fixed
1. The Debugging Workflow
1.1 Systematic RCA Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 1: REPRODUCE โ
โ Can you make it happen? โ
โ If not: this is the first problem to solve โ
โ โ
โ Step 2: ISOLATE โ
โ Binary search through the system to find the component โ
โ Remove variables: isolate the MINIMAL reproducing case โ
โ โ
โ Step 3: MEASURE โ
โ Log everything. Add MORE logging if needed. โ
โ "The resolution of your debugging is limited by the โ
โ resolution of your instrumentation" โ
โ โ
โ Step 4: HYPOTHESIZE โ
โ Form a specific, testable hypothesis โ
โ Bad: "Maybe there's a memory issue" โ
โ Good: "The cache eviction runs on the wrong goroutine" โ
โ โ
โ Step 5: TEST THE HYPOTHESIS โ
โ If hypothesis is wrong โ return to Step 3 โ
โ If hypothesis is right โ FIX it โ
โ โ
โ Step 6: VERIFY THE FIX โ
โ Run the reproduction case again (it should pass) โ
โ Run the full test suite โ
โ Add the reproduction case as a REGRESSION TEST โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
1.2 The Binary Search Debugging Technique
Step through the system components:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Input โ [A] โ [B] โ [C] โ [D] โ [E] โ Output โ
โ โ
โ Check output at C: โ
โ โ
C passes โ bug is in D or E โ
โ โ C fails โ bug is in A, B, or C โ
โ โ
โ Repeat: check at B (or D, depending on result) โ
โ Continue until you've isolated the single component โ
โ โ
โ This is O(log n) โ exponentially faster than linear scan โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
2. Log-Based Debugging
2.1 Structured Logging for Debuggability
console.log('Order processed');
console.log(`Error: ${err}`);
logger.info('order.processed', {
orderId: order.id,
userId: order.userId,
duration: Date.now() - start,
items: order.items.length,
total: order.total,
});
logger.error('order.processing_failed', {
orderId,
errorCode: err.code,
errorMessage: err.message,
stackTrace: err.stack,
currentState: { status, step, retryCount },
});
2.2 Log Levels for Debugging
โโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ TRACE โ Every function entry/exit (noisy, turn off by โ
โ โ default) โ
โโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ DEBUG โ Detailed state dumps, intermediate values โ
โ โ Enable when investigating specific issue โ
โโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ INFO โ Normal operations, key business events โ
โ โ "Order created", "Payment confirmed" โ
โโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ WARN โ Anomalous but non-critical โ
โ โ "Retry attempt 2/3", "Cache miss" โ
โโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ERROR โ Something is broken โ needs investigation โ
โ โ "Database connection failed", "Timeout exceeded" โ
โโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
2.3 Finding the Needle in the Haystack
journalctl -u your-service --since "1 hour ago" | grep ERROR
tail -f /var/log/app.log | grep "order-123"
cat app.log | grep "trace_id=abc123" | jq .
cat app.log | grep "order.processed" | awk '{print $2}' | sort | uniq -c
grep ERROR app.log | grep -oP '"errorCode":"[^"]*"' | sort | uniq -c | sort -rn
cat app.log | jq 'select(.duration > 1000)' | jq -r '.message + ": " + (.duration|tostring)'
3. Production Debugging
3.1 The Production Debugging Checklist
โก Is it happening NOW?
โ Check monitoring dashboard (latency, error rate, saturation)
โ Check alerting for related incidents
โ Check recent deployments/configuration changes
โก Is it a known issue?
โ Search internal KB / runbooks / postmortems
โ Search Slack history for similar symptoms
โ Check GitHub issues / bug tracker
โก Can we observe the problem?
โ Check logs (see section 2)
โ Check metrics (CPU, memory, disk, network, GC)
โ Check distributed traces (Jaeger/Tempo)
โก Can we reproduce it in staging?
โ Same deployment version
โ Same data or data pattern
โ Same traffic pattern
โ If not reproducible โ add more instrumentation
3.2 Safe Production Debugging
curl -s http://service:8080/health | jq .
curl -s http://service:8080/metrics | head -50
curl -s http://service:8080/debug/vars
kubectl exec -it pod/app -- /bin/sh -c "curl localhost:8080/debug/pprof/heap"
3.3 Postmortem-Driven Debugging
When you encounter a bug, ask:
โ "What type of bug is this?"
- Logic bug (wrong condition, missing case)
- Concurrency bug (race, deadlock, stale data)
- Data bug (corruption, encoding, validation)
- Configuration bug (wrong env, wrong feature flag)
- Dependency bug (upstream change, API drift)
- Resource bug (memory, file handles, connections)
โ "Does this bug belong to a known category?"
If yes โ apply known pattern fix
If no โ write a new postmortem entry about this pattern
4. Debugging by Bug Type
4.1 Concurrency / Race Conditions
go test -race ./...
go run -race ./...
node --async-stack-traces app.js
Promise.config({
warnings: true,
longStackTraces: true
});
4.2 Memory Leaks
Symptoms: RAM grows over time, GC overhead increases, OOM crashes
Debug flow:
1. Take a heap snapshot (time 0)
2. Run operation n times
3. Take another heap snapshot
4. Compare: what grows?
- Growing maps (map without cleanup) โ common
- Growing slices (append without limit) โ common
- Event listeners without removal
- Closed-over variables in callbacks
- Cached objects with no eviction
4.3 Deadlocks
go test -v -timeout=5s ./...
go test -race ./...
import "net/http/pprof"
func main() {
}
curl http:
4.4 Heisenbugs (Bugs that Disappear When You Look)
Symptoms:
- Adding a log line "fixes" the bug
- Debugger breakpoints "fix" the bug
- Bug only happens in production, never staging
Causes:
- Timing-dependent bugs (race, channel, timeout)
- Buffer flush / log delay timing
- Heisenberg uncertainty principle of debugging:
"The act of observing changes the behavior"
Debug strategies:
1. Use structured logging (less I/O impact than console.log)
2. Use tcpdump / strace (observe without modifying)
3. Add counters instead of log lines:
metrics.counter('bug_scenario.hit').inc()
4. Capture state, don't log it:
Take periodic snapshots โ analyze offline
4.5 Non-Deterministic Bugs
Symptoms:
- "Sometimes it works, sometimes it doesn't"
- "I can't reproduce it"
- "It only happens on Tuesdays"
The 5 most common causes of non-determinism:
1. Uninitialized memory โ read before write
2. Map iteration order (random in many languages)
3. Goroutine/thread scheduling order
4. Network timing / retry interactions
5. Hash collision / random seed
5. Tools Arsenal
5.1 Quick Reference by Problem
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Problem โ Tool โ
โโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ CPU spike โ pprof, perf top, top -H โ
โ Memory leak โ pprof heap, heap dump โ
โ Deadlock โ goroutine stack dump, lsof โ
โ Race condition โ race detector, tsan โ
โ Slow DB query โ EXPLAIN ANALYZE, pg_stat_activityโ
โ High GC โ gc tracer, allocation profilerโ
โ Network issue โ tcpdump, strace, ss, iperf โ
โ Disk I/O โ iostat, iotop, fio โ
โ File handle leak โ lsof -p PID, /proc/PID/fd โ
โ Config wrong โ diff config files, env vars โ
โ SSL/TLS โ openssl s_client, ssllabs โ
โโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
5.2 Universal Debugging Commands
top -H -p <PID>
strace -p <PID> -e trace=network
lsof -p <PID>
ls /proc/<PID>/fd/ | wc -l
ss -tulpn
tcpdump -i eth0 port 8080 -w capture.pcap
iostat -x 1
df -h
du -sh /path
dmesg | tail -20
free -m
ulimit -a
6. The Debugging Mindset
6.1 What Great Debuggers Do Differently
As a junior dev, I'd panic and change random things.
As a senior engineer, I use the scientific method.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Junior Engineer: โ
โ "Oh no! The database is failing! Let me restart it!" โ
โ โ Restarts the DB โ "It works now!" โ
โ โ Same bug happens tomorrow โ
โ โ
โ Senior Engineer: โ
โ "The database is failing. Let me check the logs." โ
โ โ Finds "disk space 100% full" โ
โ โ Cleans up old data, sets up disk alert โ
โ โ Bug never comes back โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
6.2 The Two-Day Rule
If you can't fix a bug after 2 hours of focused debugging:
1. Step away from the keyboard (5-10 minutes)
โ Fresh perspective is the #1 debugging tool
2. Explain the bug to someone else (rubber duck debugging)
โ Saying it out loud forces clarity
3. Write down what you KNOW vs what you ASSUME
โ Most stuck bugs come from a wrong assumption
4. Ask yourself:
"What would have to be TRUE for this bug to reproduce?"
"What evidence DISPROVES my current hypothesis?"
5. If still stuck after 2 more hours:
โ Escalate or pair with someone who hasn't seen the issue
6.3 The Rubber Duck Debugging
def rubber_duck_debug(code_bug):
"""
Explain the code line by line to a rubber duck.
The duck doesn't know anything, so you need to
be precise enough that a complete beginner would
understand.
90% of the time, you find the bug mid-explanation.
"""
while not bug_found:
for line in code_bug:
explain_out_loud(line)
7. Debugging Anti-Patterns
โ "Let me just try restarting it"
โ You learned nothing. Same bug will return.
โ "Let me change this randomly and see if it helps"
โ If it "fixes" the bug, you still don't know WHY.
โ The "fix" might have introduced a different, worse bug.
โ "I checked the code, it looks fine"
โ The code is NOT fine โ the bug proves it.
โ The bug is in the gap between "what you think the code does"
and "what the code actually does."
โ "It must be a compiler/interpreter bug"
โ It's NEVER the compiler. (Google's postmortem: zero compiler bugs)
โ The compiler is better tested than your code.
โ "This worked yesterday, nothing changed!"
โ Something ALWAYS changed. Find it.
โ Deployment, config, data, traffic pattern, time of day.
References
references/debugging-recipes.md โ Language-specific debugging recipes (Go/Node/Python/Rust)
references/logging-standards.md โ Structured logging patterns for debuggability
references/post-deploy-checks.md โ What to check immediately after a deployment