用途
Systematic Debugging 用于在遇到任何技术问题时进行系统化调试,包括测试失败、生产环境 bug、异常行为、性能问题、构建失败和集成问题。它的核心原则是:在尝试任何修复之前,必须先找到根本原因;只修复症状被视为失败。该技能提出一条「铁律」:没有先完成根本原因调查,就不允许提出修复方案。
主要能力
该技能把调试过程划分为四个必须依次完成的阶段:
-
根本原因调查:在尝试任何修复之前,仔细阅读错误信息与堆栈跟踪,记录行号、文件路径和错误码;稳定复现问题,明确触发步骤;检查近期变更(git diff、提交、新依赖、配置变更、环境差异);在多组件系统中,在提出修复前先在各组件边界添加诊断埋点,记录进入和离开组件的数据,验证环境与配置传递,检查每一层的状态,运行一次以收集证据,确定故障发生在哪一层,再针对该组件深入调查;当错误位于调用栈深处时,沿调用栈向上追溯坏值的来源,在源头而非症状处修复。
-
模式分析:在修复前先找模式。寻找同一代码库中类似的可工作代码;如果是在实现某种模式,完整阅读参考实现,不要略读;列出工作版本与故障版本之间的每一处差异,无论多小;理解依赖关系,包括所需的其他组件、设置、配置、环境以及所做的假设。
-
假设与测试:采用科学方法。形成单一假设,明确写出「我认为 X 是根本原因,因为 Y」;做最小改动来测试假设,一次只改变一个变量,不要同时修复多个问题;验证后再继续——成功则进入第四阶段,失败则形成新假设,不要在原有修复上继续叠加。当确实不懂时,应承认「我不理解 X」,不要假装知道,可以求助或进一步研究。
-
实施修复:修复根本原因而非症状。先创建失败测试用例(尽可能最简单复现,能自动化则自动化,没有框架则用一次性测试脚本),这是修复前必须完成的;然后实施单一修复,一次只做一个改动,不做「顺便」改进,不捆绑重构;最后验证修复——测试是否通过、其他测试是否被破坏、问题是否真正解决。如果修复无效,应停止并计数:尝试少于 3 次则回到第一阶段,用新信息重新分析;达到或超过 3 次则停止并质疑架构,不要在没有架构讨论的情况下尝试第 4 次修复。
使用方式
该技能以流程规范的形式使用,要求在每个阶段完成后再进入下一阶段。它列出了需要停止并回到流程的红旗信号,例如:「先快速修复,之后再调查」「先改 X 试试看行不行」「一次加多个改动再跑测试」「跳过测试,我手动验证」「大概是 X,我来修一下」「我不完全理解,但这样可能行」「再试一次修复」(在已尝试 2 次以上时)等。同时给出常见借口的对照表,例如「问题很简单,不需要流程」——简单问题也有根本原因,流程对简单 bug 很快;「紧急情况,没时间走流程」——系统化调试比反复猜测更快;「先试这个,然后再调查」——第一次修复就设定了模式,应从开始就做对;「确认修复有效后再写测试」——未经测试的修复不牢固,先写测试才能证明;「一次修多个节省时间」——无法隔离哪个起作用,还会引入新 bug;「参考太长,我按自己的方式改」——部分理解必然导致 bug,应完整阅读;「我看到问题了,让我修」——看到症状不等于理解根本原因;「再试一次修复」(在 2 次以上失败后)——3 次以上失败意味着架构问题,应质疑模式而非继续修复。
该技能还提示注意来自人类伙伴的信号,例如「那没有发生吗?」表示你未经核实就做了假设;「它会向我们展示……吗?」表示你本应添加证据收集;「停止猜测」表示你在不理解的情况下提出修复;「深入思考这个」表示应质疑根本问题而非症状;「我们卡住了?」(沮丧)表示你的方法不奏效。看到这些信号时应停止并回到第一阶段。
适用场景
适用于任何技术问题,尤其在以下情况更应使用:时间压力大(紧急情况容易诱使人猜测)、「只需一个快速修复」看起来很明显、已经尝试过多次修复、之前的修复没有奏效、尚未完全理解问题。也不应因为问题看似简单、自己很着急或管理者要求立刻修复而跳过流程。
限制与边界
该技能强调,如果系统化调查后发现问题是真正的环境性、时序相关或外部问题,则视为已完成流程:记录调查内容,实施适当的处理(重试、超时、错误消息),并添加监控/日志以便未来调查;但 95% 的「没有根本原因」情况其实是调查不完整。该技能还包含配套技术文档:root-cause-tracing.md(沿调用栈向后追溯 bug 以找到原始触发点)、defense-in-depth.md(找到根本原因后在多层添加验证)、condition-based-waiting.md(用条件轮询替代任意超时),并关联 test-driven-development(用于创建失败测试用例)和 verifying-before-completion(在声称成功前验证修复有效)等相关技能。