| name | bm.systematic-troubleshooting |
| description | 系统化排查根因:4 阶段法(复现 → 缩小 → 假设 → 验证)。Use when something is broken, wrong, slow, weird, unexpected, intermittent, 出错, 报错, bug, 问题, 不对, 慢, 卡, 怪, 排查, 定位, debug, troubleshoot. 适用于代码 bug、产品异常、流程出错、数据不对——任何「事情没按预期发生」的场景。 |
| source | opencrew |
| version | 20260521.01 |
Skill: Systematic Troubleshooting(系统化排查)
先复现,再缩小,再假设,最后验证。绝不靠猜。 适用于所有「事情没按预期发生」的场景。
不只是代码 bug——产品流程异常、数据对不上、设备不工作、操作没反应、流程卡住,都适用。
核心原则
- 先证据,后假设:没复现就别开始修
- 缩小范围,不是放大:每一步都让问题边界更清晰
- 假设要可证伪:能用一个动作验证 yes/no
- 改最少的东西:一次只动一个变量
- 修了之后必须验证:否则可能只是掩盖
4 阶段法
阶段 1:复现(Reproduce)
目标:让问题可控地、稳定地、最小步数地出现。
关键动作:
- 找最小复现步骤(删掉所有非必要步骤)
- 记录环境差异:什么时候出/什么时候不出
- 不能稳定复现的:记录频率和条件,不要跳过
问题分类(决定下一步策略):
| 类型 | 特征 | 难度 |
|---|
| 必现 | 每次都出 | 低 |
| 条件触发 | 特定输入/状态/时间才出 | 中 |
| 偶发 | 时有时无,无规律 | 高(先想办法变成条件触发) |
| 一次性 | 出过一次再没出 | 最高(先收集证据,等再次出现) |
反模式:
- ❌ 没复现就开始改代码 → 改了不知道有没有用
- ❌ 复现一次就以为搞定了 → 偶发问题需要多次确认
阶段 2:缩小范围(Isolate)
目标:把"问题出在哪"的边界从大到小压缩。
方法:
| 方法 | 操作 | 适用 |
|---|
| 二分 | 砍一半看问题在不在 | 不知道在哪一段 |
| 切换 | 换数据/环境/设备/账号 | 怀疑环境 |
| 回退 | 退到一个已知好的版本 | 之前是好的 |
| 最小化 | 剥离到最小可复现 | 复杂系统 |
| 时间法 | git bisect / 翻历史 | 回归问题 |
追问 5 次"为什么":
- 表面:报错了
- 为什么报错?→ 函数返回 None
- 为什么是 None?→ 数据库查询没返回
- 为什么没返回?→ 字段名拼错了
- 为什么字段名拼错?→ 上周改了 schema 没同步
- 根因:变更管理流程缺失
修最深一层能修的,别修最表面那层。
阶段 3:假设(Hypothesize)
目标:基于证据列出 2-3 个最可能的根因,每个都能验证。
好假设的标准:
- ✅ 具体:"
config.json 里 port 字段是字符串 '8080' 而不是数字 8080"
- ✅ 可证伪:跑一个动作就知道对不对
- ❌ "可能是配置问题" — 太模糊
- ❌ "应该是缓存的问题" — 没证据
列假设按概率排序:
假设 1(最可能):A 改坏了 B
- 验证:跑 X 看 Y
- 证据来源:错误信息提到 A
假设 2:环境差异
- 验证:在 staging 复现
- 证据来源:本地好用,线上不好用
假设 3(兜底):第三方依赖
- 验证:检查依赖版本
- 证据来源:最近升级过
阶段 4:验证(Verify)
目标:确认假设对不对,然后再确认修复有效。
两步验证:
-
验证根因:用阶段 3 的检验动作,确认假设成立
- 假设错了 → 回阶段 3,换下一个假设
- 假设对了 → 进入修复
-
验证修复:
- 改最少的代码 / 配置 / 步骤
- 用阶段 1 的复现步骤再跑一次 — 必须不再触发问题
- 跑相关测试 / 周边场景 — 没引入新问题
- 用 bm.verification 给出完成报告
输出格式
排查过程要落档,方便用户跟进 / 后续复盘:
# 排查记录:{问题}
## 现象
- {一句话描述}
- 复现步骤:
1. ...
2. ...
## 环境
- {OS / 版本 / 配置}
## 排查时间线
### 阶段 1:复现 ✅
- 复现成功,必现
- 最小步骤:...
### 阶段 2:缩小
- 二分了 X,问题在 Y 模块
- 5why:... → 根因猜测:...
### 阶段 3:假设
1. ❌ A:验证后排除(证据:...)
2. ✅ B:成立(证据:...)
### 阶段 4:验证
- 修了 ...
- 复现步骤现在不再触发 ✅
- 周边测试通过 ✅
## 根因
{一句话}
## 修复
{改了什么,为什么这样改}
## 防再发
- {流程/检查项)
反模式
| 反模式 | 为什么错 | 正确做法 |
|---|
| 瞎改试运气 | 改对了不知道为啥,改错了浪费时间 | 先复现 + 假设 |
| 跳过复现直接猜 | 假设没证据,方向可能完全错 | 先稳定复现 |
| 一次改 5 个地方 | 不知道哪个起了作用 | 一次一个变量 |
| 改了能跑就不管 | 只是掩盖,可能再发 | 修根因,不修症状 |
| 看错误最外层 | 最外层是入口不是根因 | stack trace 从内往外读 |
| 不看现有 issue | 可能是已知问题 | 先搜 GitHub Issues / 历史排查记录 |
| 修完不验证 | 可能没修好或引入新问题 | 跑复现步骤 + 周边测试 |
非代码场景示例
场景:用户说"我的某个文件不见了"
- 复现:让用户描述具体哪个文件、什么时候发现、上次见到是什么时候
- 缩小:是真的删了还是搜不到?回收站/最近文件/git log?
- 假设:误删 / 重命名 / 同步出问题 / 路径变了
- 验证:每个假设对应一个查找动作
场景:会议没人来
- 复现:之前几次会议谁来了?
- 缩小:是这次特殊还是一直这样?同步发邀请的是谁?
- 假设:邀请没发到 / 时间冲突 / 主题没吸引力 / 没人觉得自己该来
- 验证:抽查几个人问情况
文件落点
- 排查记录 →
./working/troubleshoot-{topic}.md
- 重要问题的根因总结 → 归档到
./postmortems/{date}-{topic}.md