Ai debugging

Systematic debugging approaches — scientific method applied to code, structured techniques, common bug categories, git bisect, time-boxing, prevention strategies, and anti-patterns. Use when investigating bugs, diagnosing failures, or establishing debugging practices.

install
source · Clone the upstream repo
git clone https://github.com/wpank/ai
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/wpank/ai "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/testing/debugging" ~/.claude/skills/wpank-ai-debugging && rm -rf "$T"
manifest: skills/testing/debugging/SKILL.md
source content

Debugging Methodology

A systematic, scientific approach to finding and fixing bugs — replacing guesswork with method.

"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian Kernighan

Installation

OpenClaw / Moltbot / Clawbot

npx clawhub@latest install debugging

Debugging Philosophy

Debugging is not guessing. It is applied science.

  1. Observe — gather evidence: error messages, logs, stack traces, screenshots, reproduction steps
  2. Hypothesize — form a specific, testable explanation for the behavior
  3. Test — design an experiment that confirms or disproves the hypothesis
  4. Conclude — accept or reject the hypothesis based on evidence, then iterate

Every debugging session should follow this loop. If you catch yourself making random changes and re-running, stop — you've left the scientific method.


Systematic Methods

Choose the method that matches the situation:

MethodWhen to UseHow It Works
Binary searchLarge codebase, unknown originBisect code or commits to narrow location by half each step
Hypothesis testingKnown symptoms, multiple possible causesForm specific hypothesis, design targeted test, verify or refute
Minimal reproductionComplex bugs, intermittent failuresStrip away code, config, and data until smallest case reproduces
Trace analysisFlow-based bugs, wrong outputFollow data or execution path step by step from input to output
Rubber duckStuck, unclear thinking, no progressExplain the problem aloud — forces clarity and often reveals the answer
Divide and conquerMulti-component issues, integration bugsIsolate each component, test independently, find which misbehaves

Combining Methods

Most real bugs require combining methods. A typical pattern:

  1. Start with trace analysis to understand the flow
  2. Use hypothesis testing to narrow the cause
  3. Apply binary search (on code or commits) to pinpoint the location
  4. Build a minimal reproduction to confirm and share

Debugging Workflow

Follow these six steps in order. Do not skip ahead.

Step 1: Reproduce

Can you make the bug happen reliably? Write down the exact steps.

  • What input triggers it?
  • What environment (OS, browser, Node version)?
  • Is it deterministic or intermittent?
  • What is the expected behavior vs actual behavior?

If you cannot reproduce it, you cannot confirm you have fixed it.

Step 2: Isolate

Narrow the scope. Which component, file, function, or line?

  • Use binary search: comment out half the code, does it still fail?
  • Use divide and conquer: test each component independently
  • Check recent changes:
    git log --oneline -20
    and
    git diff

Step 3: Diagnose

Understand the root cause, not just the symptom.

  • Ask "why?" repeatedly until you reach the actual defect
  • Distinguish between the symptom (what you see) and the cause (why it happens)
  • Verify your diagnosis by predicting what will happen if you change a specific thing

Step 4: Fix

Make the smallest change that addresses the root cause.

  • Fix the cause, not the symptom
  • Avoid side effects — don't "fix" by restructuring unrelated code
  • If the fix is complex, break it into smaller, verifiable steps

Step 5: Verify

Confirm the fix resolves the original reproduction case.

  • Run the exact reproduction steps from Step 1
  • Check for regressions — did the fix break anything else?
  • Test edge cases related to the bug

Step 6: Prevent

Add safeguards so this class of bug cannot recur.

  • Write a regression test that reproduces the original bug
  • Improve types, add assertions, or add validation
  • Update monitoring or alerts if the bug was caught late
  • Document the fix if the root cause was non-obvious

Debugging Toolkit

ToolPurposeWhen to Reach for It
DebuggerSet breakpoints, step through execution, inspect stateLogic errors, unexpected control flow
LoggingAdd strategic log statements at key pointsFlow tracing, production issues, async bugs
ProfilerMeasure CPU, memory, and timingPerformance regressions, slow endpoints
Network inspectorInspect HTTP requests, responses, headers, timingAPI integration bugs, auth failures, CORS
Memory analyzerHeap snapshots, allocation tracking, leak detectionMemory leaks, growing memory usage

Effective Logging

  • Include the module and function name as a prefix:
    [OrderService.processPayment]
  • Log inputs on entry and outputs/status on exit
  • Use structured data (objects), not string concatenation
  • Remove debug logging before committing (or use a debug log level)

Common Bug Categories

Recognize the pattern to find the fix faster:

