Files
gh-glittercowboy-taches-cc-…/skills/debug-like-expert/references/debugging-mindset.md
2025-11-29 18:28:37 +08:00

8.7 KiB

Debugging is applied epistemology. You're investigating a system to discover truth about its behavior. The difference between junior and senior debugging is not knowledge of frameworks - it's the discipline of systematic investigation.

<meta_debugging> Special challenge: When you're debugging code you wrote or modified, you're fighting your own mental model.

Why this is harder:

  • You made the design decisions - they feel obviously correct
  • You remember your intent, not what you actually implemented
  • You see what you meant to write, not what's there
  • Familiarity breeds blindness to bugs

The trap:

  • "I know this works because I implemented it correctly"
  • "The bug must be elsewhere - I designed this part"
  • "I tested this approach"
  • These thoughts are red flags. Code you wrote is guilty until proven innocent.

The discipline:

1. Treat your own code as foreign

  • Read it as if someone else wrote it
  • Don't assume it does what you intended
  • Verify what it actually does, not what you think it does
  • Fresh eyes see bugs; familiar eyes see intent

2. Question your own design decisions

  • "I chose approach X because..." - Was that reasoning sound?
  • "I assumed Y would..." - Have you verified Y actually does that?
  • Your implementation decisions are hypotheses, not facts

3. Admit your mental model might be wrong

  • You built a mental model of how this works
  • That model might be incomplete or incorrect
  • The code's behavior is truth; your model is just a guess
  • Be willing to discover you misunderstood the problem

4. Prioritize code you touched

  • If you modified 100 lines and something breaks
  • Those 100 lines are the prime suspects
  • Don't assume the bug is in the framework or existing code
  • Start investigating where you made changes
"I implemented the auth flow correctly, the bug must be in the existing user service"

"I implemented the auth flow. Let me verify each part:

  • Does login actually set the token? [test it]
  • Does the middleware actually validate it? [test it]
  • Does logout actually clear it? [test it]
  • One of these is probably wrong"

The second approach found that logout wasn't clearing the token from localStorage, only from memory.

The hardest admission: "I implemented this wrong."

Not "the requirements were unclear" or "the library is confusing" - YOU made an error. Whether it was 5 minutes ago or 5 days ago doesn't matter. Your code, your responsibility, your bug to find.

This intellectual honesty is the difference between debugging for hours and finding bugs quickly. </meta_debugging>

When debugging, return to foundational truths:

What do you know for certain?

  • What have you directly observed (not assumed)?
  • What can you prove with a test right now?
  • What is speculation vs evidence?

What are you assuming?

  • "This library should work this way" - Have you verified?
  • "The docs say X" - Have you tested that X actually happens?
  • "This worked before" - Can you prove when it worked and what changed?

Strip away everything you think you know. Build understanding from observable facts.

"React state updates should be synchronous here" "Let me add a console.log to observe when state actually updates"

"The API must be returning bad data" "Let me log the exact response payload to see what's actually being returned"

"This database query should be fast" "Let me run EXPLAIN to see the actual execution plan"

<cognitive_biases>

**The problem**: You form a hypothesis and only look for evidence that confirms it.

The trap: "I think it's a race condition" → You only look for async code, missing the actual typo in a variable name.

The antidote: Actively seek evidence that disproves your hypothesis. Ask "What would prove me wrong?"

**The problem**: The first explanation you encounter becomes your anchor, and you adjust from there instead of considering alternatives.

The trap: Error message mentions "timeout" → You assume it's a network issue, when it's actually a deadlock.

The antidote: Generate multiple independent hypotheses before investigating any single one. Force yourself to list 3+ possible causes.

**The problem**: You remember recent bugs and assume similar symptoms mean the same cause.

The trap: "We had a caching issue last week, this must be caching too."

The antidote: Treat each bug as novel until evidence suggests otherwise. Recent memory is not evidence.

**The problem**: You've spent 2 hours debugging down one path, so you keep going even when evidence suggests it's wrong.

The trap: "I've almost figured out this state management issue" - when the actual bug is in the API layer.

The antidote: Set checkpoints. Every 30 minutes, ask: "If I started fresh right now, is this still the path I'd take?"

</cognitive_biases>

<systematic_investigation>

**Why it matters**: If you change multiple things at once, you don't know which one fixed (or broke) it.

In practice:

  1. Make one change
  2. Test
  3. Observe result
  4. Document
  5. Repeat

The temptation: "Let me also update this dependency and refactor this function and change this config..."

The reality: Now you have no idea what actually mattered.

**Why it matters**: Skimming code causes you to miss crucial details. You see what you expect to see, not what's there.

In practice:

  • Read entire functions, not just the "relevant" lines
  • Read imports and dependencies
  • Read configuration files completely
  • Read test files to understand intended behavior

The shortcut: "This function is long, I'll just read the part where the error happens"

The miss: The bug is actually in how the function is called 50 lines up.

**Why it matters**: Premature certainty stops investigation. "I don't know" is a position of strength.

In practice:

  • "I don't know why this fails" - Good. Now you can investigate.
  • "It must be X" - Dangerous. You've stopped thinking.

The pressure: Users want answers. Managers want ETAs. Your ego wants to look smart.

The truth: "I need to investigate further" is more professional than a wrong fix.

</systematic_investigation>

<when_to_restart>

<restart_signals> You should consider starting over when:

  1. You've been investigating for 2+ hours with no progress

    • You're likely tunnel-visioned
    • Take a break, then restart from evidence gathering
  2. You've made 3+ "fixes" that didn't work

    • Your mental model is wrong
    • Go back to first principles
  3. You can't explain the current behavior

    • Don't add more changes on top of confusion
    • First understand what's happening, then fix it
  4. You're debugging the debugger

    • "Is my logging broken? Is the debugger lying?"
    • Step back. Something fundamental is wrong.
  5. The fix works but you don't know why

    • This isn't fixed. This is luck.
    • Investigate until you understand, or revert the change </restart_signals>

<restart_protocol> When restarting:

  1. Close all files and terminals
  2. Write down what you know for certain (not what you think)
  3. Write down what you've ruled out
  4. List new hypotheses (different from before)
  5. Begin again from Phase 1: Evidence Gathering

This isn't failure. This is professionalism. </restart_protocol>

</when_to_restart>

The best debuggers have deep humility about their mental models:

They know:

  • Their understanding of the system is incomplete
  • Documentation can be wrong or outdated
  • Their memory of "how this works" may be faulty
  • The system's behavior is the only truth

They don't:

  • Trust their first instinct
  • Assume anything works as designed
  • Skip verification steps
  • Declare victory without proof

They ask:

  • "What am I missing?"
  • "What am I wrong about?"
  • "What haven't I tested?"
  • "What does the evidence actually say?"
Debugging is a craft that improves with practice:

Novice debuggers:

  • Try random things hoping something works
  • Skip reading code carefully
  • Don't test their hypotheses
  • Declare success too early

Expert debuggers:

  • Form hypotheses explicitly
  • Test hypotheses systematically
  • Read code like literature
  • Verify fixes rigorously
  • Learn from each investigation

The difference: Not intelligence. Not knowledge. Discipline.

Practice the discipline of systematic investigation, and debugging becomes a strength.