Tier 4

debugging

Systematic procedure for diagnosing and fixing software bugs using Claude Code's tool capabilities

Usage in Claude Code: /debugging your question here

Debugging

Input: $ARGUMENTS


Overview

Systematic procedure for diagnosing and fixing software bugs. This procedure leverages Claude Code’s unique capabilities: exhaustive code search, pattern matching across codebase, and direct code execution.


Step 0: Context Assessment

FactorValueNotes
UrgencyCRITICAL / HIGH / NORMALProduction down vs development issue
ReproducibilityCONSISTENT / INTERMITTENT / RAREAffects isolation strategy
Codebase familiarityHIGH / LOWAffects search vs read approach

If CRITICAL + CONSISTENT: Skip to Step 3 (isolate quickly) If INTERMITTENT: Extra logging in Step 2


Step 1: Gather Bug Information

Collect all available information about the bug.

What to gather:

  1. Error messages and stack traces (exact text)
  2. Expected vs actual behavior
  3. Environment details (versions, configs)
  4. When the bug started (recent changes?)
  5. Conditions that trigger the bug

LLM Execution:

# Read error logs if path known
→ Use Read tool on log files

# Search for error message in codebase
→ Use Grep: pattern="[error message text]"

# Check recent commits for relevant changes
→ Use Bash: git log --oneline -20 -- [relevant_path]

# Find configuration files
→ Use Glob: pattern="**/config*.{json,yaml,yml,toml}"

Output format:

BUG SUMMARY
===========
Error: [exact error message]
Expected: [what should happen]
Actual: [what happens instead]
Environment: [relevant versions/config]
First occurrence: [when/what changed]
Trigger conditions: [how to cause bug]

Step 2: Reproduce the Bug

Achieve consistent reproduction of the bug.

Strategy:

  1. Start with exact reproduction steps from report
  2. Simplify to minimal reproduction case
  3. Document what IS and ISN’T required to trigger

LLM Execution:

# Run the failing test/command
→ Use Bash: [command that triggers bug]

# If no command provided, search for tests
→ Use Grep: pattern="test.*[function_name]" type="py" (or relevant type)

# Run specific test
→ Use Bash: pytest [test_path] -v

# If intermittent, add logging
→ Use Edit: Add print/log statements at suspected points
→ Use Bash: Run multiple times, collect logs

Output format:

REPRODUCTION
============
Command: [exact command]
Expected output: [what should appear]
Actual output: [what actually appears]
Reproducible: [YES consistently / YES intermittently / NO]
Minimal case: [simplest trigger]

If cannot reproduce:

  • Request additional environment details from user
  • Add logging and ask user to trigger again
  • Document as “reproduction blocked” and note what’s needed

Step 3: Isolate the Bug Location

Narrow down where the bug occurs.

Strategies (use in order of efficiency):

  1. Stack trace analysis: If stack trace exists, start there
  2. Error message search: Find where error is raised
  3. Binary search: Comment out half the code path
  4. Git bisect: Find introducing commit
  5. Input/output tracing: Check values at boundaries

LLM Execution:

# Search for error message origin
→ Use Grep: pattern="[error text or exception type]" output_mode="content"

# Find function definition from stack trace
→ Use Grep: pattern="def [function_name]" output_mode="content"

# Read the suspected file
→ Use Read: [file_path]

# Check git history for suspicious changes
→ Use Bash: git log -p --follow -S "[suspicious_string]" -- [file_path]

# Binary search via commenting (if needed)
→ Use Edit: Comment out suspected section
→ Use Bash: Run test
→ Observe if error persists
→ Use Edit: Restore or narrow down

LLM Alternative to Interactive Debugger: Instead of breakpoints, use strategic print statements:

# Add diagnostic print statement
→ Use Edit: Add print(f"DEBUG: {variable}={variable!r}") at suspected points

# Run and capture output
→ Use Bash: [command] 2>&1

# Read output
→ Analyze printed values

# Remove debug statements after fix
→ Use Edit: Remove DEBUG prints

Output format:

ISOLATION
=========
Bug location: [file:line]
Function: [function_name]
Confidence: [HIGH/MED/LOW]
Evidence: [what indicates this location]

Step 4: Analyze Root Cause

Understand WHY the bug occurs, not just where.

Analysis approach:

  1. Read code at bug location carefully
  2. Trace data flow into the location
  3. Apply Five Whys technique
  4. Categorize the bug type
  5. Check assumptions the code makes