CategoryTypical SymptomsFirst Things to Check
Null reference"Cannot read property of undefined/null"Input validation, optional chaining, API response shape
Off-by-oneMissing first/last item, extra iteration, boundary failureLoop bounds, array indices, fence-post conditions
Race conditionIntermittent failures, order-dependent, works in debuggerShared mutable state, missing locks/awaits, event ordering
Memory leakGrowing memory over time, eventual OOM or slowdownEvent listeners not removed, closures holding references, cache without eviction
State managementUI out of sync, stale data, phantom updatesMutation vs immutability, missing re-renders, stale closures
EncodingGarbled text, wrong characters, hash mismatchesUTF-8 vs Latin-1, URL encoding, base64 padding
TimezoneTimes off by hours, wrong dates near midnightUTC vs local, DST transitions, serialization format
Async orderingOperations complete in unexpected orderMissing await, unhandled promise, callback timing
ConfigurationWorks locally, fails in staging/productionEnvironment variables, feature flags, config file differences
Dependency versionBroke after update, works with old versionLock file changes, breaking API changes, peer dependency conflicts

Git Bisect Guide

When you know a bug was introduced between two commits,

git bisect
finds the exact breaking commit using binary search.

# 1. Start bisect
git bisect start

# 2. Mark the current (broken) commit as bad
git bisect bad

# 3. Mark a known good commit
git bisect good abc1234

# 4. Git checks out a middle commit — test it, then tell git:
git bisect good   # if this commit works fine
git bisect bad    # if this commit has the bug

# 5. Repeat until git identifies the first bad commit
#    Output: "<commit-hash> is the first bad commit"

# 6. Examine the breaking commit
git show <commit-hash>

# 7. End the bisect session
git bisect reset

Automated Bisect

If you have a test script that exits 0 for good and non-zero for bad:

git bisect start
git bisect bad HEAD
git bisect good abc1234
git bisect run ./test-for-bug.sh

Git runs the script at each step automatically and reports the first bad commit.


Questions to Ask When Stuck

Use this checklist when you've been stuck for more than 15 minutes. Answer each question explicitly:

  • What changed recently? — new deploy, dependency update, config change, data migration?
  • Can I reproduce it? — reliably, intermittently, or not at all?
  • What are the exact error conditions? — specific input, user, environment, time of day?
  • What does the data look like? — inspect actual values, not what you assume they are
  • What do the logs say? — read them carefully, including timestamps and context
  • Does it happen in other environments? — local, staging, production, different OS/browser?
  • What is the expected behavior? — can I articulate exactly what should happen?
  • Have I seen this pattern before? — check the common bug categories table above
  • Am I looking at the right layer? — frontend vs backend, app vs infrastructure, code vs config?
  • What assumptions am I making? — list them explicitly, then verify each one

Time-Boxing

If you've spent more than 30 minutes without progress: step back and re-read the error from scratch, explain the problem aloud (rubber duck method), take a 10-minute break, try a completely different hypothesis, ask for help, or reduce scope by building the smallest reproduction from scratch.


Prevention

After fixing a bug, invest time to prevent its recurrence:

ActionHow It Helps
Add a regression testEnsures this exact bug cannot return undetected
Improve typesCatches null, undefined, and shape mismatches at compile time
Add runtime assertionsFails fast with a clear message instead of silent corruption
Document the fixHelps future developers understand the "why" behind the code
Review related codeThe same mistake pattern may exist in similar locations
Update monitoringAdd alerts or dashboards so the symptom is caught earlier next time

Anti-Patterns

Behaviors that waste time and make bugs harder to find:

Anti-PatternWhy It FailsDo This Instead
Random changesIntroduces new bugs, obscures original causeFollow the scientific method — one variable at a time
Fixing symptomsThe root cause remains and will resurfaceAsk "why?" until you reach the actual defect
Debugging in productionRisk to users, limited tooling, high stressReproduce locally or in staging first
Not reproducing firstYou cannot confirm a fix for a bug you cannot triggerInvest the time to build a reliable reproduction
Ignoring error messagesThe answer is often in the message you skippedRead the full error, including stack trace and context
Assuming your code is correctConfirmation bias hides obvious mistakesRe-read your code as if someone else wrote it
Changing multiple things at onceYou cannot tell which change fixed (or broke) itMake one change, test, then move to the next
Debugging while fatiguedTired debugging creates more bugs than it fixesTake a break, come back with fresh eyes

NEVER Do

  1. NEVER push a fix you cannot explain — if you don't know why it works, it doesn't work
  2. NEVER debug without version control — always be able to revert to a known-good state
  3. NEVER ignore a failing test — a skipped test is a hidden bug waiting to resurface
  4. NEVER assume the bug is in someone else's code first — check your own code before blaming libraries or frameworks
  5. NEVER debug while fatigued — tired debugging creates more bugs than it fixes
  6. NEVER delete error handling to "simplify" — error handling is where bugs reveal themselves
  7. NEVER skip the reproduction step — a fix without a reproduction is a guess, not a solution
  8. NEVER make random changes hoping something works — follow the scientific method; one variable at a time