Reproduction Steps
【免费下载链接】claude-skills67 Specialized Skills for Full-Stack Developers. Transform Claude Code into your expert pair programmer.项目地址: https://gitcode.com/GitHub_Trending/claud/claude-skills
- Navigate to /users
- Click "Load More" button
- Wait for loading spinner
- ERROR: "Cannot read property 'map' of undefined"
Environment
- Browser: Chrome 120
- User: Admin role
- Data state: 50+ users in database
环境信息(浏览器、用户角色、数据状态)同样关键——bug 往往只在特定数据量或特定角色下触发。这对应 SKILL.md 中 MUST DO 的 "Gather complete error messages and stack traces"。 ### 1.3 检查最近的变更 回归类 bug 十有八九由最近提交引入: ```bash # What changed recently? git log --oneline -10 # What specifically changed in the failing file? git log -p UserList.tsx # When did this start failing? git bisect start git bisect bad HEAD git bisect good v1.2.0git bisect可以自动化二分定位"第一个坏提交"。关于二分定位的更完整用法,仓库在 strategies.md 中有专门小节,支持git bisect run npm test之类的全自动回归扫描。
1.4 沿数据流反向追踪
从报错点出发,一步一步向前追问"这个变量从哪来":
// Error happens here: users.map(u => u.name) // users is undefined // Trace backward: // Where does 'users' come from? const users = props.users; // Where do props come from? <UserList users={data.users} /> // Where does data come from? const { data } = useQuery(GET_USERS); // ROOT CAUSE: Query returns { users: null } when loading这一步骤往往能直接暴露根因——本例中,查询处于 loading 状态时返回{ users: null },而组件渲染时没有任何空值保护。
1.5 添加诊断性插桩
在关键边界处加临时日志,确认数据在各环节的真实形态:
// Add temporary logging at boundaries console.log('[UserList] props:', JSON.stringify(props)); console.log('[UserList] users type:', typeof props.users); console.log('[UserList] users value:', props.users); // Check at data source console.log('[API] Response:', response); console.log('[API] Response.data:', response.data);提示:插桩日志属于临时调试手段。SKILL.md 的约束明确要求 "Remove all debug code before committing(提交前删除所有调试代码)"。
Phase 2:模式分析
目标:找到正常工作的示例,搞清楚"正确行为"应该长什么样。
2.1 定位相似的正常实现
# Find similar components that work correctly grep -r "useQuery" src/components/ --include="*.tsx" # Find how other lists handle loading states grep -r "loading" src/components/*List* --include="*.tsx"2.2 完整研究参考实现
逐行对比正常与异常实现的差异——往往差异点就是病灶:
// WORKING: ProductList.tsx function ProductList({ products, loading }) { if (loading) return <Spinner />; if (!products) return null; // ← Handles undefined case return products.map(p => <ProductItem key={p.id} {...p} />); } // BROKEN: UserList.tsx function UserList({ users, loading }) { if (loading) return <Spinner />; // Missing: !users check return users.map(u => <UserItem key={u.id} {...u} />); // 💥 Crashes }2.3 记录全部差异
| Aspect | Working (ProductList) | Broken (UserList) |
|---|---|---|
| Null check | if (!products) | Missing |
| Default value | products ?? [] | None |
| Loading handled | Before render | Before render |
| Error handled | Returns ErrorState | Missing |
差异表让"正常 vs 异常"的差距一目了然,是形成假设的直接输入。
Phase 3:假设验证
目标:用受控实验验证你的理解是否正确。
3.1 形成具体、书面的假设
## Hypothesis #1 **Statement:** The crash occurs because `users` is undefined when the query is complete but returns no data. **Prediction:** Adding a null check before `.map()` will prevent the crash. **Test:** Add `if (!users) return null;` before the map call.好的假设必须包含三要素:陈述(Statement)、可观测的预测(Prediction)、最小验证实验(Test)。
3.2 用最小变更验证
// Change ONLY one thing function UserList({ users, loading }) { if (loading) return <Spinner />; if (!users) return null; // ← Single change return users.map(u => <UserItem key={u.id} {...u} />); }3.3 一次只改变一个变量
## Test Results | Hypothesis | Change | Result | Conclusion | |------------|--------|--------|------------| | #1: Null check | Add `if (!users)` | ✓ Pass | Confirmed | Do NOT test multiple hypotheses simultaneously.严禁同时验证多个假设——否则无法判断究竟是哪个改动生效。这与仓库的调试红线完全一致:SKILL.md 的 MUST NOT DO 明令 "Make multiple changes at once"。
Phase 4:实现
目标:带防护措施地永久修复 bug,而不是打补丁。
4.1 先写失败的测试用例
在动手改代码之前,先让测试"证明 bug 存在":
describe('UserList', () => { it('should handle undefined users gracefully', () => { // This test should FAIL before the fix const { container } = render(<UserList users={undefined} loading={false} />); expect(container).not.toThrow(); expect(screen.queryByRole('list')).not.toBeInTheDocument(); }); });这条测试在修复前必须失败(当前实现没有空值保护),修复后通过。TDD 的红绿循环保证了修复的可验证性。对应 SKILL.md 中 MUST DO 的 "Add regression tests after fixing"。
4.2 实现针对根因的单一修复
function UserList({ users, loading }: UserListProps) { if (loading) return <Spinner />; if (!users || users.length === 0) { return <EmptyState message="No users found" />; } return ( <ul role="list"> {users.map(u => <UserItem key={u.id} {...u} />)} </ul> ); }注意这里比最初的假设多了一个边界:不仅处理undefined,还处理空数组并渲染专门的EmptyState,这是对根因(查询返回空数据)的完整覆盖,而非只堵住崩溃点。
4.3 验证没有产生新的破坏
# Run full test suite npm test # Run specific component tests npm test UserList # Run integration tests npm run test:integration # Verify in browser # 1. Normal case: 50 users # 2. Empty case: 0 users # 3. Loading case: spinner shows # 4. Error case: error message shows验证覆盖四类场景:正常数据、空数据、加载中、出错态。只有全部通过,才算修复完成。SKILL.md 的 Output Templates 也要求调试结束时给出四件套:Root Cause(根因)→ Evidence(证据)→ Fix(修复)→ Prevention(防复发措施)。
三连修复阈值:连续三次失败后立即停止
连续 3 次修复尝试失败 → 停止(After 3 failed fix attempts → STOP)。
三次失败且失败点各不相同,通常意味着架构性问题,而不是孤立的 bug:
Fix Attempt 1: Added null check → New error in child component Fix Attempt 2: Fixed child component → New error in parent Fix Attempt 3: Fixed parent → Original error returns ↓ STOP. QUESTION ARCHITECTURE.到达阈值时应该做什么
- 停止修补症状;
- 记录失败的模式(每次修复又引入了什么新错误);
- 识别被违反的架构假设;
- 提出结构性变更,而不是继续打补丁;
- 与团队讨论后再继续。
这个"停手阈值"与决策流程图中的Question architecture分支衔接:当修复尝试次数达到上限仍未通过测试时,流程就进入架构反思,而不是退回 Phase 1 无限重试。
需要重置流程的红旗信号
出现以下任何信号,立即停止当前动作、回到 Phase 1 重新开始:
| Red Flag | Why It's Wrong |
|---|---|
| Proposing solutions before tracing data flow | Guessing, not debugging |
| Making multiple simultaneous changes | Can't identify which change worked |
| Skipping test creation | Bug will recur |
| "Let's try this and see if it works" | Shotgun debugging |
| Fixing without understanding the cause | Band-aid, not cure |
这些信号本质上把调试从"科学实验"退化成"碰运气"。对照 SKILL.md 的 MUST NOT DO 清单可以进一步验证:跳过复现步骤、不做验证就猜测、一次改多处、假定已知根因、在生产环境无防护地调试,都是被明令禁止的行为。
决策流程图
整个调试流程可以用下面这张决策图概括,它是四阶段方法论的运行时总控:
┌──────────────────┐ │ Bug Reported │ └────────┬─────────┘ │ ┌──────────────▼──────────────┐ │ Can you reproduce it? │ └──────────────┬──────────────┘ No │ Yes ┌────────────────┴────────────────┐ ▼ ▼ ┌───────────────┐ ┌─────────────────┐ │ Get more info │ │ Trace data flow │ └───────────────┘ └────────┬────────┘ │ ┌──────────────▼──────────────┐ │ Do you understand the cause? │ └──────────────┬──────────────┘ No │ Yes ┌────────────────────────┴─────────┐ ▼ ▼ ┌───────────────┐ ┌─────────────────┐ │ Study working │ │ Write hypothesis│ │ examples │ └────────┬────────┘ └───────────────┘ │ ┌───────▼───────┐ │ Write test │ └───────┬───────┘ │ ┌───────▼───────┐ │ Implement │ └───────┬───────┘ │ ┌──────────────────▼──────────────────┐ │ Does test pass? │ └──────────────────┬──────────────────┘ No │ Yes ┌────────────────────────┴──────────┐ ▼ ▼ ┌───────────────┐ ┌─────────────────┐ │ Attempt < 3? │ │ Done │ └───────┬───────┘ └─────────────────┘ No │ Yes ┌───────────────┴─────────────────┐ ▼ ▼ ┌───────────────────┐ ┌─────────────────────┐ │ Question │ │ Return to Phase 1 │ │ architecture │ └─────────────────────┘ └───────────────────┘关键分支解读:
- 无法复现→ 不盲目猜测,先收集更多信息;
- 不理解根因→ 去研究正常实现(Phase 2);
- 理解根因→ 写假设、写测试、实现;
- 测试未通过→ 判断是否达到三次尝试上限:未达到则返回 Phase 1 重新调查;达到则质疑架构。
配套武器库:工具、策略与常见模式
系统性调试方法论不是孤立的——它在 claude-skills 仓库中与 debugging-wizard 技能的其他参考文档组成完整武器库,在 SKILL.md 的路由表中按场景分发:
| Topic | Reference | Load When |
|---|---|---|
| Debugging Tools | references/debugging-tools.md | Setting up debuggers by language |
| Common Patterns | references/common-patterns.md | Recognizing bug patterns |
| Strategies | references/strategies.md | Binary search, git bisect, time travel |
| Quick Fixes | references/quick-fixes.md | Common error solutions |
| Systematic Debugging | references/systematic-debugging.md | Complex bugs, multiple failed fixes, root cause analysis |
调试器速查(debugging-tools.md)
# Node.js / TypeScript node --inspect-brk dist/main.js # 暂停在首行,可接 Chrome DevTools # Python python -m pdb script.py # 启动 pdb python -m pdb -c continue script.py # 异常后 post-mortem 检查 # Go (Delve) dlv debug ./cmd/server # (dlv) break main.go:55 # (dlv) print myVar # Rust rust-gdb ./target/debug/app代码内快速诊断技巧:
debugger; // JS 断点 console.log({ variable }); // 打印变量名+值 console.trace('Called from'); // 打印调用栈breakpoint() # Python 3.7+ print(f"{variable=}") # Python 3.8+ 打印变量名+值VS Code 调试配置示例(.vscode/launch.json):
{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Debug TypeScript", "program": "${workspaceFolder}/src/main.ts", "preLaunchTask": "tsc: build", "outFiles": ["${workspaceFolder}/dist/**/*.js"] } ] }核心调试策略(strategies.md)
| Strategy | Best For |
|---|---|
| Binary Search | Unknown bug location(未知 bug 位置) |
| Minimal Repro | Complex bugs, reporting(复杂 bug、上报) |
| Git Bisect | Regression bugs(回归 bug) |
| Time Travel | Known error location(已知错误位置) |
| Rubber Duck | Logic errors(逻辑错误) |
| Delta Debug | Recent breakage(近期破坏) |
例如二分查找定位未知 bug:注释掉一半代码,测试 bug 是否仍存在,据此确定 bug 所在半区,反复缩小范围直到隔离。最小复现策略则主张:新建最小项目、只保留能复现 bug 的代码、逐个移除依赖、把输入简化到最小失败用例。
高频 bug 模式识别(common-patterns.md)
| Pattern | Symptom | Likely Cause |
|---|---|---|
| Race condition | Intermittent failures | Missing await, async timing |
| Off-by-one | Missing first/last item | <vs<=, array bounds |
| Null reference | "undefined is not..." | Missing null check |
| Memory leak | Growing memory | Uncleaned listeners/intervals |
| N+1 queries | Slow with more data | Fetching in loop |
| Type coercion | Unexpected behavior | ==instead of=== |
| Closure issue | Wrong variable value | Loop variable capture |
| Stale state | Old value used | React state closure |
常见修复范式(quick-fixes.md)也可作为 Phase 4 的直接参考,例如空引用用可选链user?.profile?.name ?? 'Unknown'、异步失败加.catch()或 try/catch、无限递归补递归基例等。注意:quick-fixes 是理解问题后的修复手段,绝不能反过来"先套修复再找问题"——这与系统性调试的第一原则相矛盾。
在 claude-skills 中的定位与触发方式
debugging-wizard 在仓库中归属 quality(质量)领域,其 frontmatter 定义的触发词包括:debug, error, bug, exception, traceback, stack trace, troubleshoot, not working, crash, fix issue。这意味着只要请求中涉及报错排查、崩溃分析、日志关联、根因定位,SKILL.md 就会被自动激活,并按场景加载上述参考文档。
它的核心工作流(Reproduce → Isolate → Hypothesize and test → Fix → Prevent)与本篇四阶段方法论一一对应;其 Output Templates 要求每次调试输出 Root Cause / Evidence / Fix / Prevention,确保调试结论可审计、可复用。
在项目级工作流中,bug 修复链路被编排为:
Bug Investigation: Debugging Wizard → Framework Expert → Test Master → Code Reviewer【免费下载链接】claude-skills67 Specialized Skills for Full-Stack Developers. Transform Claude Code into your expert pair programmer.项目地址: https://gitcode.com/GitHub_Trending/claud/claude-skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考