用途
Systematic Debugging 用于在遇到任何 bug、测试失败或异常行为时,在提出修复方案之前进行系统化调试。它适用于任何技术问题,包括测试失败、生产环境缺陷、异常行为、性能问题、构建失败和集成问题。尤其适用于时间压力大、看似只需“快速修复”、已经尝试过多次修复、之前的修复无效或尚未完全理解问题的情况。
核心原则
核心原则是:始终在尝试修复之前找到根本原因。症状修复是失败。违反流程的字面要求就是违反调试的精神。铁律是:没有根本原因调查就不修复。如果尚未完成第一阶段,就不能提出修复方案。
四个阶段
必须完成每个阶段才能进入下一个阶段。
第一阶段:根本原因调查
在尝试任何修复之前:仔细阅读错误信息,不要跳过错误或警告,它们通常包含确切的解决方案;完整阅读堆栈跟踪,注意行号、文件路径和错误代码。一致地复现问题,确认能否可靠触发、确切的步骤是什么、是否每次都发生;如果无法复现,则收集更多数据,不要猜测。检查最近的变更,包括 git diff、最近的提交、新依赖、配置变更和环境差异。在多组件系统中收集证据:当系统有多个组件(如 CI → 构建 → 签名、API → 服务 → 数据库)时,在提出修复方案之前,为每个组件边界添加诊断插桩,记录进入组件的数据、离开组件的数据、验证环境/配置传播、检查每一层的状态。运行一次以收集证据,显示在哪里中断,然后分析证据以识别失败的组件,再调查该特定组件。追踪数据流:当错误位于调用栈深处时,需要使用 superpowers:root-cause-tracing 进行向后追踪。快速版本:坏值从哪里来?什么用坏值调用了这里?一直向上追踪直到找到源头。在源头修复,而不是在症状处修复。
第二阶段:模式分析
在修复之前找到模式:找到可工作的示例,在同一代码库中定位类似的可工作代码,什么可工作的代码与损坏的代码相似?与参考实现对比:如果正在实现某个模式,完整阅读参考实现,不要略读,阅读每一行,在应用之前完全理解该模式。识别差异:可工作的和损坏的之间有什么不同?列出每一个差异,无论多小,不要假设“那不可能有影响”。理解依赖关系:这需要哪些其他组件?什么设置、配置、环境?它做了哪些假设?
第三阶段:假设与测试
科学方法:形成单一假设,清晰陈述“我认为 X 是根本原因,因为 Y”,写下来,要具体而不是模糊。最小化测试:对假设进行尽可能小的更改来测试,一次一个变量,不要同时修复多个东西。继续之前验证:它工作了吗?是 → 第四阶段;没有?形成新的假设,不要在顶部添加更多修复。当不知道时:说“我不理解 X”,不要假装知道,寻求帮助,做更多研究。
第四阶段:实施
修复根本原因,而不是症状:创建失败测试用例,最简单的复现,如果可能则自动化测试,如果没有框架则使用一次性测试脚本,必须在修复之前拥有。需要使用 superpowers:test-driven-development 来编写正确的失败测试。实施单一修复:解决已识别的根本原因,一次一个更改,不要“顺便”改进,不要捆绑重构。验证修复:测试现在通过了吗?没有其他测试被破坏?问题真的解决了吗?如果修复无效:停止,计数:已经尝试了多少次修复?如果少于 3 次:返回第一阶段,用新信息重新分析。如果大于等于 3 次:停止并质疑架构(见下文第 5 步)。不要在未进行架构讨论的情况下尝试第 4 次修复。
如果 3 次以上修复失败:质疑架构
表明架构问题的模式:每次修复都在不同地方揭示新的共享状态/耦合/问题;修复需要“大规模重构”才能实施;每次修复都在其他地方产生新症状。停止并质疑基本面:这个模式从根本上合理吗?我们是否“仅仅因为惯性而坚持它”?应该重构架构还是继续修复症状?在尝试更多修复之前与人类伙伴讨论。这不是失败的假设——这是错误的架构。
红旗信号——停止并遵循流程
如果发现自己想:“现在快速修复,稍后调查”、“只是尝试更改 X 看看是否有效”、“添加多个更改,运行测试”、“跳过测试,我会手动验证”、“可能是 X,让我修复它”、“我不完全理解但这可能有效”、“模式说 X 但我会以不同方式适应它”、“以下是主要问题:[列出修复而没有调查]”、在追踪数据流之前提出解决方案、“再试一次修复”(当已经尝试 2 次以上时)、每次修复都在不同地方揭示新问题。所有这些都意味着:停止。返回第一阶段。如果 3 次以上修复失败:质疑架构(见第四阶段第 5 步)。
人类伙伴的信号表明你做错了
注意这些重定向:“那没有发生吗?”——你没有验证就假设了;“它会向我们展示……吗?”——你应该添加证据收集;“停止猜测”——你在不理解的情况下提出修复;“深入思考这个”——质疑基本面,而不仅仅是症状;“我们卡住了?”(沮丧)——你的方法不起作用。当你看到这些时:停止。返回第一阶段。
常见借口
“问题很简单,不需要流程”——简单问题也有根本原因。流程对简单 bug 很快。“紧急,没有时间走流程”——系统化调试比猜测-检查的反复折腾更快。“先试试这个,然后再调查”——第一次修复设定了模式。从一开始就做对。“我会在确认修复有效后写测试”——未测试的修复不会持久。先测试证明它。“一次多个修复节省时间”——无法隔离哪个有效。导致新 bug。“参考太长,我会适应模式”——部分理解保证 bug。完整阅读它。“我看到问题了,让我修复它”——看到症状 ≠ 理解根本原因。“再试一次修复”(在 2 次以上失败后)——3 次以上失败 = 架构问题。质疑模式,不要再修复。
快速参考
第一阶段:根本原因——阅读错误、复现、检查变更、收集证据;成功标准:理解 WHAT 和 WHY。第二阶段:模式——找到可工作的示例、比较;成功标准:识别差异。第三阶段:假设——形成理论、最小化测试;成功标准:确认或新假设。第四阶段:实施——创建测试、修复、验证;成功标准:bug 解决、测试通过。
当流程揭示“没有根本原因”时
如果系统调查揭示问题确实是环境性的、时间依赖的或外部的:你已经完成了流程;记录你调查了什么;实施适当的处理(重试、超时、错误消息);添加监控/日志以供未来调查。但是:95% 的“没有根本原因”案例是不完整的调查。
与其他技能的集成
此技能需要使用:root-cause-tracing——当错误位于调用栈深处时必需(见第一阶段第 5 步);test-driven-development——创建失败测试用例时必需(见第四阶段第 1 步)。互补技能:defense-in-depth——在找到根本原因后在多层添加验证;condition-based-waiting——替换第二阶段中识别的任意超时;verification-before-completion——在声称成功之前验证修复有效。
实际影响
来自调试会话:系统化方法:15-30 分钟修复;随机修复方法:2-3 小时反复折腾;首次修复率:95% 对 40%;引入的新 bug:接近零 对 常见。