--- name: systematic-debugging description: 遇到任何 bug、测试失败、构建失败或意外行为时、在提出修法之前使用——按「根因调查 → 模式分析 → 假设验证 → 实施」四阶段排障,先找根因再动手;修 3 次不成就质疑架构。 --- # systematic-debugging — 先找根因,再动手 ## 概述 随手改一处试试看,既浪费时间又制造新 bug;快速补丁只是盖住了真正的问题。 **核心原则**:先找到根因,再提修法。只修症状就是失败。 ``` 没做完根因调查,不提修法 ``` 第一阶段没走完,就不能提修法。 ## 何时用 任何技术问题:测试失败、bug、意外行为、性能问题、构建失败、集成问题。 **越是想跳过越要用**:时间紧、「就改一下试试」看着很显然、上一次修没生效、问题看着很简单。系统排障比瞎试更快——简单 bug 也有根因。 ## 四个阶段 一个阶段做完再进下一个。 ### 第一阶段:根因调查 1. **仔细读错误信息**——完整堆栈、行号、错误码。答案常常就在里面。 2. **稳定复现**——精确步骤、可靠触发。复现不了 → 继续收集数据,不猜。 3. **看最近改了什么**——git diff、最近提交、新依赖、配置、环境。 4. **在组件边界收集证据**(多组件系统:CI → 构建 → 签名,API → 服务 → 库):提修法之前,先记下每个组件的进出,跑一次,让证据说明**在哪一段**坏了——再去查那一段。 5. **沿数据流向上追**——坏值从哪来?沿调用链一路追到源头;在源头修,不在症状处修。完整技法见本目录 [root-cause-tracing.md](root-cause-tracing.md)。 ### 第二阶段:模式分析 - 在同一代码库里找类似但能工作的代码 - **通读**参考实现——略读只能得到片面理解 - 列出能工作的和坏掉的之间**每一处**差异,再小也列,不假设「这个不可能有关系」 - 弄清涉及的依赖、配置与假设 ### 第三阶段:假设与验证 - 陈述一个单一、具体的假设:「我认为根因是 X,因为 Y」 - 用**最小**改动验证它,一次只动一个变量 - 没生效 → 提新假设;**不在旧改动上叠新改动** - 有不懂的地方就说「我不理解 X」,去查——不装懂 ### 第四阶段:实施 1. **先有失败的复现**:能自动化的,先写一个能复现的失败测试(用 `test-driven-development`)——最简复现,修之前必须存在;不能自动化的,用第一阶段的精确步骤复现症状,修完要能展示症状消失。 2. **只做一处修复**——针对根因的**一个**改动,不带「顺手」改进,不夹带重构。 3. **验证**:测试通过、其它测试没坏、症状确实消失;交回物里如实写跑了什么(没跑的写「未执行」)。 4. **修了没用?** 停。数一数尝试次数:不到 3 次 → 带着新信息回第一阶段;**3 次及以上 → 质疑架构**。 ### 修 3 次都失败:质疑架构 每次修复都在别处冒出新问题,或每次都要「大改」,说明模式本身可能是错的。**3 次是停止试错、升级诊断的程序阈值,不是「架构已经错了」的结论**——它只说明继续同样地修不划算。**停下,和用户讨论**,再决定要不要继续修。 计数按**同一个根因**算:三次尝试修的是同一处,才构成这个信号;三个互不相干的失败累加起来不是。 ## 红旗——停下,回第一阶段 - 「先快速修一下,之后再查」「先把 X 改了看看」 - 一次改多处;跳过失败测试 - 「大概是 X」「我不完全理解但这样可能行」 - 还没追数据流就提方案 - **已经试了 2 次以上,还想「再试一次」** - 用户在质疑你的做法(「验证过吗?」「别猜了」)——这是纠偏,不是噪音 | 借口 | 实际 | |------|------| | 「问题很简单,不用走流程」 | 简单问题也有根因;流程对它们本来就很快 | | 「紧急,没时间走流程」 | 系统排障比瞎试更快 | | 「先确认修法有效再补测试」 | 没测试的修复留不住。先失败测试才证明修好了 | | 「参考太长,我照着模式改」 | 片面理解必出 bug。通读 | | 「再试一次」(已试 2 次以上) | 同一根因 3 次没修好 = 停下升级,别再修症状 | ## 速查 | 阶段 | 做什么 | 完成标志 | |------|--------|----------| | 1. 根因 | 读错误、复现、看改动、收证据 | 知道**什么**坏了、**为什么** | | 2. 模式 | 找能工作的例子、逐项比对 | 差异列清 | | 3. 假设 | 单一理论、最小验证 | 被证实或被替换 | | 4. 实施 | 失败复现、单一修复、验证 | bug 消失、测试通过 | ## 查不出根因时 真是环境或时序问题:记录查了什么,做合适的处理(重试 / 超时 / 清晰的错误信息),加日志以便下次追。但先老实回答一句:四个阶段真的都走完了吗?「查不出根因」多数时候是调查没做完。 ## 配套技法 本目录:[root-cause-tracing.md](root-cause-tracing.md)(沿调用链向上追到源头,含定位「哪个测试污染了环境」的二分法)、[defense-in-depth.md](defense-in-depth.md)(找到根因后的多层校验)、[condition-based-waiting.md](condition-based-waiting.md)(用条件轮询替换随手写的等待时长)。 相关技能:`test-driven-development`(第四阶段的失败测试)。修完在交回物里只写实际跑过的验证。