toolhq.io

skills / code quality and testing

debug-systematically

Narrow a bug down instead of guessing at it, and explain the cause before proposing any fix.

SKILL.md

Save as .claude/skills/debug-systematically/SKILL.md in a repository, or under ~/.claude/skills/debug-systematically/SKILL.md to have it everywhere. The copy button takes the whole file, frontmatter included.

---
name: debug-systematically
description: Find the root cause of a bug by narrowing it down instead of guessing. Use when something is broken, a test fails intermittently, behaviour differs between environments, or someone asks why code is not working.
---

# Debug systematically

The failure mode in debugging is not a lack of ideas. It is changing things
before you understand them, which destroys the evidence and sometimes hides the
bug instead of fixing it.

Do not propose a fix until step 4.

## 1. Reproduce it

Nothing else is worth doing until the bug happens on demand.

- Get the exact input, the exact command, the exact environment.
- Write down the shortest sequence that triggers it.
- If it only happens sometimes, record how often. One in two and one in a
  thousand point at different causes.

If you cannot reproduce it, say so and start gathering what would let you. Do
not skip ahead and guess. A fix for a bug you never saw cannot be verified.

## 2. Get the real error

Read the actual message and the full trace, not a summary of it.

- Find the deepest frame in code you control. That is usually where to look,
  not the top of the trace.
- Read what the error says literally. "Cannot read property of undefined" means
  something was undefined, so the question is which thing and why, not what
  general class of bug this resembles.
- If an error is being swallowed, find the handler hiding it before anything
  else.

## 3. Narrow it

Halve the search space until the cause is cornered. Pick whichever split fits:

- **In time.** It worked before. Find the last good version, then bisect the
  commits between.
- **In space.** Remove parts of the input or disable parts of the system until
  the failure stops. The last thing you removed is implicated.
- **Across environments.** It works locally and fails in staging. List every
  difference, then test them one at a time: version, configuration,
  permissions, data, concurrency, clock.

State what each test ruled out. "Not the database, because it also fails with a
stub" is progress and worth recording.

## 4. Explain it before fixing it

Write the causal chain in one or two sentences: this input, through this path,
produces this state, which fails here.

If you cannot write that sentence, you have not found the cause. You have found
a change that makes the symptom go away, which is different and often worse.

## 5. Fix the cause, not the symptom

- Fix it where every caller passes through, not in the one place that was
  reported. A guard in the shared function is a smaller diff than a guard in
  each caller, and it fixes the siblings that nobody reported yet.
- Check the other callers of anything you touch before you touch it.
- Add a test that fails without the fix. Run it both ways and confirm.

## Traps

- **The intermittent bug is usually ordering, state or time.** Shared mutable
  state, a race, a test that depends on the previous test, a timezone, a cache.
- **"It works on my machine" is data, not an insult.** The difference between
  the machines is the bug.
- **Recent changes are the first suspects**, but not the only ones. A latent
  bug exposed by a deploy is still a bug in the old code.
- **If the fix does not match the explanation, you have not finished.**

## Report back

1. How to reproduce it.
2. The root cause, as a causal chain.
3. What you ruled out along the way.
4. The fix, and the test that proves it.

what it needs

Nothing. This skill is instructions only, so it works in any agent that reads SKILL.md with nothing installed and no account. Save the file and it is live in the next session.

the instructions, in full

Debug systematically

The failure mode in debugging is not a lack of ideas. It is changing things before you understand them, which destroys the evidence and sometimes hides the bug instead of fixing it.

Do not propose a fix until step 4.

1. Reproduce it

Nothing else is worth doing until the bug happens on demand.

If you cannot reproduce it, say so and start gathering what would let you. Do not skip ahead and guess. A fix for a bug you never saw cannot be verified.

2. Get the real error

Read the actual message and the full trace, not a summary of it.

3. Narrow it

Halve the search space until the cause is cornered. Pick whichever split fits:

State what each test ruled out. "Not the database, because it also fails with a stub" is progress and worth recording.

4. Explain it before fixing it

Write the causal chain in one or two sentences: this input, through this path, produces this state, which fails here.

If you cannot write that sentence, you have not found the cause. You have found a change that makes the symptom go away, which is different and often worse.

5. Fix the cause, not the symptom

Traps

Report back

  1. How to reproduce it.
  2. The root cause, as a causal chain.
  3. What you ruled out along the way.
  4. The fix, and the test that proves it.