bug-debugging-playbook
Use when debugging a reproducible or intermittent bug, regression, crash, freeze, flaky test, failing build, or production incident where symptoms, environments, logs, recent changes, or root cause ne
SKILL.mdbug-debugging-playbook/SKILL.md
GitHubBug 调试证据链手册
中文简介
Bug 调试证据链手册:把异常症状转为可复现案例或可关联观测,再用可证伪假设和单变量实验确认根因,最后交付最小修复、回归门禁与剩余风险。它不把“最可能的代码”当作根因,也不以临时缓解冒充永久修复。
使用背景
用于崩溃、接口错误、数据异常、卡死、回归、间歇性故障、失败构建和 flaky test。它消费症状、期望/实际行为、复现步骤、日志/trace、时间与数据、环境、近期变更和代码库证据,产出可复现案例或观测增强方案、假设日志、根因证据、最小修复与回归门禁。
在只有 Issue、尚未判断是否为 Bug 或需要安全推进实现时,可先用 issue-to-pr;陌生仓库没有入口或测试地图时,先用 codebase-onboarding。它不是性能容量规划、纯代码风格审查或在没有症状的情况下重构。
核心原则
- 先分离症状、影响和机制:症状是观察到的差异,根因是被证据支持的机制;两者不能互相替代。
- 每个
[事实]回链到命令、日志、trace、指标、版本、代码或用户原话;[推断]写依据与验证动作;[假设]写可证伪预测;未知不补造。 - 在代码修改前先得到最小复现,或明确记录为何不能复现并增加观测。没有证据门槛,不做猜测性修复。
- 一个实验只改变一个可控变量,并预先写明预期信号、反证信号和恢复方式;不要并行修改多个“可能点”。
- 缓解措施降低影响或采集证据,根因修复解释并移除故障机制。两者必须分别标记和验证。
- 最小修复由最小可证明行为决定,而不是最少行数;回归门禁必须覆盖原始症状、邻近不变量和受影响环境。
适用场景
- 接口在特定数据、权限、版本或环境下返回错误、超时或错误结果。
- 用户旅程崩溃、卡死、重复提交、状态错乱,且有日志、会话、trace 或可补齐的观测点。
- 测试间歇失败、回归或构建失败,需要区分 fixture、时序、隔离和真实产品行为。
- 线上事故需要先减小用户影响,同时建立时间线、环境差异和变更范围来确认根因。
- 已有近期变更、版本范围或 last-known-good,可用变更二分缩小责任范围。
不适用场景
- 没有异常、期望行为或影响,只想“顺手看看”代码时;使用
code-review-assistant或refactor-with-safety。 - 需要从产品反馈判断是否应改变需求时;使用
user-feedback-synthesizer、metric-diagnosis或prd-builder。 - 已确认修复目标、验收、测试位置且只需实施和交付 PR 时;使用
issue-to-pr。 - 需要为已知行为设计覆盖矩阵而非排障时;使用
test-generator。
输入要求
保留输入来源。最少收集以下内容;缺失项标为 [未知] 并说明其阻断的判断:
- 症状与影响:谁受影响、频率、严重度、开始/结束时间、期望与实际行为。
- 可复现材料:步骤、最小请求/数据、前置状态、失败信号、成功对照和复现率。
- 证据:脱敏日志、request/session/trace ID、堆栈、指标、截图、录屏和采集时间。
- 环境:应用、浏览器/设备、操作系统、区域、账户/权限、配置、feature flag、依赖与数据版本。
- 时间线与变更:首次出现、last-known-good、部署、配置、schema、依赖、流量或数据变化。
- 约束:允许的环境、数据处理/脱敏要求、变更窗口、回滚能力、代码库和测试访问权限。
信息不足时的处理
- 没有期望或影响: 只交付澄清问题、已知症状和停止条件,不宣称这是 Bug 或给出修复。
- 不能稳定复现: 记录每次尝试的变量和结果;建立最小观测增强(关联 ID、时间、版本、状态转换、耗时、错误边界和采样),定义采样窗口、隐私边界、owner 与触发条件。此时可给出可回退的缓解措施,但不能宣称根因已确认或合入永久修复。
- 环境不全或表现不同: 先输出环境差异矩阵,优先验证能解释差异的一个变量;不要把本地成功、staging 失败直接归因于代码。
- 缺日志但有近期变更: 记录变更候选和时间线;若前后版本可运行,使用受控变更二分。二分只缩小候选范围,仍需机制证据确认根因。
- 用户要求先改“最可能”代码: 将请求列为
[假设],要求最小复现或观测、可证伪预测和单变量实验;若不满足,停止代码修改并交付调查或缓解计划。
工作流
- 保护现场并定义症状。 记录工作区状态、当前版本、时间范围、影响、期望/实际和已知未受影响对照;不覆盖现有用户改动。
- 建立证据链与时间线。 给日志、命令、trace、指标、截图和代码阅读分配
E-*;按发生、部署/配置/数据变化、首次观察和恢复排列,标明采集时间与时区。 - 做最小复现。 消去与故障无关的步骤、数据和并发,保留最少前置状态与失败信号;记录复现率和成功对照。无法复现则转入观测增强,不进入猜测性代码修复。
- 比较环境与输入。 用矩阵比较成功/失败样本的版本、flag、权限、数据、时间、浏览器/设备、依赖和网络;每次只选一个解释力最高的差异验证。
- 建立假设日志。 每个
H-*写机制、支持/反对证据、优先级、预测、实验和判定条件。优先级由影响、可解释证据、可测试性和恢复风险决定,不由直觉决定。 - 运行单变量实验。 为
X-*固定其他变量,变更一个输入、配置、版本、时间序列或隔离条件;记录前后信号、反证和可恢复性。实验失败也保留为证据。 - 二分变更和检查机制。 若时间线指向近期变更,用版本、配置或数据变更二分缩小范围;随后读取最小调用链,验证候选机制怎样从输入产生症状。二分命中不是根因确认。
- 确认根因或明确未确认。 只有当机制同时解释症状、环境差异和复现,并由独立证据或反向实验支持时,标记为
[已确认根因];否则保持[未确认假设]。 - 实施最小修复或缓解。 先写会失败的回归测试或等价可观察断言,确认红灯原因;一次只改变当前根因所需责任。缓解措施要写范围、失效条件和撤除标准。
- 回归验证与交接。 运行原始复现、根因测试、邻近不变量、受影响环境的构建/类型/lint/集成或人工检查;交付证据、回滚、监控和剩余风险。
专业判断规则
证据、症状与根因
| 结论状态 | 最小要求 | 禁止表述 |
|---|---|---|
[事实] |
可定位来源、时间、环境或命令结果 | 用截图印象替代原始观察 |
[推断] |
至少一个事实依据与下一步验证 | 当作已确认根因 |
[假设] |
可证伪预测、单变量实验和反证条件 | “最可能所以直接改” |
[已确认根因] |
机制解释复现/差异且有独立验证 | 仅因改动后暂时不报错 |
[缓解措施] |
减少影响的证据、范围、撤除条件 | 标记为永久修复 |
- 最小复现必须同时写前置状态、输入、执行动作、失败信号和成功对照。只有错误日志而没有可控制输入时,称为“可关联观察”,不是“可复现”。
- 环境差异表只比较会影响机制的变量;不同项没有因果证据前仍是候选。生产数据、令牌、个人信息和凭据必须脱敏。
- 同一轮实验不同时改代码、依赖版本、feature flag 和测试 fixture。若必须改变多个变量,先声明该实验只能生成线索,不能确认因果。
- 变更二分的候选集合需要明确基线、目标、每步版本/配置、运行环境与失败信号;数据漂移、不可重复构建或外部服务变化会使二分结论降级。
修复与回归门禁
- 在根因未确认时,代码改动只能作为明确标记的可回退缓解;不得把它写成“已修复”。
- 最小修复只触及证明机制所需的输入校验、状态转换、错误边界、幂等控制或资源释放责任;重构、格式化、依赖升级需另列并获得确认。
- 回归测试先验证原始症状,再固定根因不变量和一个未受影响对照。若不能自动化,写明人工步骤、环境、观察窗口、owner 与自动化缺口。
- 每个“已验证”都记录工作目录、完整命令或动作、执行时间、退出码/观察、关键脱敏输出和
E-*位置;[未运行]只写阻断原因,不写通过。
输出契约
按 references/templates.md 交付;没有信息也保留状态:
- 调试卡:症状、影响、期望/实际、范围、停止条件和事实/推断/假设/未知账本。
- 证据链与时间线:
E-*、来源、采集时间、环境、关联 ID、脱敏状态和结论边界。 - 最小复现或观测增强:步骤/信号/对照/复现率,或采集字段、采样、隐私、owner 与停止条件。
- 环境与数据差异矩阵:成功/失败样本及下一轮只验证的一个变量。
- 假设日志与单变量实验:
H-*、X-*、优先级、预测、反证、结果和后续。 - 变更二分与机制证据:基线、候选范围、每步结果、最小调用链和确认状态。
- 根因与缓解决策:明确区分
[已确认根因]、[未确认假设]和[缓解措施]。 - 最小修复与回归门禁:红灯、最小改动、绿灯、原始复现、邻近不变量、环境验证、回滚/监控和剩余风险。
质量门槛
- 不把症状、日志关键词、最近改动或“最可能文件”直接写成根因。
- 不在没有最小复现或观测增强计划时修改代码,也不同时试多个猜测性改动。
- 不遗漏成功对照、环境差异、数据/时间线或会改变结论的未知项。
- 不把变更二分结果、测试暂绿或影响下降单独当作根因确认。
- 不以“已测试”替代带
E-*的命令/观察证据;无法运行的门禁明确为未运行和阻断。 - 不合入没有原始症状覆盖、邻近不变量、回滚/缓解边界与剩余风险的修复结论。
常见失败与修正
| 失败 | 修正 |
|---|---|
看见 500 就在 controller 加 catch |
先保留请求/数据/版本证据,验证错误来源与期望映射;catch 只能是标记的缓解。 |
| 本地正常、staging 失败就断言部署代码坏了 | 建环境差异矩阵,一轮只验证一个能解释差异的变量。 |
| 卡死不能复现仍改“可能死循环”的代码 | 补状态转换、长任务、错误边界和会话关联观测;先确定采样与停止条件。 |
| 一次改重试、超时、缓存和 fixture | 拆为 X-* 单变量实验;多变量结果只作为线索。 |
| 二分到某个提交就宣布根因 | 阅读最小调用链,用反向实验确认机制并排除数据/环境漂移。 |
| 降低重试次数后事故减少就关闭问题 | 标记缓解,保留重复请求机制、撤除条件和永久修复验证。 |
参考资料
references/usage-guide.zh.md:中文独立调用方式、输入准备和上下游衔接。references/templates.md:调试卡、证据/实验账本、根因/缓解和回归门禁模板。references/checklists.md:输入、证据、实验、修复和交接质量门。references/examples.md:稳定接口错误、偶发前端卡死和拒绝先改猜测代码的示例。