code:debug
Code Debug
Systematic debugging workflow to find and fix bugs efficiently. Never guess — always narrow down the problem through systematic elimination.
The Debugging Mindset
| Bad | Good |
|---|---|
| "I think it's..." | "Let me verify..." |
| Changing random things | Binary search narrowing |
| Hoping it'll work | Verifying each hypothesis |
| Moving on after it works | Adding regression test |
The Five-Step Workflow
Step 1: Reproduce
Create the smallest possible reproduction case.
Questions to answer:
- What exactly triggers the bug?
- Can I make it happen on demand?
- What's the minimal input that causes it?
Techniques:
- Extract exact steps from bug report
- Remove unrelated factors
- Create a minimal test case
Output: "I can reproduce the bug by [exact steps]."
Step 2: Isolate
Narrow down where the bug lives using binary search.
Binary search strategies:
- Comment out half the code → does it still fail?
- Isolate by layer: is it the UI, service, or data layer?
- Isolate by file: which file contains the bug?
- Isolate by function: which function causes the issue?
Git bisect (for regression bugs):
git bisect start
git bisect bad # current commit is broken
git bisect good <last-working-commit>
# Run tests, mark good/bad
git bisect reset # done
Output: "The bug is in [specific file/function/component]."
Step 3: Form Hypothesis
Make a testable guess about the root cause.
Good hypothesis:
- Specific: "The user ID is undefined because..."
- Testable: Can verify with a test or log
- Grounded: Based on evidence, not guess
Questions to ask:
- Why does this happen?
- What are the preconditions?
- What's the actual vs expected behavior?
- What changed recently?
Output: "I hypothesize that [root cause] because [evidence]."
Step 4: Fix
Implement the fix based on your hypothesis.
Rules:
- Make minimal changes
- Don't refactor while fixing (separate concerns)
- Write failing test first if possible
- Consider edge cases
Fix patterns:
- Missing value → Add null check or default
- Wrong value → Correct the logic
- Exception → Handle the error case
- Logic error → Fix the condition
Output: "Fix applied: [brief description]."
Step 5: Verify
Confirm the fix works and doesn't break anything.
Verification steps:
- Run the reproduction case → should pass now
- Run related tests → should pass
- Run full test suite → should pass
- Add regression test → prevents future breakage
Output: "Fix verified. Tests passing. Regression test added."
Debugging Techniques
Print Debugging
Quick and dirty, but sometimes the fastest way.
When to use:
- Quick checks in development
- When debugger isn't available
- Understanding flow in unfamiliar code
Best practices:
- Print at entry/exit of functions
- Print variable values at key points
- Remove print statements before committing
- Consider structured logging instead
Debugger
More powerful than print debugging.
When to use:
- Complex state to inspect
- Stepping through logic
- Evaluating expressions in context
See references/language-tools.md for language-specific debugger commands.
Logging
Better than print for production issues.
Strategies:
- Add context: request ID, user ID, correlation IDs
- Log at appropriate levels: DEBUG, INFO, WARN, ERROR
- Don't log sensitive data
- Structured logging (JSON) for easier parsing
See references/techniques.md for logging patterns.
Binary Search
Halving the search space.
Strategies:
- Comment out code blocks
- Test with half the data
- Compare working vs broken configurations
- Git bisect for regressions
Rubber Ducking
Explain the problem out loud (or to a rubber duck).
Process:
- Describe the problem in detail
- Explain what you expect to happen
- Explain what's actually happening
- Often the solution becomes clear mid-explanation
Common Bug Patterns
Null/Undefined Errors
Symptoms: Cannot read property X of undefined
Debug approach:
- Find where the value becomes undefined
- Trace back where it should come from
- Add guard or fix source
Fix patterns:
// Before
const name = user.profile.name;
// After
const name = user?.profile?.name ?? 'Unknown';
// or
if (!user?.profile) return;
const name = user.profile.name;
Race Conditions
Symptoms: Intermittent failures, flaky tests
Debug approach:
- Identify async operations
- Find timing dependencies
- Add delays or force ordering
Fix patterns:
- Use async/await properly
- Add proper synchronization
- Consider immutable state
Logic Errors
Symptoms: Wrong output, wrong behavior
Debug approach:
- Trace through the logic manually
- Compare expected vs actual at each step
- Find the first step that diverges
Fix patterns:
- Fix the condition
- Correct the calculation
- Update the state correctly
Performance Issues
Symptoms: Slow, memory leaks, timeouts
Debug approach:
- Measure first (don't guess)
- Identify hot paths
- Optimize the bottleneck
See code:perf skill for detailed performance debugging.
Skill Loading
Check available skills for additional debugging support:
- If dealing with performance issues → load code:perf
- If security vulnerability suspected → load code:security
- If debugging tests → load oracle:testing
Output Format
After each debugging session, summarize:
## Debug Summary
**Problem:** [One sentence]
**Root Cause:** [What actually was wrong]
**Fix:** [How you fixed it]
**Verification:** [Test results]
**Prevention:** [Regression test added?]
This helps future-you understand what happened.