debug · git:20260817.d4dfa41 · 2026-08-17 · sha256 630ed39a9fb87d2c

debug git:20260817.d4dfa41A

Immutable. This exact content is served forever at /api/v1/blob/630ed39a9fb87d2c.

---
name: debug
description: 排查 bug 时用。顺序是硬的:先复现、再让证据淘汰假设、修根因、用原始复现路径验证。不要看到第一个可疑点就动手改。
---

# 调试

顺序是硬的:**复现 → 定位 → 修 → 验证**。跳过复现直接改,修完也不知道
修没修好;「看起来像这个问题」的改动堆几层,代码比原来更难懂。

## 1. 复现

- 拿到最小复现路径:什么输入、什么操作、期望什么、实际什么。
- 按用户的描述复现不出来时,先对齐环境差异(版本、配置、数据),
  不要按「大概是这个意思」开修。
- 间歇性的 bug 先想并发、时序、未初始化、缓存。复现率不到 100% 也要
  拿到一个「多跑几次必现」的脚本——没有它,最后没法证明修好了。

## 2. 定位:让证据淘汰假设

- 看到症状先列 2-3 个候选假设,再找**能区分它们**的证据——不是找
  支持第一个假设的证据。
- 二分收敛:注释掉一半、在中间点打日志、`git bisect`。每一步的目标
  是砍掉一半可能性,而不是「再看一眼」。
- `[约束]` 打日志要打**变量的值**,不是「到这里了」。执行路径只占
  bug 的少数,多数 bug 错在数据不在路径。
- git 历史是证据:`git log -p -- 那个文件` 看这行什么时候变的、当时
  的提交想干什么。「以前是好的」这类 bug,bisect 比读代码快得多。

## 3. 修

- 修**根因**,不是在症状处打补丁。上游给了空值,在下游判空只是把
  炸点挪远了,还把上游的问题盖住了。
- 最小改动。定位过程中看到的其它可疑点,记下来告诉用户,不要一起
  改——一起改就分不清哪个改动起了作用。

## 4. 验证

- 用第 1 步的**原始复现路径**再跑一遍,不是用「我觉得等价」的路径。
- 跑一遍相关测试,确认没修出新的问题。
- 收尾回答两个问题:**同类的地方还有吗**(同一个错误模式往往不止
  一处,grep 一把);**要不要加个测试守住这里**(这个 bug 能溜进来,
  说明现在没有测试拦它)。

## 调试自己刚写的代码时

嫌疑最大的不是代码,是自己对「输入长什么样」「这个 API 行为如何」的
假设。打印真实输入和中间值对着看,比反复重读代码快。