# Systematic Debugging

> 遇到 bug、测试失败、构建失败、异常行为、性能退化或修复无效时使用；在提出修复方案、改代码或加 workaround 之前执行。用于阻止猜测式修复，要求先复现、收集证据、定位根因、评估影响面。不用于已明确根因且用户只要求按指定内容改的机械修改。

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

---


# 系统化调试

目标：先证明根因，再做最小修复。任何“先试一下”“大概是”“改了再看”都算失败。

## 硬门禁

除紧急止血例外外，在满足下面 5 个门禁前，不要提出业务修复方案、不要改业务代码、不要加 workaround：

1. **复现门禁**：能说明复现命令/步骤、实际结果、期望结果；不能复现时先补日志或收集证据。
2. **证据门禁**：阅读完整的错误上下文、堆栈、失败断言和相关日志，覆盖能够影响根因判断的范围，并记录关键文件、行号、错误码或异常值。
3. **根因门禁**：能用一句话说明“根因是 X，因为证据 Y 指向 Z”，而不是只描述症状。
4. **影响门禁**：修复前评估要改的符号/接口/配置会影响谁；有 GitNexus 索引时必须先跑影响面分析。
5. **验证门禁**：修复前先有失败用例、最小复现或可重复验证命令；修复后必须运行它。

如果任一门禁缺失，先补门禁，不进入修复。

## 执行流程

### 0. 侦察代码地图（有 GitNexus 索引时）

优先用 GitNexus 建立 bug 周围的执行流地图：

1. 用错误关键词或业务术语查询相关流程与符号。
2. 查看疑似符号上下文，确认调用者、被调用者、所属执行流。
3. 需要精确链路时再用自定义图查询追踪调用链。

通过标准：能说出 bug 涉及的符号、执行流、直接上游和直接下游。

如果索引过期，先运行 `npx gitnexus analyze --index-only`；没有索引则跳过本阶段。工具参数见 `../gitnexus/references/toolkit.md`。

### 1. 根因调查

按顺序完成：

1. **读完整错误**：错误信息、堆栈、断言 diff、日志上下文、触发位置。
2. **稳定复现**：记录最小复现命令/步骤；不稳定时记录出现频率和条件。
3. **查近期变更**：检查当前 diff、最近提交、依赖/配置/环境变化。
4. **追踪数据流**：从错误值向上追踪到最早产生处；不要只在报错行修症状。
5. **跨边界取证**：多组件系统要在组件边界记录输入、输出、配置和状态；优先使用日志、调试器或请求捕获等观测手段，不改变业务行为。必须增加诊断代码时，应保持可逆、限制范围，并在定位后移除。

通过标准：能解释错误值从哪里来、在哪一层变坏、为什么会到达失败点。

反向追踪方法见 `references/auxiliary-techniques/root-cause-tracing.md`。

### 2. 模式对比

在修复前找一个正常样本：

1. 找同仓库或参考项目中相同模式的正常实现。
2. 阅读与当前执行流相关的完整实现，不要只看相似行。
3. 列出正常实现与失败实现的差异。
4. 标出依赖条件：配置、时序、缓存、权限、环境、第三方行为。

通过标准：能说出“正常路径满足 A/B/C，失败路径缺少或破坏了 D”。

### 3. 单假设验证

一次只验证一个假设：

1. 写出假设：`我认为根因是 X，因为证据 Y；如果成立，观察到 Z`。
2. 用最小实验验证：一条命令、一个日志点、一个断言或一个临时隔离改动。
3. 验证失败就撤回该假设，回到第 1 阶段补证据；不要在失败假设上叠加更多改动。

通过标准：假设被证据确认，且能指向一个明确修复点。

### 4. 修复前影响面

修复前先评估爆炸半径：

- 有 GitNexus 索引：对要修改的函数、类、方法、路由或关键符号做上游影响分析。
- 无索引：手动列出调用方、配置消费者、接口消费者和相关测试范围。

风险处理：

| 风险 | 判断 | 要求 |
| --- | --- | --- |
| LOW | 少量调用方，非关键流程 | 最小修复 + 相关验证 |
| MEDIUM | 多个调用方或 2 个以上流程 | 补回归测试 + 跑相关测试 |
| HIGH | 大量调用方、公共工具、核心状态 | 先说明风险和方案，再修复 |
| CRITICAL | 认证、支付、数据写入、权限、生产配置 | 先和用户确认方案 |

