diagnosing-bugs

产品介绍

diagnosing-bugs 是 Matt Pocock Skills 中「修 bug 流水线」的第 2 步,是一个系统化的 Bug 诊断循环 Skill。安装在 ~/.trae-cn/skills/diagnosing-bugs/SKILL.md,当用户说「诊断/调试这个」时 AI 自动调用。

为什么要用诊断循环

遇到 bug 时,人本能反应是「看代码 → 猜原因 → 改 → 测」。但这个冲动经常导致:

  • 改错了地方:真正的问题在别处,改的代码根本没用
  • 改出新问题:为修一个 bug 引入了另一个 bug
  • 修得不彻底:只解决了表面现象,过几天又出现

诊断循环强迫你先证明 bug 存在,再找原因,最后修。每一步都有证据支撑,不是靠猜。

6 步诊断循环

第 1 步:建反馈环

做什么:构造一个能复现 bug 的测试。

为什么是第一步:没有能复现的测试,后面所有步骤都是空中楼阁。你无法验证自己的假设,也无法验证修复是否真的有效。

完成标准:有一个测试,跑一次就失败,失败信息和线上问题一致。

例子:

// 用户报告:点击保存按钮偶尔报错
// 先写一个测试模拟这个操作
test('点击保存按钮应该成功', () => {
  // 模拟用户的操作
  user.click(saveButton);
  // 期望没有错误抛出
  expect(noError).toBe(true);
});

第 2 步:复现 + 最小化

做什么:确认能稳定重现,然后缩到最小场景。

为什么:「偶尔会崩」没法修。必须找到稳定的复现路径。而最小化是为了排除无关因素,让问题更清晰。

完成标准:每次运行都复现,且场景最小(去掉任何无关操作后就不再复现)。

例子:

// 原始场景:用户进入设置 → 打开通知面板 → 点击高级选项 → 保存 → 崩溃
// 最小化后:直接调用 save() 函数 → 崩溃
// 说明:进入设置、打开面板这些操作和崩溃无关

第 3 步:假设

做什么:生成 3-5 个排序的假设,按可能性从高到低排列。

为什么:不要只猜一个原因。多个假设可以并行验证,效率更高。排序让你优先验证最可能的,节省时间。

完成标准:有 3-5 个具体的假设,每个假设都对应一个可验证的预测。

例子:

用户报告:保存时偶尔报错

假设 1(最可能):并发请求导致竞态条件
  预测:连续快速点击保存按钮会触发

假设 2:后端接口偶尔超时
  预测:网络慢的时候更容易出现

假设 3:本地缓存数据格式不兼容
  预测:从旧版本升级来的用户更容易出现

假设 4:某个特定字段为空时触发
  预测:缺少某个必填字段时出现

第 4 步:打探针

做什么:每次改一个变量,验证假设。

为什么:一次改多个变量,你不知道哪个起了作用。每次只改一个,才能得出可靠的因果关系。

完成标准:每个假设都被验证或排除,有明确的结论。

例子:

验证假设 1(竞态条件):
→ 打探针:给 save() 加防抖,看是否解决问题
→ 结果:问题消失 → 假设成立!

验证假设 2(后端超时):
→ 打探针:看后端日志,有没有超时记录
→ 结果:没有超时记录 → 假设排除

第 5 步:修 + 回归测试

做什么:先写测试,再修代码。

为什么:先写测试保证修完后不会回归。如果先修再写测试,修完可能忘了怎么复现,测试就写不出来了。

完成标准:测试通过,且修复没有破坏其他功能。

例子:

// 先写回归测试
test('快速点击保存不应该导致竞态', () => {
  // 模拟快速点击 5 次
  for (let i = 0; i < 5; i++) {
    user.click(saveButton);
  }
  // 所有请求都应该成功
  expect(allRequestsSucceeded).toBe(true);
});

// 再修代码
function save() {
  // 加防抖,避免并发请求
  debouncedSave();
}

第 6 步:清理

做什么:移除调试代码,记录根因。

为什么:调试代码留在生产环境是隐患。记录根因是为了以后遇到类似问题能快速定位,也是给团队积累知识。

完成标准:调试代码已删除,根因已记录,测试已加入回归测试套件。

核心原则

  • 反馈环是核心:没有能复现 bug 的测试,就不要开始假设。先有测试,再有推理。
  • 每次改一个变量:避免一次改多个导致无法判断哪个有效。这是科学方法的体现——控制变量。
  • 假设要排序:不要只盯一个原因不放。多个假设并行验证,效率更高。

常见陷阱

  1. 跳过第 1 步直接猜原因:没复现就修,修完不知道有没有真的解决
  2. 一次改多个变量:不知道哪个起了作用,下次遇到还得重新来
  3. 只生成一个假设:钻牛角尖,万一猜错了浪费大量时间
  4. 修完不写回归测试:过几天又出现,还得重新定位
  5. 不记录根因:同样的问题以后还会犯

使用场景

  • 遇到难修的 bug 时,按这 6 步走
  • 避免「看一眼代码就猜原因」的冲动
  • 团队协作时,用统一的流程定位问题

与上下游的关系

  • 上游:triage 分流完后,ready-for-agent 的 bug 进入诊断循环
  • 下游:修完后,用 tdd 的方法写回归测试和补充新功能

相关笔记

相关日记