Bug type categories:

TypeExamples
Logic errorWrong condition, off-by-one, wrong operator
State errorRace condition, stale state, mutation side effect
Type errorWrong type, null/undefined, type coercion
Boundary errorEdge case, empty input, large input
Integration errorAPI mismatch, version incompatibility
Environment errorMissing config, wrong path, permissions

LLM Execution:

# Read code at identified location
→ Use Read: [file_path]

# Trace where inputs come from
→ Use Grep: pattern="[variable_name]\s*=" to find assignments

# Check related code for patterns
→ Use Grep: pattern="[similar_pattern]" to find analogous code

# Review data structures
→ Use Grep: pattern="class [ClassName]|def [function]" to understand types

Output format:

ROOT CAUSE ANALYSIS
===================
Root cause: [specific cause]
Bug type: [from categories above]
Why it causes the error: [explanation]
Why it wasn't caught: [testing gap, edge case, etc.]

Step 5: Design the Fix

Plan the fix before implementing.

Design considerations:

  1. Simplest fix that addresses root cause
  2. Side effects on other code
  3. Regression risk
  4. Test requirements

Fix approach options:

ApproachWhen to use
Direct fixClear bug, isolated location
Defensive fixAdd validation/guards
Refactor fixBug reveals design problem
WorkaroundQuick fix now, proper fix later

LLM Execution:

# Check for similar patterns that might have same bug
→ Use Grep: pattern="[buggy_pattern]" to find similar code

# Understand dependencies of code being modified
→ Use Grep: pattern="import.*[module]|from [module]" to find usages

# Check test coverage for area
→ Use Grep: pattern="test.*[function_name]" type="py"

Output format:

FIX DESIGN
==========
Approach: [direct/defensive/refactor/workaround]
Change summary: [what will change]
Files affected: [list]
Potential side effects: [list or "none identified"]
Test plan: [how to verify fix]

Step 6: Implement and Test the Fix

Apply the fix and verify it works.

Implementation checklist:

  1. Make minimal code change
  2. Follow existing code style
  3. Add explanatory comments if fix is non-obvious
  4. Write test that would have caught the bug
  5. Verify original bug is fixed
  6. Run existing tests for regressions

LLM Execution:

# Apply the fix
→ Use Edit: [specific change]

# Run the previously failing test
→ Use Bash: [test command]
→ Verify: Previously failing now passes

# Run broader test suite
→ Use Bash: pytest [test_directory] or equivalent
→ Verify: No regressions

# If test doesn't exist, create one
→ Use Edit: Add test in appropriate test file

# Run new test
→ Use Bash: [test command for new test]

Output format:

FIX IMPLEMENTATION
==================
Change made: [description]
File(s) modified: [list]

Test results:
- Original bug: [FIXED/STILL PRESENT]
- Regression tests: [PASS/FAIL with details]
- New test added: [YES/NO, test name if yes]

Step 7: Document and Prevent

Complete the debugging cycle.

Documentation:

  1. Clear commit message explaining fix
  2. Update relevant docs if behavior changed
  3. Note any related code that might need checking
  4. Recommend process improvements

LLM Execution:

# Prepare commit message
# Format: "[type]: Brief summary\n\nRoot cause: ...\nFix: ..."

# Check for similar code that might have same issue
→ Use Grep: pattern="[original_buggy_pattern]"
→ Report: Other locations that may need review

Output format:

DOCUMENTATION
=============
Commit message: [suggested message]
Related code to check: [list or "none"]
Process improvement: [suggestion or "none"]

When to Use

  • Bug report received from users or QA
  • Unexpected system behavior observed
  • Test failures that need investigation
  • Production incidents requiring diagnosis
  • Code behaving differently than expected
  • Performance degradation (with adaptation)
  • Intermittent failures need root cause

Verification Criteria

StepVerification
Step 1Bug info complete (error, expected, actual, conditions)
Step 2Bug reproducible OR documented as blocked
Step 3Location isolated to specific file:line
Step 4Root cause identified with category
Step 5Fix designed with test plan
Step 6Tests pass (original fixed, no regressions)
Step 7Commit message prepared

Overall verification:

  • Bug is reproducible before fix
  • Bug is not reproducible after fix
  • Root cause is clearly documented
  • Fix addresses root cause (not symptoms)
  • Regression test exists for this bug
  • All existing tests pass
  • Similar code checked for same issue

