# Systematic Debugging

> 遇到任何 bug、测试失败、构建失败或意外行为时、在提出修法之前使用——按「根因调查 → 模式分析 → 假设验证 → 实施」四阶段排障，先找根因再动手；修 3 次不成就质疑架构。

- Skill: `lumiogames/systematic-debugging` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add lumiogames/systematic-debugging`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lumiogames/systematic-debugging/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: LumioGames (https://skillmd.com/u/lumiogames)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lumiogames/systematic-debugging

---


# 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`（第四阶段的失败测试）。修完在交回物里只写实际跑过的验证。

