📊 页面导航
适用角色与上手难度
🎯 学习产出: 掌握 Claude Code 调试方法和技巧,能独立用 4 阶段流程 + 5 种技能组合拳定位并修复各类 bug
🚀 AI 能力提升: 调试诊断
Bug 调试技巧 — 技能组合拳
预计阅读时间: 13 分钟
不是"试试改这里看看行不行"——是复现 → 隔离 → 假设 → 验证 → 修复,每一步有证据。
调试铁律
❌ 看到报错 → 猜原因 → 改代码 → 不行 → 再猜 → 再改
✅ 看到报错 → 复现 → 收集证据 → 列假设(按概率排序)→ 逐条排除 → 修复根因
第一条铁律:没有找到根因之前,不动代码。猜测不是调试。
四大核心技能
Superpowers 插件内置了完整的调试技能链,每个覆盖不同阶段:
系统化调试 4 阶段
来自 Superpowers systematic-debugging 技能,每次遇到 bug 自动激活:
阶段 1:根因调查
├─ 读懂错误信息(不是扫一眼,是逐行理解)
├─ 稳定复现(写最小复现步骤)
├─ 检查 git diff(这个 bug 是不是我刚刚引入的?)
└─ 追踪数据流(数据从哪来、经过谁、到哪去)
阶段 2:模式分析
├─ 找项目中类似功能的正确实现作为参考
├─ 对比"能跑的部分"和"报错的部分"的差异
└─ 缩小范围到具体的函数/模块
阶段 3:假设与验证
├─ 列假设表(按概率排序,至少 3 条)
├─ 逐条设计最小验证方案
├─ 验证一条 → 确认或排除 → 下一条
└─ 3 条都排除 → 重新回到阶段 1
阶段 4:实现修复
├─ 先写一个会失败的测试(复现 bug)
├─ 写最小修复让测试通过
├─ 跑全量回归
└─ 记录根因和预防措施
3 击规则
同一个 bug 尝试修复 3 次还没好 → 停下来,质疑架构。
第 1 次失败 → 回到假设表,检查是否排除了正确的假设
第 2 次失败 → 换个分析角度(从调用方看 vs 从被调用方看)
第 3 次失败 → 停下来,这可能是设计问题而非实现问题,需要人工判断
技能组合拳
真实调试中,单个技能不够——需要多个技能按阶段配合:
组合 1:快速修 bug(5 分钟内)
报错 → systematic-debugging 自动激活 → 4 阶段追溯 → 修复
→ verification-before-completion 拦停(跑测试确认)
→ /code-review 扫一眼有没有引入新问题
适用:报错信息清晰、stack trace 直接指向出问题的那行。
你做什么:把报错信息贴给 Claude Code,其他的自动走完。
> 这个接口报 500 了,日志:NullPointerException at UserService.java:42
> 帮我 debug
Claude Code 自动走 4 阶段 → 找到 NPE 根因 → 写测试 → 修复 → 验证 → 审查。
组合 2:深层 bug(根因离报错很远)
报错(深层)→ systematic-debugging 定位到症状
→ root-cause-tracing 沿调用栈逆向追踪
→ 生成 DOT 调用图,标注数据流向
→ 在源头修复(不是报错点)
→ reproducible-test-case 写成可复现测试
→ verification-before-completion 验证
适用:报错在 Controller 层,但根因在 Service → Repository → 数据库 schema 三层之下的某个隐式约定被破坏了。
> 订单接口偶尔返回 400,但不是每次。日志看不出原因。
> 帮我用 root-cause-tracing 追踪完整调用链。
组合 3:间歇性 bug(偶发、难复现)
偶发 bug → reproducible-test-case 设计复现条件
→ 找到稳定触发方式
→ systematic-debugging 正常追溯
→ 如果还是间歇的 → 加日志、加 trace、跑 N 次
→ /loop 跑 50 次测试直到复现
> 这个测试有时候绿有时候红,帮我找规律。
> 先跑 /loop run this test 50 times and collect every failure
组合 4:性能 bug
"这个接口慢" → 先测量(不是猜)
→ 加计时代码或用 APM 数据定位慢的环节
→ LSP: incomingCalls 找到谁在频繁调
→ codegraph: trace 数据流看有没有 N+1
→ 精确修复,再测量
→ verification: 确认修复后的耗时
> 订单列表接口 /api/orders 返回 2.3s,帮我定位瓶颈。
> 先用 codegraph trace 分析调用链,找到耗时最长的环节。
组合 5:前端渲染 bug
"页面白屏/渲染不对" → agent-browser: snapshot 确认当前 DOM 状态
→ agent-browser: screenshot 截图存证
→ LSP: diagnostics 检查类型错误
→ LSP: goToDefinition 追踪数据来源
→ Chrome DevTools: console 日志
→ 修复 → agent-browser: 重新截图验证
> 用户管理页面在筛选条件为空时白屏了。
> 帮我用 agent-browser 打开页面,截图确认当前状态。
> 然后用 LSP diagnostics 检查有没有类型错误。
假设表模板
每次调试都先列假设表——这是从"猜"到"科学排除"的关键一步:
> 对这个 bug 列出至少 3 条假设,按概率排序:
| # | 假设 | 概率 | 验证方法 | 验证结果 |
|----|------|------|---------|---------|
| 1 | 数据库连接池耗尽导致查询超时 | 40% | 查连接池监控 / 增加超时日志 | |
| 2 | 某个请求参数为 null 时 NPE | 30% | 加 null guard + 日志打印所有参数 | |
| 3 | 第三方 API 偶发超时未处理 | 20% | 加 try-catch + 超时配置 | |
| 4 | 缓存过期后雪崩 | 10% | 检查缓存 TTL 和预热逻辑 | |
每条假设验证完才到下一条。3 条全排除 → 回到阶段 1 重新收集证据。
实战案例
案例 1:NPE 但 stack trace 指向 JDK 内部
现象:接口报 NullPointerException,stack trace 最后一行在 HashMap.java:1234。
> 这个 NPE 的 stack trace 在 JDK 内部,看不出来业务代码哪里出问题。
> 用 root-cause-tracing 从 Controller 入口追踪完整数据流。
Claude Code 流程:
root-cause-tracing:
Controller.getUser(123)
→ UserService.findById(123)
→ UserRepository.findById(123) ← 返回 Optional.empty()
→ UserService 没检查 Optional,直接 .get()
→ NullPointerException
根因:findById 返回 Optional.empty(),调用方没处理。修复:加 orElseThrow()。
案例 2:API 偶尔 400,日志看不出
现象:/api/orders 偶尔返回 400,每次持续几秒后自动恢复,并发高时更频繁。
> 这个 400 是偶发的,帮我 debug:
> 1. 先用 reproducible-test-case 写一个高并发测试,模拟 50 并发请求
> 2. 用 /loop 跑 20 次,收集所有失败
> 3. 分析失败请求的 pattern
发现:高并发下数据库连接池耗尽(默认 10 连接,50 并发来了不够用)。根因是连接池配置太小,不是业务代码 bug。
案例 3:前端列表筛选后数据不更新
现象:用户选择筛选条件后,列表没有刷新。
> 用户管理页面:选择状态下拉 → 列表不刷新。
> 1. agent-browser: 打开页面,选择"激活"状态,截图
> 2. LSP: findReferences 找到 status filter 的所有引用
> 3. 检查 watch/watchEffect 的依赖是否正确
发现:watchEffect 里用了 filters.value.status,但筛选组件 emit 的是 filters.status(少了一层 .value)——两边的数据形状不一致导致静默失败。
配套工具速查
CLAUDE 调试法
一个记忆口诀——每一步的首字母拼成 CLAUDE:
不该做的事
相关页面
功能串联
调试的起点是 Superpowers 的 systematic-debugging 技能(自动激活),终点是 verification-before-completion 的强制验证。日常调试推荐组合:systematic-debugging(根因追溯) + root-cause-tracing(深层 bug) + /code-review(修复后审查)。遇到间歇性 bug 用 /loop 工程 跑 N 次直到复现。