Integration Points

  • Often invoked from: /procedure_engine (problem input), /code_review (bug discovered during review)
  • Routes to: /code_review (fix ready for review), /testing_strategy (if test gaps identified)
  • Related: /root_cause_5_whys, /root_cause_analysis

LLM Debugging Superpowers

Unlike human debuggers, LLM can leverage unique capabilities:

LLM can:
- Search entire codebase for error message origin instantly
  → Use Grep: pattern="[exact_error_text]"
- Find ALL usages of a function/variable
  → Use Grep: pattern="[function_name]\("
- Trace ALL import chains
  → Use Grep: pattern="from.*[module]|import [module]"
- Find similar patterns that might have same bug
  → Use Grep: pattern="[buggy_pattern]"

Parallel Investigation

LLM can:
- Read multiple suspicious files simultaneously
  → Use Read on all candidate files in parallel
- Run multiple test subsets to isolate
  → Use Bash: pytest [file1] && Use Bash: pytest [file2]
- Check multiple hypotheses at once
  → Add debug prints at multiple locations

Pattern Matching at Scale

LLM can:
- Recognize bug patterns from training (millions of bugs seen)
- Cross-reference against known anti-patterns
- Suggest fixes based on similar historical bugs

What LLM Cannot Do (Plan Around These)

LimitationWorkaround
Interactive debugger (breakpoints)Use print statements via Edit + Bash
Watch memory in real-timeAdd memory logging, read logs
Profile performance interactivelyAdd timing code, run profiler via Bash
Step through executionStrategic prints at each step

Common Debugging Patterns (LLM Reference)

Logic Errors

PatternSymptomsLikely CauseFirst CheckLLM Detection
Off-by-oneWorks for most cases, fails at boundariesLoop bounds, array indexing< vs <=, array lengthGrep: `range(.*-1
Wrong operatorIncorrect results, no crash= vs ==, and vs orConditionals near bugGrep: if.*=.*: (assignment in if)
Incorrect orderPartial success, some operations failInitialization order, dependenciesTrace execution orderRead code flow
Missing caseWorks except for edge casesIncomplete switch/if logicEnumerate all casesCount case handling

State Errors

PatternSymptomsLikely CauseFirst CheckLLM Detection
Null/NoneTypeError, AttributeErrorMissing initialization, failed lookupWhere value is assignedGrep: `= None
Stale stateWorks once, fails on repeatMutation, cachingState initializationGrep: `cache
Race conditionIntermittent, timing-dependentShared state, async operationsConcurrent access pointsGrep: `async
Unintended mutationCorrect then wrongIn-place modificationTrace object identityGrep: `.append(

Type Errors

PatternSymptomsLikely CauseFirst CheckLLM Detection
Type coercionUnexpected values, silent failuresDynamic typing, implicit conversionType at assignmentGrep: `int(
Wrong typeTypeError, AttributeErrorExpected X got YTrace input typesAdd: print(type(x))
SerializationData loss, corruptionObject not serializableCheck serialization boundaryGrep: `json.dumps

Async/Concurrency Errors

PatternSymptomsLikely CauseFirst CheckLLM Detection
Forgotten awaitFunction returns coroutine, not resultMissing awaitCheck all async callsGrep: async def without matching await
DeadlockHangs foreverCircular lock dependencyTrace lock acquisitionGrep: `acquire(
Lost exceptionSilent failure in asyncException in task not awaitedCheck task handlingGrep: `create_task

Environment Errors

PatternSymptomsLikely CauseFirst CheckLLM Detection
Missing configWorks locally, fails deployedEnvironment variable unsetCheck all env varsGrep: `os.environ
Path issuesFile not found, import failsWrong working directoryPrint cwd and pathsBash: pwd; ls
Version mismatchWorks in dev, fails in prodDependency version differsCheck requirementsBash: `pip freeze
Permission errorAccess deniedFile/network permissionsCheck actual permissionsBash: ls -la [path]

Integration Errors

PatternSymptomsLikely CauseFirst CheckLLM Detection
API mismatchWrong data format, 400 errorsAPI changed, spec wrongCheck actual vs expectedRead API response
TimeoutSlow then failsNetwork, slow dependencyAdd timingBash: time [command]
Retry exhaustedWorks sometimesTransient failuresCheck retry logicGrep: `retry