Purpose
Find the root cause of any bug before attempting a fix. Random fixes waste time and create new bugs. Symptom fixes are failure.
Core principle: ALWAYS find root cause before attempting fixes.
When to Use
Use for ANY technical issue:
- Test failures
- Bugs in production
- Unexpected behavior
- Performance problems
- Build failures
- Integration issues
Use this especially when:
- Under time pressure (emergencies make guessing tempting)
- "Just one quick fix" seems obvious
- You've already tried multiple fixes
- Previous fix didn't work
- You don't fully understand the issue
Do not skip when:
- Issue seems simple (simple bugs have root causes too)
- You're in a hurry (rushing guarantees rework)
- Someone wants it fixed immediately (systematic is faster than thrashing)
Inputs
- Bug description or error message.
- Stack trace or reproduction steps.
- Environment context (OS, versions, recent changes).
- Access to the codebase and test suite.
Workflow
You MUST complete each phase before proceeding to the next.
Phase 1: Root Cause Investigation
Before attempting ANY fix:
Read error messages carefully — Don't skip past errors or warnings. Read stack traces completely. Note line numbers, file paths, error codes.
Reproduce consistently — Can you trigger it reliably? What are the exact steps? If not reproducible: gather more data, don't guess.
Check recent changes — What changed that could cause this? Git diff, recent commits, new dependencies, config changes, environmental differences.
Gather evidence in multi-component systems — When the system has multiple components (CI → build → signing, API → service → database):
For EACH component boundary:
- Log what data enters the component
- Log what data exits the component
- Verify environment/config propagation
- Check state at each layer
Run once to gather evidence showing WHERE it breaks.
Then analyze to identify the failing component.
Then investigate that specific component.
Trace data flow — Where does the bad value originate? What called this with the bad value? Keep tracing up until you find the source. Fix at source, not at symptom. See references/root-cause-tracing.md for the complete backward tracing technique.
Phase 2: Pattern Analysis
- Find working examples — Locate similar working code in the same codebase.
- Compare against references — Read reference implementations completely. Don't skim.
- Identify differences — List every difference between working and broken, however small.
- Understand dependencies — What other components, settings, or config does this need?
Phase 3: Hypothesis and Testing
- Form a single hypothesis — "I think X is the root cause because Y." Write it down. Be specific.
- Test minimally — Make the smallest possible change to test the hypothesis. One variable at a time.
- Verify before continuing — Did it work? Yes → Phase 4. No → form a new hypothesis. Do not add more fixes on top.
- When you don't know — Say so. Ask for help. Research more. Do not pretend to know.
Phase 4: Implementation
Create a failing test case — Simplest possible reproduction. Automated test if possible. Must exist before fixing.
Implement a single fix — Address the root cause identified. One change at a time. No "while I'm here" improvements.
Verify the fix — Test passes? No other tests broken? Issue actually resolved?
If fix doesn't work — STOP. Count how many fixes you've tried.
- If fewer than 3: return to Phase 1, re-analyze with new information.
- If 3 or more: question the architecture (see below).
If 3+ fixes failed: question the architecture — Each fix revealing new shared state or coupling elsewhere is a sign of an architectural problem, not a hypothesis failure. Stop and discuss with the team before attempting another fix.
Output
Root cause: <what caused the bug — specific, not vague>
Evidence: <what you observed that confirmed the hypothesis>
Fix: <what was changed and where>
Test: <test added or updated>
Verification: <how fix was confirmed>
Verification
Failure Modes
Red flags — STOP and return to Phase 1:
- "Quick fix for now, investigate later"
- "Just try changing X and see if it works"
- Adding multiple changes at once
- "Skip the test, I'll manually verify"
- "It's probably X" without evidence
- "I don't fully understand but this might work"
- "Here are the main problems: [lists fixes without investigation]"
- Proposing solutions before tracing data flow
- "One more fix attempt" (when already tried 2+)
- Each fix reveals a new problem in a different place
Common rationalizations:
| Excuse |
Reality |
| "Issue is simple, don't need process" |
Simple issues have root causes too. Process is fast for simple bugs. |
| "Emergency, no time for process" |
Systematic debugging is faster than guess-and-check thrashing. |
| "Just try this first, then investigate" |
First fix sets the pattern. Do it right from the start. |
| "I'll write test after confirming fix works" |
Untested fixes don't stick. Test first proves it. |
| "Multiple fixes at once saves time" |
Can't isolate what worked. Causes new bugs. |
| "One more fix attempt" (after 2+ failures) |
3+ failures = architectural problem. Question pattern, don't fix again. |
Supporting References
Techniques available in references/:
root-cause-tracing.md — Trace bugs backward through the call stack to find the original trigger.
defense-in-depth.md — Add validation at multiple layers after finding the root cause.
condition-based-waiting.md — Replace arbitrary timeouts with condition polling to fix flaky tests.
find-polluter.sh — Bisection script to identify which test creates unwanted files or state.
Example implementation in examples/:
condition-based-waiting-example.ts — Complete TypeScript implementation of condition-based waiting utilities.
1---2name: systematic-debugging3description: Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes.4---56## Purpose78Find the root cause of any bug before attempting a fix. Random fixes waste time and create new bugs. Symptom fixes are failure.910**Core principle:** ALWAYS find root cause before attempting fixes.1112## When to Use1314Use for ANY technical issue:15- Test failures16- Bugs in production17- Unexpected behavior18- Performance problems19- Build failures20- Integration issues2122**Use this especially when:**2324- Under time pressure (emergencies make guessing tempting)25- "Just one quick fix" seems obvious26- You've already tried multiple fixes27- Previous fix didn't work28- You don't fully understand the issue2930**Do not skip when:**3132- Issue seems simple (simple bugs have root causes too)33- You're in a hurry (rushing guarantees rework)34- Someone wants it fixed immediately (systematic is faster than thrashing)3536## Inputs3738- Bug description or error message.39- Stack trace or reproduction steps.40- Environment context (OS, versions, recent changes).41- Access to the codebase and test suite.4243## Workflow4445You MUST complete each phase before proceeding to the next.4647### Phase 1: Root Cause Investigation4849**Before attempting ANY fix:**50511. **Read error messages carefully** — Don't skip past errors or warnings. Read stack traces completely. Note line numbers, file paths, error codes.52532. **Reproduce consistently** — Can you trigger it reliably? What are the exact steps? If not reproducible: gather more data, don't guess.54553. **Check recent changes** — What changed that could cause this? Git diff, recent commits, new dependencies, config changes, environmental differences.56574. **Gather evidence in multi-component systems** — When the system has multiple components (CI → build → signing, API → service → database):58 ```md59 For EACH component boundary:60 - Log what data enters the component61 - Log what data exits the component62 - Verify environment/config propagation63 - Check state at each layer6465 Run once to gather evidence showing WHERE it breaks.66 Then analyze to identify the failing component.67 Then investigate that specific component.68 ```69705. **Trace data flow** — Where does the bad value originate? What called this with the bad value? Keep tracing up until you find the source. Fix at source, not at symptom. See `references/root-cause-tracing.md` for the complete backward tracing technique.7172### Phase 2: Pattern Analysis73741. **Find working examples** — Locate similar working code in the same codebase.752. **Compare against references** — Read reference implementations completely. Don't skim.763. **Identify differences** — List every difference between working and broken, however small.774. **Understand dependencies** — What other components, settings, or config does this need?7879### Phase 3: Hypothesis and Testing80811. **Form a single hypothesis** — "I think X is the root cause because Y." Write it down. Be specific.822. **Test minimally** — Make the smallest possible change to test the hypothesis. One variable at a time.833. **Verify before continuing** — Did it work? Yes → Phase 4. No → form a new hypothesis. Do not add more fixes on top.844. **When you don't know** — Say so. Ask for help. Research more. Do not pretend to know.8586### Phase 4: Implementation87881. **Create a failing test case** — Simplest possible reproduction. Automated test if possible. Must exist before fixing.89902. **Implement a single fix** — Address the root cause identified. One change at a time. No "while I'm here" improvements.91923. **Verify the fix** — Test passes? No other tests broken? Issue actually resolved?93944. **If fix doesn't work** — STOP. Count how many fixes you've tried.95 - If fewer than 3: return to Phase 1, re-analyze with new information.96 - If 3 or more: question the architecture (see below).97985. **If 3+ fixes failed: question the architecture** — Each fix revealing new shared state or coupling elsewhere is a sign of an architectural problem, not a hypothesis failure. Stop and discuss with the team before attempting another fix.99100## Output101102```103Root cause: <what caused the bug — specific, not vague>104Evidence: <what you observed that confirmed the hypothesis>105Fix: <what was changed and where>106Test: <test added or updated>107Verification: <how fix was confirmed>108```109110## Verification111112- [ ] Root cause found (not just symptom location)113- [ ] Single hypothesis tested at a time114- [ ] Minimal fix applied — no bundled changes115- [ ] Test added or updated116- [ ] If 3+ fixes failed: architectural discussion initiated, not another fix attempt117118## Failure Modes119120**Red flags — STOP and return to Phase 1:**121122- "Quick fix for now, investigate later"123- "Just try changing X and see if it works"124- Adding multiple changes at once125- "Skip the test, I'll manually verify"126- "It's probably X" without evidence127- "I don't fully understand but this might work"128- "Here are the main problems: [lists fixes without investigation]"129- Proposing solutions before tracing data flow130- "One more fix attempt" (when already tried 2+)131- Each fix reveals a new problem in a different place132133**Common rationalizations:**134135| Excuse | Reality |136| --- | --- |137| "Issue is simple, don't need process" | Simple issues have root causes too. Process is fast for simple bugs. |138| "Emergency, no time for process" | Systematic debugging is faster than guess-and-check thrashing. |139| "Just try this first, then investigate" | First fix sets the pattern. Do it right from the start. |140| "I'll write test after confirming fix works" | Untested fixes don't stick. Test first proves it. |141| "Multiple fixes at once saves time" | Can't isolate what worked. Causes new bugs. |142| "One more fix attempt" (after 2+ failures) | 3+ failures = architectural problem. Question pattern, don't fix again. |143144## Supporting References145146Techniques available in `references/`:147148- **`root-cause-tracing.md`** — Trace bugs backward through the call stack to find the original trigger.149- **`defense-in-depth.md`** — Add validation at multiple layers after finding the root cause.150- **`condition-based-waiting.md`** — Replace arbitrary timeouts with condition polling to fix flaky tests.151- **`find-polluter.sh`** — Bisection script to identify which test creates unwanted files or state.152153Example implementation in `examples/`:154155- **`condition-based-waiting-example.ts`** — Complete TypeScript implementation of condition-based waiting utilities.