{"slug":"systematic-debugging-11","title":"systematic-debugging","summary":"Debugging methodology, hypothesis testing, reading stack traces, isolating issues. Use when facing an unexpected bug, a flaky test, a production incident, or any situation where the cause isn't immediately obvious.","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-09-17T16:54:39.990441Z","repo":{"url":"https://github.com/sabahattink/antigravity-fullstack-hq","stars":30,"forks":9,"license":"MIT","updatedAt":"2026-09-21T13:27:15Z"},"bodyHtml":"<hr>\n<h2>name: systematic-debugging\ndescription: Debugging methodology, hypothesis testing, reading stack traces, isolating issues. Use when facing an unexpected bug, a flaky test, a production incident, or any situation where the cause isn't immediately obvious.</h2>\n<h1>Systematic Debugging</h1>\n<h2>The Scientific Method Applied to Bugs</h2>\n<pre><code>1. OBSERVE   — Reproduce the issue reliably\n2. HYPOTHESIZE — Form the simplest explanation consistent with symptoms\n3. PREDICT   — \"If my hypothesis is correct, then X should be true\"\n4. TEST      — Run an experiment to falsify the hypothesis\n5. CONCLUDE  — If wrong, refine hypothesis and repeat\n</code></pre>\n<p>Never jump to a fix before you understand the cause. A fix without understanding is guessing.</p>\n<h2>Step 1: Reproduce Reliably</h2>\n<p>You cannot debug a bug you cannot reproduce.</p>\n<pre><code># Note the exact conditions:\n# - Input / request body\n# - Environment (local / staging / prod)\n# - User account or data state\n# - Frequency (always / sometimes / once)\n# - When it started (after which deploy?)\n\n# Find the last good commit\ngit bisect start\ngit bisect bad HEAD\ngit bisect good v1.2.3   # last known good tag\n# git bisect then checks out midpoints — test and mark good/bad\n</code></pre>\n<h2>Step 2: Read the Stack Trace</h2>\n<pre><code>Error: Cannot read properties of undefined (reading 'email')\n    at UserService.findOneOrFail (/src/users/users.service.ts:42:23)\n    at UsersController.findOne (/src/users/users.controller.ts:28:31)\n    at ...\n\nReading strategy:\n1. Top line: the actual error — read it carefully word by word\n2. First frame after your code: where it crashed (users.service.ts:42)\n3. Frame above that: what called it (users.controller.ts:28)\n4. Ignore node_modules frames unless diagnosing a library issue\n</code></pre>\n<pre><code>// users.service.ts line 42 — the crash site\nasync findOneOrFail(id: number): Promise&lt;User&gt; {\n  const user = await this.repo.findById(id)\n  // line 42: user is undefined, not null — we expected null\n  return user  // accessing .email somewhere downstream fails\n}\n\n// Fix: repo.findOne returns undefined when not found, but our types say null\n// The contract mismatch is the root cause, not the downstream access\n</code></pre>\n<h2>Step 3: Isolate the Problem</h2>\n<p>Binary search through the call stack to find where the invariant breaks.</p>\n<pre><code>// Add strategic logging — not everywhere, but at the boundary\nasync findOneOrFail(id: number): Promise&lt;User&gt; {\n  console.log('[DEBUG] findOneOrFail called with', { id, type: typeof id })\n  const user = await this.repo.findById(id)\n  console.log('[DEBUG] repo returned', { user, type: user === null ? 'null' : typeof user })\n  // ...\n}\n\n// Now you know: is `id` the wrong value, or does the repo return unexpected type?\n</code></pre>\n<pre><code># Isolate environment issues\nNODE_ENV=production node -e \"require('./dist/main')\"  # test prod build locally\n\n# Isolate database issues — run the query directly\npsql $DATABASE_URL -c \"SELECT * FROM users WHERE id = 42;\"\n\n# Isolate network issues\ncurl -v -H \"Authorization: Bearer $TOKEN\" http://localhost:3000/api/v1/users/42\n</code></pre>\n<h2>Common Bug Patterns</h2>\n<h3>Async/Await Mistakes</h3>\n<pre><code>// BUG: missing await — returns Promise, not value\nasync function getBadge(userId: string) {\n  const user = this.repo.findById(userId)  // ← missing await\n  return user.role === 'admin' ? 'admin' : 'user'  // TypeError: user.role undefined\n}\n\n// BUG: forEach with async — fires and forgets\nasync function notifyAll(userIds: string[]) {\n  userIds.forEach(async id =&gt; {  // ← async inside forEach is a trap\n    await this.emailService.send(id)\n  })\n  // returns before any emails sent!\n}\n\n// FIX: use Promise.all or for...of\nasync function notifyAll(userIds: string[]) {\n  await Promise.all(userIds.map(id =&gt; this.emailService.send(id)))\n  // OR (sequential):\n  for (const id of userIds) {\n    await this.emailService.send(id)\n  }\n}\n</code></pre>\n<h3>Stale Closure</h3>\n<pre><code>// BUG: stale closure captures old value\nfunction Timer() {\n  const [count, setCount] = useState(0)\n\n  useEffect(() =&gt; {\n    const id = setInterval(() =&gt; {\n      setCount(count + 1)  // ← count is always 0 in this closure\n    }, 1000)\n    return () =&gt; clearInterval(id)\n  }, [])  // ← empty deps — count never updates in closure\n\n  // FIX: use functional update\n  useEffect(() =&gt; {\n    const id = setInterval(() =&gt; {\n      setCount(c =&gt; c + 1)  // ← always uses latest value\n    }, 1000)\n    return () =&gt; clearInterval(id)\n  }, [])\n}\n</code></pre>\n<h3>Race Condition</h3>\n<pre><code>// BUG: two concurrent requests overwrite each other\nasync function incrementViews(postId: string) {\n  const post = await db.posts.findOne(postId)\n  post.views++\n  await db.posts.save(post)  // request B may have read same value\n}\n\n// FIX: atomic update\nasync function incrementViews(postId: string) {\n  await db.posts.increment({ id: postId }, 'views', 1)\n  // or: UPDATE posts SET views = views + 1 WHERE id = $1\n}\n</code></pre>\n<h3>TypeScript Lies</h3>\n<pre><code>// Type assertions hide runtime issues\nconst user = result as User  // if result is null, this silently succeeds\nuser.email  // CRASHES at runtime\n\n// Safe pattern: validate at runtime\nfunction assertIsUser(value: unknown): asserts value is User {\n  if (!value || typeof value !== 'object' || !('email' in value)) {\n    throw new Error(`Expected User, got: ${JSON.stringify(value)}`)\n  }\n}\n\n// Or use Zod/io-ts at system boundaries\nconst UserSchema = z.object({ id: z.number(), email: z.string().email() })\nconst user = UserSchema.parse(apiResponse)  // throws with clear message if wrong\n</code></pre>\n<h2>Debugging Tools</h2>\n<h3>Node.js Inspector</h3>\n<pre><code># Start with inspector\nnode --inspect-brk dist/main.js\n\n# Attach VS Code debugger (launch.json)\n{\n  \"type\": \"node\",\n  \"request\": \"attach\",\n  \"name\": \"Attach to Process\",\n  \"processId\": \"${command:PickProcess}\",\n  \"sourceMaps\": true,\n  \"outFiles\": [\"${workspaceFolder}/dist/**/*.js\"]\n}\n</code></pre>\n<h3>VS Code launch.json for NestJS</h3>\n<pre><code>{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Debug NestJS\",\n      \"program\": \"${workspaceFolder}/src/main.ts\",\n      \"preLaunchTask\": \"tsc: build\",\n      \"outFiles\": [\"${workspaceFolder}/dist/**/*.js\"],\n      \"sourceMaps\": true,\n      \"envFile\": \"${workspaceFolder}/.env.local\"\n    },\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Debug Tests\",\n      \"program\": \"${workspaceFolder}/node_modules/vitest/vitest.mjs\",\n      \"args\": [\"run\", \"--reporter=verbose\"],\n      \"sourceMaps\": true\n    }\n  ]\n}\n</code></pre>\n<h3>Database Query Debugging</h3>\n<pre><code>// TypeORM: enable query logging in dev\nTypeOrmModule.forRoot({\n  logging: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],\n})\n\n// Log specific query with explain\nconst result = await this.dataSource.query(`\n  EXPLAIN ANALYZE\n  SELECT * FROM users WHERE email = $1\n`, ['jane@example.com'])\nconsole.log(result)\n</code></pre>\n<h2>Debugging Checklist</h2>\n<pre><code>□ Can I reproduce it? (if not, gather more info first)\n□ Did it ever work? (if yes, git bisect to find regression)\n□ What changed recently? (last deploy, config, data migration)\n□ What do the logs say? (search by request ID or user ID)\n□ Is it environment-specific? (only prod? only with certain data?)\n□ What does the stack trace say? (first line, first your-code frame)\n□ What is the actual vs expected value at the crash site?\n□ Is it a type mismatch? null vs undefined? string vs number?\n□ Is it a timing issue? (async, race condition, timeout)\n□ Is it an environment issue? (env vars, secrets, config)\n</code></pre>\n<h2>Production Incident Playbook</h2>\n<pre><code>1. TRIAGE (&lt; 5 min)\n   - Identify affected users/scope\n   - Is data at risk? → escalate immediately\n   - Can we roll back? → do it if yes and impact is high\n\n2. INVESTIGATE (keep timeline)\n   - Pull logs: kubectl logs / CloudWatch / Datadog\n   - Find first error occurrence: git log + deploy history\n   - Identify causal commit or config change\n\n3. MITIGATE\n   - Rollback deploy if a commit caused it\n   - Feature flag off if available\n   - Scale up if load-related\n\n4. FIX\n   - Write a failing test that reproduces the bug\n   - Fix it\n   - Deploy hotfix\n\n5. POSTMORTEM\n   - Timeline of events\n   - Root cause (not \"human error\" — what systemic issue enabled it?)\n   - Action items with owners and due dates\n</code></pre>\n<h2>Forbidden Patterns</h2>\n<ul>\n<li>Never change multiple things at once while debugging — you won't know what fixed it</li>\n<li>Never \"fix\" a bug by catching and suppressing the error</li>\n<li>Never assume the bug is in someone else's code before verifying</li>\n<li>Never debug production by adding <code>console.log</code> to prod builds — use structured logging</li>\n<li>Never close an investigation with \"it works now\" without understanding why</li>\n<li>Never skip writing a regression test after fixing a bug</li>\n</ul>\n","files":[{"path":"SKILL.md","sizeBytes":8784,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"notes-only","suspicious":0,"notes":2,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-09-17T16:55:38.669084Z","sha256":"EDC41238BDA4467C68FDB40E231E95A3E1CA3DA7BA4A6A4ED5AD9BEBD32C81D9","sizeBytes":4024},"review":null,"source":{"repositoryUrl":"https://github.com/sabahattink/antigravity-fullstack-hq","path":"skills/systematic-debugging","license":"MIT","commit":"90524b3f8e9ccb8e33e9a0d97e9463d28abe2646","subtreeSha":"A7A2CC87399B617C174D50889E47835391EA0321CE6D05E32CDE0A5B5E22466B","lastSyncedAt":"2026-09-25T23:11:42.035577Z"},"reviewedAt":"2026-09-17T16:58:34.471122Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/sabahattink/antigravity-fullstack-hq/tree/main/skills/systematic-debugging"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install sabahattink-antigravity-fullstack-hq@llmmart"},{"target":"git","command":"git clone https://github.com/sabahattink/antigravity-fullstack-hq.git"}]}