Skip to main content

systematic-debugging

For when you are actively fixing a bug, not documenting it. Replaces guess-and-check with a loop that isolates the cause before touching code.

Distinct from identify-bug: that skill documents a bug for someone else to fix and stops there. This one drives the fix. Use identify-bug to hand off; use this to resolve.

When to use

  • A test or endpoint fails and the cause is not obvious.
  • You have already tried one fix and it did not work (this is the strongest signal to switch to a systematic loop).
  • Behavior differs between environments (local vs staging) or between similar code paths.

Phase 1 — Reproduce

Get a reliable, minimal reproduction before changing anything.

  • Reduce to the smallest input/steps that still fail.
  • If it only reproduces in staging/prod, use obs-logs to pull the evidence; do not guess from a description.
  • If you cannot reproduce, you cannot verify a fix — invest here first.

Write the reproduction down (a failing test is ideal — see test-driven-development).

Phase 2 — Isolate

Narrow where the failure originates before asking why. In this stack, instrument at the boundaries data crosses:

BoundaryWhat to check
Gateway controllerIs the request shape correct on arrival? Log the DTO.
@MessagePattern RPCIs the payload intact across the service hop?
Service layerIs the business input correct before the DB call?
Mongo / AuroraIs the query/filter what you think? Log the actual filter + result count.

Bisect: confirm the data is correct at boundary N, wrong at boundary N+1. The bug lives between them. This turns "somewhere in the flow" into "these 20 lines".

Phase 3 — Hypothesize + instrument

State a specific, falsifiable hypothesis: "the filter drops the communityId because it's a string, not a number." Then add the instrumentation that would prove or disprove it (a log line with the actual value + type). Run. Read the evidence.

Do not apply a fix yet. Confirm the hypothesis first. A confirmed cause makes the fix obvious; an unconfirmed one makes it a guess.

Phase 4 — Fix + verify

  • Apply the minimal fix that addresses the confirmed cause.
  • Verify against the Phase-1 reproduction: it must now pass.
  • Remove temporary instrumentation (or convert genuinely useful lines to proper this.logger calls per the error-logging rules).
  • If you wrote a reproduction test, keep it as a regression guard.

The 3-attempt stop

After 3 failed fix attempts, STOP. Do not try a 4th variation. Three failures mean the model of the problem is wrong, not the fix. When you hit this:

  1. Discard the assumptions from the failed attempts — write them down as "ruled out".
  2. Return to Phase 2 and re-isolate with fresh instrumentation.
  3. If still stuck, escalate: hand off via identify-bug with the ruled-out list, or ask a domain agent (payments, auth-security, database) for the subsystem you're stuck in.

Repeated failure is a signal to change approach, not to try harder at the same one.

Red-flags table

RationalizationThe discipline
"I think I know what it is, let me just fix it"State the hypothesis, instrument, confirm — then fix.
"Let me try changing this and see"That's attempt-and-pray. Isolate first (Phase 2).
"It works now, not sure why"You did not find the cause. Re-run the reproduction 3× and confirm you understand the fix.
"One more fix idea" (after 3 tries)Stop. The problem model is wrong. Re-isolate.
"I'll read the whole log"grep for the boundary you're bisecting; delegate a wide log search to a subagent.

Token note

Instrumentation output and log dumps are heavy. When a phase produces large output (a full log tail, a wide query result), run it in a subagent per delegate-heavy-context so only the relevant evidence returns to the main thread.