关于 systematic-debugging
systematic-debugging 是用于处理缺陷、测试失败和异常行为的调试流程技能。它要求先复现问题、建立测试或等效验证手段并提出根因假设,再进行针对性修复。
主要功能
- 01定义并复现故障
- 02在系统边界收集证据
- 03一次验证一个根因假设
- 04修复前锁定故障
- 05修复后沿相同路径验证
适用场景
- 01调查失败的测试
- 02复现生产或本地缺陷
- 03追查间歇性故障或性能下降
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ tmdgusya/ engineering-discipline --skill systematic-debugging安装后在 Claude Code 中输入/systematic-debugging确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
condition-based-waiting-example.tscondition-based-waiting.mddefense-in-depth.mdfind-polluter.shroot-cause-tracing.md详细介绍
系统化调试
严格的调试工作流。遇到 Bug、测试失败或意外行为时使用。
三个核心目的:
- 修复根因,而非表象。
- 避免基于猜测进行修复。
- 修复前先用测试锁定故障。
硬性门槛
这些规则没有例外。
- 在获得可复现或可观察的状态之前,不要修复。
- 在陈述根因假设之前,不要修复。
- 在获得失败测试或等效复现机制之前,不要修复。
- 一次只验证一个假设。
- 修复时不得顺手重构。
- 如果三次修复尝试都失败,应先怀疑结构性问题,再应用其他补丁。
违反此流程即视为调试失败。
何时使用
在以下情况下使用此技能:
- 测试失败时
- 线上或本地出现 Bug 时
- 响应、状态、渲染或查询结果与预期不符时
- 调查性能下降、超时、竞态条件或间歇性故障时
- 某个问题至少修复过一次后再次出现时
以下借口不可接受:
- “看起来很简单,我直接修一下”
- “没时间了,先打个补丁再说”
- “可能是这个原因,我先改改看”
必需的输出约定
使用此技能时,必须在内部明确以下事项:
- 问题陈述:用一句话说明出了什么问题
- 复现路径:如何复现或观察故障
- 证据:实际观察到的结果
- 根因假设:你认为问题发生的原因
- 失败防护:以下其中一项:失败的测试、复现脚本或日志验证
- 修复:针对根因进行的一项修复
- 验证:修复后的复现路径和相关测试结果
缺少以上七项中的任何一项,都说明工作尚未完成。
工作流
按顺序执行以下步骤。
阶段 1. 定义问题
首先,简要概括问题。
- 预期行为是什么
- 观察到的行为是什么
- 影响范围是什么
- 问题总能复现,还是间歇性出现
输出格式:
Problem: <expected> but got <actual> under <condition>
不要把症状与猜测混在一起。
Good: Product detail API returns 500 when brand is null.
Bad: Serializer is broken because brand mapping seems wrong.
阶段 2. 复现或添加检测
修复前,你必须能够再次看到故障。
优先级:
- 使用现有测试复现
- 使用最小集成测试复现
- 使用单元测试复现
- 通过复现脚本或命令观察
- 添加日志或检测后观察
规则:
- 尽可能简化复现路径。
- 即使 Bug 只在 UI 中可见,也应尽可能优先在更低层复现。
- 对于间歇性故障,通过添加日志、捕获输入、时间戳和并发条件来提高可观察性。
- 如果无法复现,不要继续修复,而应提高可观察性。
无法复现时的处理方法:
- 记录输入值
- 检查环境差异
- 检查近期变更
- 在边界点添加日志
- 查找能产生相同症状的更小条件
阶段 3. 收集证据
只收集可观察的事实。
务必检查:
- 完整的错误消息和堆栈跟踪
- 失败时的输入值
- 最近修改过的文件或提交
- 环境/配置差异
- 调用路径和数据流
对于多组件问题,应检查每个边界。
示例:
- 控制器 -> 应用程序 -> 服务 -> 存储库
- 客户端 -> API -> 外部服务
- 调度器 -> 批处理服务 -> 数据库
在每个边界,检查:
- 输入了什么
- 输出了什么
- 哪些值发生了转换
- 在什么条件下会出错
在确定问题所在位置之前,不要修复。
阶段 4:隔离根因
只提出一个原因假设。
格式:
Hypothesis: <root cause> because <evidence>
好的假设具备以下特点:
- 指向单一原因
- 与观察到的证据相关联
- 可以通过小型实验证伪
不好的假设示例:
- “某处似乎存在一些异步问题”
- “整个序列化层似乎不稳定”
将原因追溯到源头。如果错误出现在调用栈深处,应追溯输入的来源,而不是症状。
阶段 5:锁定故障
修复前先锁定故障。
优先顺序:
- 自动化失败测试
- 在现有测试中添加回归用例
- 最小复现脚本
- 通过日志或断言进行临时验证
规则:
- 尽可能创建自动化测试。
- 修复前,测试必须失败。
- 修复后,同一路径上的测试必须通过。
- 测试名称必须揭示问题所在。
如果可以编写自动化测试,请同时使用 test-driven-development 技能。
阶段 6:实施单一修复
修复只针对一个假设。
允许:
- 直接解决原因的最小代码改动
- 验证所需的最小辅助改动
禁止:
- 捆绑多个看似相关的修复
- 将重构与修复合并
- 顺带进行格式调整、清理或重命名
- 在没有证据的情况下添加空值保护
- 吞掉异常
如果修复失败,请立即返回阶段 1 或阶段 3。此前的假设是错误的。
阶段 7:验证并收尾
收尾前必须满足以下所有条件:
- 原始复现路径不再失败。
- 新增的失败保护测试通过。
- 相关测试未被破坏。
- 你能解释该修复阻止的是原因,而非症状。
对于间歇性 Bug,仅通过一次还不够。必须在重复运行或不同条件下进行验证。
停止条件
出现以下情况时,请停止并重新审视问题。
1. 复现失败
如果多次尝试后仍无法复现:
- 检查可观测性是否不足。
- 检查环境是否存在差异。
- 检查问题定义是否有误。
禁止在无法复现的情况下修改代码。
2. 三次修复失败
如果连续三次修复都未能解决问题,则应得出结论:
- 当前理解有误,或
- 问题可能具有结构性,例如共享状态、边界设计、职责划分
从这时起,“第四次补丁”并不能解决问题,需要进行结构性讨论。
3. 没有失败保护
如果无法创建失败测试或等效的复现机制,就不要宣称任务已完成。至少应留下复现命令和观察结果。
危险信号
如果出现以下任何想法,请立即停止并返回前一阶段。
- “我只要改这一行,应该就能解决”
- “我稍后再看日志,先修复再说”
- “我之后再添加测试”
- “我把这两个问题一起修复吧”
- “错误消失了,所以不需要弄清原因”
最简检查清单
执行过程中使用此清单进行自我检查。
- 用一句话定义问题
- 复现问题或让故障变得可观测
- 收集证据
- 创建单一根因假设
- 修复前创建失败保护
- 只实施单一修复
- 修复后通过相同路径进行验证
完成标准
此技能的完成标准不是“代码已更改”。
完成标准:
- 问题定义清晰
- 修复前已锁定故障
- 修复与根因相关联
- 保留验证结果
若未满足这四项,调试就尚未完成。