### 5. 最小修复与验证

1. 涉及可测试行为的 Bug 修复，先按 `test-driven-development` 创建并验证失败的回归测试；其他场景使用最小复现脚本或明确可重复的验证命令，并记录原因。
2. 只修已确认根因，不顺手重构，不捆绑无关优化。
3. 运行复现验证、相关测试和必要的 lint/typecheck/build。
4. 修复后进入最终验证阶段；若当前流程已由 TDD 或上层工作流衔接 `verification-before-completion`，由其统一执行，不重复启动；只能根据实际命令输出声明成功。
5. 有 GitNexus 索引时，修复后运行变更检测，确认影响范围符合预期。

如果修复失败：

- 修复验证失败：停止叠加补丁，回到第 1 阶段补充证据；若新证据与当前根因假设矛盾，重新评估数据流、模型和影响面。

## 紧急止血例外

只有生产环境全面中断、数据持续损坏或安全风险正在扩大时，才允许止血与根因调查并行。

止血必须同时满足：

1. 可逆：有开关、回滚路径、超时上限或熔断边界。
2. 不冒充修复：明确标记为临时缓解。
3. 记录决策：写下采取了什么、为什么、待查根因是什么。
4. 立即继续调查：止血后回到第 1 阶段。
5. 有跟踪：设定后续验证或告警，避免临时方案永久化。

部分降级、单用户问题、有 workaround、普通测试失败不适用该例外。

## 常见绕过方式

出现这些想法时，立刻回到门禁：

- “先临时修一下，之后再排查。”
- “试着改 X 看看。”
- “一次改多个地方，跑了再说。”
- “看起来很简单，不用复现。”
- “我不完全理解，但应该能行。”
- “测试晚点补。”
- “参考实现太长，凭经验改。”
- “已经失败两次了，再试一次。”
- “有 GitNexus 但先不用。”
- “影响面分析太麻烦。”

## 团队压力下的做法

硬门禁是独处时的纪律。在真实团队中，你不可能每次都硬刚到底——**以下做法不是"退而求其次"，而是技能设计的正确用法。**

核心原则：**你不必赢下争论，但你必须在妥协前完成自己能控制的部分。** 即使团队决定先合入未经根因验证的变更，也必须明确记录其未验证状态，不得将其描述为已修复。

### 你的三件事（在妥协前必须完成）

1. **请求一个短时间盒**，不要争论。"给我 10 分钟验证 token 在 middleware 前后是否变化。" 把流程从"对错之争"变成"小实验"。
2. **用具体问题替代反对**。"这个状态在哪一层从 completed 变成 pending？" "middleware 是验签失败还是主动清除了 token？" 具体问题比"我们应该调查根因"更有说服力。
3. **记录风险**。"我担心在报错点兜底会掩盖上游字段丢失，建议至少跑 X 验证。" 即使现在不改，也让风险可见。

### 如果团队决定先合入

你已经完成了当前能做的短时间盒、具体问题和风险记录。现在：

- 接受团队决定，不继续僵持。
- 明确标记这个变更为"临时缓解，根因待查"，不得称为已修复。
- 创建跟踪 ticket，继续验证结果和根因补查。

**这就是技能的正确用法。** 你不是在"妥协"——你是在有纪律地走完自己能控制的部分，然后把团队决策交给团队。

## 输出格式

调试过程中对用户保持这个结构，简短即可：

```text
复现：<命令/步骤> → <实际结果>
证据：<关键日志/堆栈/差异>
根因假设：<X，因为 Y；预期观察 Z>
验证：<已跑/要跑的最小实验>
影响面：<要改的符号/文件，调用方与风险>
修复：<确认根因后的一句话方案>
验证结果：<命令与结果>
```

## 相关资料

- GitNexus 工具参数：`../gitnexus/references/toolkit.md`
- 反向根因追踪：`references/auxiliary-techniques/root-cause-tracing.md`
- 防御式修复：`references/auxiliary-techniques/defense-in-depth.md`
- 条件等待替代固定 sleep：`references/auxiliary-techniques/condition-based-waiting.md`

