systematic-debugging

verified

0428cc7b-97b8-4b2a-9590-bde5cca5774c

Use when debugging any bug — 4-phase root cause methodology: reproduce, isolate, hypothesize, fix-verify. No code changes before a repro exists.

Metadata

Skill ID
0428cc7b-97b8-4b2a-9590-bde5cca5774c
Version
1
Owner
387274b7-2891-478b-81b8-e11d5adb9319
Tags
debuggingmethodologytroubleshootingroot-cause-analysis
Signature
verified
Integrity
OK
Content hash
426df9361954e011b121571486cf72ff7c431b3c55de0a273800c5b2f08f2e9d
Created
2026-08-15T05:27:29Z

Skill file

Raw skill file (markdown source)
# Systematic Debugging

**Use when** you encounter a bug of any severity. Never change code before completing Phase 1. This is a four-phase, evidence-first methodology.

## Phase 1: Reproduce (NO CODE CHANGES YET)

Rule: if you cannot reproduce the bug, you cannot fix it.

```bash
# 1. Capture the exact command/input that triggers it
# 2. Record the environment
python -c "import sys, platform; print(sys.version, platform.platform())"

# 3. Confirm it fails every time — run it 5x
for i in $(seq 5); do
    pytest tests/test_broken.py::test_thing -x 2>&1 | tail -1
done
```

**Minimum repro** — strip everything possible:
```python
# Before: 200-line script that fails 30% of the time
# After: 8-line repro that fails 100%
def test_minimal_repro():
    from app.service import process
    assert process({"id": None, "items": []}) == {"status": "ok"}
    # Should fail with AttributeError: 'NoneType' object has no attribute 'lower'
```

Decision gate:
| Can you reproduce? | Action |
|---|---|
| Yes, 100% of the time | Proceed to Phase 2 |
| Yes, but only sometimes | Go to flaky-test-triage skill first |
| No, never on your machine | Check env diff: Python version, OS, deps (`pip freeze` diff) |

## Phase 2: Isolate

Narrow from the system to the failing line using bisection.

```python
# Binary-search logging: log at midpoint, run, cut in half
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

# Then add:
logger.debug("Entering process_order: order_id=%s, items=%s", order_id, len(items))
```

**Git bisect** (if it worked before):
```bash
git bisect start
git bisect bad HEAD
git bisect good v2.5.0
git bisect run pytest tests/test_broken.py::test_thing
git bisect reset
```

**Binary search by commenting**: comment out the back half of the function. If it still fails, the bug is in the front half. Repeat. You'll find the exact line in ~log2(n) steps.

## Phase 3: Hypothesize

Before changing ANYTHING, write down:
1. "I believe the root cause is _____________ because _____________"
2. "If I'm right, changing X to Y will make the test pass"
3. "If I'm wrong, I'll see _____________ instead"

```python
# Test the hypothesis WITHOUT changing production code
def test_hypothesis():
    # If root cause is None handling, then passing a valid id should work
    result = process({"id": "abc123", "items": []})
    assert result["status"] == "ok"

# This should PASS if hypothesis is correct, FAIL if something else is wrong
```

## Phase 4: Fix + Verify

Make exactly ONE minimal change:

```python
# BEFORE (failing):
def process(payload):
    normalized = payload["id"].lower()  # AttributeError on None

# AFTER (fixed):
def process(payload):
    uid = payload.get("id")
    normalized = uid.lower() if uid else ""
```

Then:
```bash
# 1. Prove the repro now passes
pytest tests/test_minimal_repro.py -x -v

# 2. Prove nothing else broke
pytest tests/ -x --no-header -q

# 3. If the fix introduced a new edge case, capture it
git diff  # review the single change
```

## Guardrails

- NO code changes in Phase 1, 2, or 3. Period.
- One fix. NOT two things at once. You can't tell which one worked.
- "It works now" is not a root cause. You must be able to explain WHY.
- If the fix is >10 lines, you haven't isolated enough.

## Pitfalls

| Pitfall | Consequence | Fix |
|---------|-------------|-----|
| Fixing before reproducing | You're guessing, not debugging | Delete the fix, start at Phase 1 |
| Multiple simultaneous "fixes" | Can't tell which solved it | Revert all, apply one at a time |
| Skipping the hypothesis | Fix symptoms, not causes | Write the hypothesis on paper |
| Not running full suite after | Regression bugs | `pytest tests/ -x` after every fix |
| Accepting "it doesn't repro for me" | Bug lives on in prod | Get exact env from the reporter |

## Verify / Checklist

- [ ] Minimal repro exists and fails 100% (flaky <10%: separate triage)
- [ ] The failing line is identified (not "somewhere in this module")
- [ ] Hypothesis written and tested BEFORE code change
- [ ] Exactly ONE change in the diff
- [ ] Full test suite passes after the fix
- [ ] Root cause can be explained in one sentence

Attached files

No attached files.