<!-- title: Claude Code调试排错五阶段全实战 -->

# Claude Code调试排错五阶段全实战：从首次报错到自动化诊断

> **学习目标**：掌握用 Claude Code 作为调试伙伴的系统化工作流——从错误观察、假设生成、证据收集、根因确认到回归测试，建立可复现、可审计的调试闭环。适合已具备 Claude Code 基础操作能力、希望将调试效率提升 3 倍以上的开发者。
>
> **适用人群**：有一定 Claude Code 使用经验的后端/全栈开发者，遇到过程序员日常最痛苦的场景——"线上 bug 排查两小时，修复三行代码"。
>
> **前置条件**：Claude Code 2.0+ 已安装并登录、Node.js 18+ / Python 3.10+ 环境、Git 版本控制、Vitest 或 Pytest 测试框架。

---

## 一、为什么"让 Claude 修 Bug"不如"让 Claude 理解 Bug"

绝大多数开发者第一次尝试用 Claude Code 调试时，会这样做：

```
❌ 直接粘贴错误信息，说"帮我修一下"
```

这种方式会得到一个 **看起来合理的补丁**，但很可能：
- 修复了表面症状而非根因
- 引入新的边界条件漏洞
- 无法通过不同输入路径复现

来自 dev.to 社区的生产环境调试统计表明：**直接要求修复的平均耗时反而比"先诊断后修复"长 40%**，因为补丁经常需要返工。

核心思维转换：

```
旧模式：错误 → 补丁 → 测试 → 返工 → 再次补丁 → ...
新模式：观察 → 假设 → 证据 → 确认 → 最小修复 → 回归测试
```

这不是纯粹的理论——每个阶段都有对应的 Claude Code 技巧和命令，下面逐一展开。

---

## 二、五阶段调试工作流总览

```mermaid
flowchart TD
    A["阶段1: 观察<br/>收集错误上下文"] --> B["阶段2: 假设生成<br/>列出3个可能根因"]
    B --> C["阶段3: 证据收集<br/>逐个验证/排除假设"]
    C --> D{"根因已确认?"}
    D -- 否 --> B
    D -- 是 --> E["阶段4: 最小修复<br/>精确改动确认的根因"]
    E --> F["阶段5: 回归测试<br/>确保bug不会重现"]
    F --> G{"测试通过?"}
    G -- 否 --> E
    G -- 是 --> H["调试完成 ✅"]

    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#f3e5f5
    style E fill:#e8f5e9
    style F fill:#fce4ec
    style H fill:#c8e6c9
```

---

## 三、阶段1：观察——收集完整的错误上下文

### 3.1 三要素输入法

不要一股脑粘贴整个项目代码。给 Claude Code **精确的三个输入**：

**输入1：错误信息 + 调用栈**

```bash
# 先复现错误，捕获完整输出
npm run test 2>&1 | tee /tmp/error-output.txt
# 或 Python 项目
pytest tests/test_checkout.py -v --tb=long 2>&1 | tee /tmp/error-output.txt
```

然后将结果提供给 Claude Code：

```
我在运行 checkout 流程测试时遇到了以下错误：

TypeError: Cannot read properties of undefined (reading 'map')
    at normalizeUsers (src/checkout/user-handler.js:15:32)
    at processOrder (src/checkout/order-processor.js:42:18)
    ...

这个错误出现在用户有店铺积分且同时使用促销折扣时。
```

**输入2：相关代码路径**

```bash
# 让 Claude Code 只读取相关的执行路径文件
# 而不是整个代码库
```

在 Claude Code 交互中：

```
请阅读以下文件，它们构成了出错的执行路径：
/read src/checkout/discount-calculator.js
/read src/checkout/credit-balance.js
/read src/models/order.js
```

**输入3：最近的变更历史**

```bash
# 查看出错模块最近的提交——这是最容易被遗忘的输入
git log --oneline -20 src/checkout/
```

将输出粘贴到对话中。很多生产 bug 的根因就藏在最近两周的提交里。

### 3.2 常见遗漏清单

| 遗漏项 | 后果 | 正确做法 |
|--------|------|----------|
| 只给错误码不给调用栈 | Claude 猜测出错位置 | 提供完整 stack trace |
| 没说触发条件 | 无法复现 | 描述"什么操作在什么情况下出错" |
| 忘记 git log | 忽略最近的引入变更 | 始终提供最近 20 条相关提交 |
| 给了整个代码库 | 注意力被稀释 | 只给执行路径上的文件 |
| 没说期望行为 | 只修复崩溃不修复逻辑 | 明确"应该怎样"vs"实际怎样" |

### 3.3 无错误信息的"行为异常"怎么观察

有时候 bug 没有清晰的错误栈——比如"周二下午下的订单有时显示错误的运费估算"。这时用 Claude Code 的搜索工具做依赖图谱：

```
我需要找出所有可能影响运费计算的代码路径。
请用 grep 搜索 "shipping_estimate"、"shippingEstimate"、"calculateShipping"，
帮我构建一个调用关系图：什么函数调用了什么函数。
```

---

## 四、阶段2：假设生成——列出三种可能根因

### 4.1 关键提示词模式

**不要问"哪里出错了"，而要问"可能是哪些原因"**：

```
基于以上错误和代码，请列出三个最可能的根因，按概率排序。
暂时不要修改任何代码——我想先理解所有可能的失败模式，再开始收集证据。

对每个假设，说明：
1. 为什么认为这个原因概率高
2. 需要什么证据来确认/排除
3. 如果是这个原因，预期的修复方向是什么
```

### 4.2 Claude Code 典型输出示例

```
假设1（概率：高）— 积分余额检查的竞态条件
  原因：余额在折扣计算之前读取，导致看到的是过时状态
  确认证据：在 creditBalanceCheck 和 applyDiscounts 之间加日志
  
假设2（概率：中）— 整数/浮点数精度问题
  原因：积分以分为单位存储，折扣以美元计算
  确认证据：检查 currency-helpers.js 中的类型转换
  
假设3（概率：低）— 空积分余额缺少 null 检查
  原因：stack trace 显示错误发生在 null 检查之前
  确认证据：测试无活跃余额用户下单场景
```

一旦有了假设列表，你就有了 **调试计划**——每个假设告诉你该收集什么证据。

### 4.3 避免锚定效应

**不要在第一个看起来合理的假设上就停下来**。Claude 会倾向于给出"最可能"的答案，但"最可能"不等于"正确"。强迫自己走完三个假设的评估流程。

---

## 五、阶段3：证据收集——用 Claude Code 工具逐个验证

这是 Claude Code 真正发挥威力的阶段。你不需要在终端和编辑器之间切换——所有诊断工具都在 Claude Code 内完成。

### 5.1 竞态条件验证

```
请帮我写一个诊断脚本：
1. 模拟特定条件（店铺积分 + 促销折扣同时应用）
2. 在每个步骤添加日志，显示 balance 对象的精确状态
3. 运行 50 次场景，看失败是否间歇性的
4. 输出成功次数、失败次数和失败时的状态快照
```

Claude Code 会生成类似这样的脚本：

```javascript
// diagnosis/race-condition-test.js
import { checkout } from '../src/checkout/index.js';

const results = { success: 0, failure: 0, snapshots: [] };

for (let i = 0; i < 50; i++) {
  const order = {
    userId: 'test-user',
    storeCredit: 5000,    // 50 美元积分（以分为单位）
    promoDiscount: 0.2,    // 20% 折扣
    items: [{ price: 10000, qty: 1 }]  // 100 美元商品
  };
  
  try {
    const result = await checkout(order);
    console.log(`Run ${i+1}: SUCCESS - balance after = ${result.remainingCredit}`);
    results.success++;
  } catch (err) {
    console.log(`Run ${i+1}: FAILURE - ${err.message}`);
    results.failure++;
    results.snapshots.push({ run: i+1, error: err.message });
  }
}

console.log('\n=== 诊断结果 ===');
console.log(`成功: ${results.success}/50`);
console.log(`失败: ${results.failure}/50`);
if (results.failure > 0) {
  console.log('失败快照:', JSON.stringify(results.snapshots, null, 2));
}
```

### 5.2 类型精度验证

```
请读取 src/utils/currency-helpers.js，
检查所有的除法和乘法操作——整个流程中是否一致地使用分为单位？
找出每一个类型转换点。
```

Claude Code 会用 Read 和 Grep 工具定位所有相关代码：

```bash
# Claude Code 内部执行的搜索
grep -rn "parseFloat\|parseInt\|Number\|Math.round\|Math.floor\|Math.ceil" src/utils/
grep -rn "\* 100\|/ 100\|\* 0.01\|/ 100" src/checkout/
```

### 5.3 使用 `/run` 保持上下文连贯

不要退出 Claude Code 去终端运行命令——用内置能力：

```
/run node diagnosis/race-condition-test.js
```

Claude Code 执行后自动将结果纳入对话上下文，无需手动复制粘贴。这种 **迭代式反馈循环** 是高效调试的核心：

```mermaid
flowchart LR
    A["假设"] --> B["生成诊断脚本"]
    B --> C["/run 执行"]
    C --> D["结果自动回传"]
    D --> E{"假设被确认?"}
    E -- 否 --> F["下一个假设"]
    F --> A
    E -- 是 --> G["进入阶段4"]

    style D fill:#e1f5fe
    style G fill:#c8e6c9
```

---

## 六、阶段4：根因确认——一句话说清楚再动手

### 6.1 强制清晰化提示词

**在写任何修复代码之前**，要求 Claude Code 用一句话陈述根因：

```
基于我们收集的证据，请：
1. 用一句清晰的中文陈述根因
2. 陈述正确的行为应该是什么
3. 陈述产生正确行为的最小改动是什么

不要开始写代码——等我确认后再动手。
```

### 6.2 好的根因陈述 vs 差的根因陈述

| ❌ 差的陈述 | ✅ 好的陈述 |
|-------------|-------------|
| "代码里有个 bug" | "积分余额在促销折扣计算之前从数据库读取，导致积分检查看到的是折扣前的总额" |
| "类型不匹配" | "积分以分为单位存储（整数），但折扣计算在美元空间进行（浮点数），两者之间缺少显式转换" |
| "加个 try-catch 就行了" | "当用户没有活跃余额时，creditBalance 字段为 null，但下游代码假设它至少是空数组" |

如果 Claude Code 无法清晰地陈述根因，说明你 **还没理解到足够深入的层次**，不应该开始写修复代码。

---

## 七、阶段5：回归测试——确保 bug 永远不再回来

### 7.1 TDD 式修复流程

来自社区实战的 **最佳实践**：先写捕获 bug 的测试，再写修复代码。

```
基于确认的根因，请按以下顺序操作：

1. 先写一个回归测试，它在当前代码下应该失败（因为 bug 还在）
2. 运行测试，确认它确实失败
3. 然后写最小修复
4. 再次运行测试，确认它通过
5. 运行完整测试套件，确保没有引入新问题

运行命令：npm test && npm run typecheck
```

### 7.2 完整的回归测试示例

假设根因是 `normalizeUsers` 函数没有处理 `undefined` 输入：

```typescript
// tests/normalize-users.test.ts
import { describe, expect, it } from "vitest";
import { normalizeUsers } from "../src/normalize-users.js";

describe("normalizeUsers", () => {
  // 回归测试：确保 undefined/null 输入不会导致崩溃
  it("当 API 返回空数据时，应返回空数组", () => {
    expect(normalizeUsers(undefined)).toEqual([]);
    expect(normalizeUsers(null)).toEqual([]);
  });

  // 回归测试：确保空名字被过滤
  it("应只保留有效的显示名", () => {
    expect(
      normalizeUsers([
        { id: "u1", name: "  张三  " },
        { id: "u2", name: "" },
        { id: "u3", name: null },
      ])
    ).toEqual(["张三"]);
  });

  // 正向测试：正常情况
  it("应正确 trim 和过滤名字", () => {
    expect(
      normalizeUsers([
        { id: "u1", name: "Alice" },
        { id: "u2", name: "  Bob  " },
      ])
    ).toEqual(["Alice", "Bob"]);
  });
});
```

修复代码（最小改动）：

```typescript
// src/normalize-users.ts
export type User = { id: string; name?: string | null };

export function normalizeUsers(users: User[] | null | undefined): string[] {
  // 核心修复：容忍缺失的 API 数据
  if (!Array.isArray(users)) return [];

  return users
    .filter((user): user is User & { name: string } =>
      typeof user.name === "string"
    )
    .map((user) => user.name.trim())
    .filter((name) => name.length > 0);
}
```

### 7.3 陷阱：不要停在 `users ?? []`

最常见的"浅层修复"是给一个默认值然后收工：

```typescript
// ❌ 这只修复了崩溃，但保留了 null 名字、空名字等问题
const list = users ?? [];
return list.map(u => u.name);  // 如果 name 是 null，这里会出问题
```

**关键原则：给 Claude Code 你想要的业务规则，而不只是你看到的异常。**

```
normalizeUsers 必须容忍缺失的 API 数据，并移除空白显示名。
先添加一个失败的 Vitest 测试用例，然后做最小修复。
最后运行 npm test 和 npm run typecheck。
```

---

## 八、实战案例：异步重试逻辑的间歇性 Bug

这是一个真实场景——某服务偶尔失败，重试机制应该兜底但似乎没有正确工作。

### 8.1 阶段1：观察

```
我们的支付服务偶尔超时，重试逻辑应该最多重试3次。
但日志显示：有时重试后成功，有时静默失败没有报错。
请先阅读 src/utils/retry.ts，了解当前的重试实现。
```

### 8.2 阶段2：假设生成

```
基于代码，列出三个可能导致"静默失败"的根因：
1. 最终错误被吞掉了（catch 块没有重新抛出）
2. 重试次数计数有 off-by-one 错误
3. delay 逻辑导致超时累积，触发外层的全局超时

不要修改代码，只分析。
```

### 8.3 阶段3：证据收集

```
请帮我写一个测试：
- 使用 mock 函数模拟"前2次失败、第3次成功"的场景
- 验证重试次数、等待间隔、最终成功
- 同时测试"3次都失败"的场景，验证最终错误是否被正确抛出
- 使用 vi.useFakeTimers() 控制时间
```

```typescript
// tests/retry.test.ts
import { describe, expect, it, vi, beforeEach } from "vitest";
import { retry } from "../src/retry.js";

describe("retry", () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

  it("前2次失败后第3次成功，应返回结果", async () => {
    const task = vi.fn()
      .mockRejectedValueOnce(new Error("timeout"))
      .mockRejectedValueOnce(new Error("timeout"))
      .mockResolvedValueOnce("success");

    const resultPromise = retry(task, { times: 3, delayMs: 1000 });

    // 推进时间让 delay 完成
    await vi.advanceTimersByTimeAsync(1000);
    await vi.advanceTimersByTimeAsync(1000);

    const result = await resultPromise;
    expect(result).toBe("success");
    expect(task).toHaveBeenCalledTimes(3);
  });

  it("全部失败时，应抛出最后的错误", async () => {
    const task = vi.fn()
      .mockRejectedValue(new Error("service unavailable"));

    const resultPromise = retry(task, { times: 3, delayMs: 1000 });

    await vi.advanceTimersByTimeAsync(5000);

    await expect(resultPromise).rejects.toThrow("service unavailable");
    expect(task).toHaveBeenCalledTimes(3);
  });
});
```

### 8.4 阶段4：根因确认

```
基于测试结果，用一句话陈述根因：
```

期望输出：

> "retry 函数在最后一次尝试失败后，catch 块没有重新抛出错误，导致错误被静默吞掉。正确行为是：最后一次失败后应 throw lastError 而不是隐式返回 undefined。"

### 8.5 阶段5：最小修复

```typescript
// src/retry.ts — 修复后的版本
export async function retry<T>(
  task: () => Promise<T>,
  { times, delayMs, sleep = defaultSleep }: RetryOptions
): Promise<T> {
  let lastError: unknown;

  for (let attempt = 1; attempt <= times; attempt += 1) {
    try {
      return await task();
    } catch (error) {
      lastError = error;
      if (attempt < times) await sleep(delayMs);
    }
  }

  // 关键修复：确保总是抛出最后的错误
  throw lastError instanceof Error
    ? lastError
    : new Error("Retry failed");
}
```

---

## 九、可直接复用的调试提示词模板

将以下模板保存到项目的 `CLAUDE.md` 或 `.claude/prompts/debug.md` 中：

```markdown
## 调试提示词模板

### 通用调试模板
目标：找到根因，添加回归测试，用最小的有效 diff 修复 bug。

**观察：**
- 完整错误信息：
- 复现步骤：
- 期望行为：
- 实际行为：
- 最近变更的文件：

**约束：**
- 列出三个假设后再编辑代码
- 避免大范围重构
- 不要用 any 隐藏类型错误
- 完成前移除临时日志
- 最后运行 npm test 和 npm run typecheck

**报告：**
- 根因
- 变更的文件
- 添加的回归测试
- 残余风险

### 间歇性 Bug 模板
这个 bug 不是每次都出现。请帮我：
1. 写一个诊断脚本，运行 50 次来确认失败率
2. 分析并发/时序相关的代码路径
3. 使用 mock 隔离外部依赖
4. 不要做全局重构

### 行为异常模板（无错误信息）
现象：[描述不正确的行为]
没有报错信息。请帮我：
1. 搜索所有相关的代码路径
2. 构建调用关系图
3. 找出可能影响该行为的所有条件分支
4. 添加日志来追踪执行路径
```

---

## 十、七种原子调试任务模式

来自 Ralphable 博客的实战总结——将复杂调试拆分为 **带通过/失败标准的原子任务**，让 Claude Code 可以自主运行而不需要反复确认：

```mermaid
flowchart TD
    subgraph 原子任务模式
        T1["模式1: 日志分析<br/>输入: 日志文件<br/>输出: 异常行 + 时间线"]
        T2["模式2: 最小复现<br/>输入: 错误描述<br/>输出: 复现脚本"]
        T3["模式3: 二分定位<br/>输入: 首次出现的版本范围<br/>输出: 引入bug的具体commit"]
        T4["模式4: 类型审计<br/>输入: 出错的类型链<br/>输出: 类型不匹配的位置"]
        T5["模式5: 性能剖析<br/>输入: 慢查询/慢函数<br/>输出: 瓶颈函数 + 耗时"]
        T6["模式6: 依赖检查<br/>输入: 报错的依赖<br/>输出: 版本冲突/缺失声明"]
        T7["模式7: 回归验证<br/>输入: 修复diff<br/>输出: 测试通过 + 无新失败"]
    end

    T1 --> T2
    T2 --> T3
    T3 --> T4
    T4 --> T5
    T5 --> T6
    T6 --> T7
```

每个模式的关键配置：

```yaml
# 模式1：日志分析
name: "analyze-logs"
prompt: |
  分析以下日志文件，找出所有异常和错误。
  输出格式：时间戳 | 级别 | 消息 | 可能的关联事件
pass_criteria: "输出包含至少3条异常记录和关联分析"
fail_criteria: "输出为空或只有INFO级别日志"

# 模式2：最小复现
name: "create-reproduction"
prompt: |
  基于以下错误描述，创建最小复现脚本。
  脚本应该是自包含的，不依赖外部服务。
pass_criteria: "脚本运行后能复现原始错误"
fail_criteria: "脚本运行成功或超时"

# 模式3：二分定位
name: "git-bisect-bug"
prompt: |
  使用 git bisect 在 v2.3.0（已知无bug）和 v2.5.0（已知有bug）之间
  找出引入 bug 的具体 commit。
  运行测试套件来判断每个 commit 是否有 bug。
pass_criteria: "找到单个引入 bug 的 commit"
fail_criteria: "无法确定或范围超过 5 个 commit"
```

---

## 十一、调试中的反模式与纠正

### 11.1 四大致命反模式

```mermaid
flowchart LR
    subgraph 反模式
        A1["🚫 粘贴海量上下文<br/>希望Claude自己找"]
        A2["🚫 不生成假设<br/>直接要修复"]
        A3["🚫 接受第一个建议<br/>不做验证"]
        A4["🚫 不反馈结果<br/>一次性对话"]
    end

    subgraph 正确做法
        B1["✅ 只给执行路径上的代码"]
        B2["✅ 先列3个假设再动手"]
        B3["✅ 用证据验证每个假设"]
        B4["✅ 迭代反馈，每轮缩小范围"]
    end

    A1 -.->|纠正| B1
    A2 -.->|纠正| B2
    A3 -.->|纠正| B3
    A4 -.->|纠正| B4
```

### 11.2 上下文管理的"金发女孩原则"

调试时的上下文量存在最佳区间：

| 上下文量 | 问题 | 解决方案 |
|----------|------|----------|
| 太少（只给错误码） | Claude 猜测，命中率低 | 补充调用栈 + 触发条件 |
| 适中（执行路径 + 最近变更） | **最佳区间** ✅ | 维持 |
| 太多（整个代码库） | 注意力被稀释 | 只提供相关文件 |

### 11.3 使用 `/compact` 保持上下文新鲜

在长时间调试会话中，对话历史会膨胀。适时压缩：

```
/compact
```

这会将之前的对话压缩为摘要，释放上下文窗口空间。**建议在每完成一个假设的验证后执行一次 `/compact`**，然后简要补充"已确认/已排除假设X"的结论。

---

## 十二、实战检查清单：每次调试必走

将以下清单保存为 Claude Code 可读取的上下文：

```markdown
## 调试检查清单

### 启动调试前
- [ ] 错误信息 + 完整调用栈已收集
- [ ] 复现步骤已确认
- [ ] 期望行为 vs 实际行为已明确
- [ ] 受影响文件的最近 20 条 git log 已获取
- [ ] 执行路径上的相关文件已用 /read 加载

### 假设阶段
- [ ] 至少列出了 3 个假设
- [ ] 每个假设有概率评估和验证方法
- [ ] 没有在第一个看起来合理的假设上就停下

### 证据阶段
- [ ] 对每个假设生成了诊断脚本或搜索命令
- [ ] 使用 /run 在 Claude Code 内执行诊断
- [ ] 结果已反馈到对话中

### 确认阶段
- [ ] 根因已用一句话清晰陈述
- [ ] 正确行为已定义
- [ ] 最小改动已明确

### 修复阶段
- [ ] 先写了回归测试，确认当前代码下测试失败
- [ ] 然后写了最小修复
- [ ] 修复后回归测试通过
- [ ] 完整测试套件通过
- [ ] 临时日志已移除
```

### 30 分钟规则

来自社区的最佳实践：

> **如果一个 bug 已经排查了 30 分钟，但你还没有确认的根因——从头开始。**

这不是开玩笑。30 分钟没有根因意味着你的假设方向错了。重启阶段1，重新收集上下文，重新生成假设。**重新开始感觉慢，但比在错误方向上继续要快得多。**

---

## 十三、进阶：自动化原子调试任务

对于可重复的调试模式，可以创建 Claude Code 子代理配置：

```json
// .claude/subagents/bug-analyzer.json
{
  "name": "bug-analyzer",
  "description": "分析错误日志并生成假设",
  "tools": ["Bash", "Read", "Grep", "Glob"],
  "prompt": "你是一个专业的 bug 分析师。分析给定的错误信息和代码上下文，列出三个最可能的根因，按概率排序。不要修改任何代码。对每个假设说明需要什么证据来确认或排除。"
}
```

```json
// .claude/subagents/test-first-fixer.json
{
  "name": "test-first-fixer",
  "description": "先写回归测试再修复 bug",
  "tools": ["Bash", "Read", "Edit", "Write", "Grep"],
  "prompt": "你是一个严格的 TDD 修复工程师。给定确认的根因，你将：1) 先写一个在当前代码下会失败的回归测试；2) 运行测试确认失败；3) 写最小修复；4) 运行测试确认通过；5) 运行完整测试套件确保没有回归。不要跳过任何步骤。"
}
```

在调试会话中使用：

```
请调用 bug-analyzer 子代理分析以下错误：
[paste error]

然后根据分析结果，调用 test-first-fixer 子代理进行修复。
```

---

## 十四、不同语言/框架的适配要点

| 框架 | 测试命令 | 类型检查 | 特殊考虑 |
|------|----------|----------|----------|
| TypeScript + Vitest | `npm test` | `npx tsc --noEmit` | 注意 `any` 类型隐藏的错误 |
| Python + Pytest | `pytest -v` | `mypy src/` | 注意动态类型的隐式转换 |
| Go | `go test ./...` | 编译器内置 | 注意 goroutine 竞态条件 |
| Rust | `cargo test` | 编译器内置 | 注意生命周期和所有权问题 |
| Java + JUnit | `mvn test` | 编译器内置 | 注意空指针和并发问题 |

对于每种框架，在提示词中明确告诉 Claude Code 使用对应的测试命令和类型检查命令。

---

## 十五、总结：调试效率的倍增器

```mermaid
graph TB
    subgraph 旧方式["旧方式：直觉驱动"]
        O1["看到错误"] --> O2["猜测原因"]
        O2 --> O3["尝试修复"]
        O3 --> O4["测试失败"]
        O4 --> O2
    end

    subgraph 新方式["新方式：证据驱动"]
        N1["收集上下文"] --> N2["生成3个假设"]
        N2 --> N3["收集证据逐个验证"]
        N3 --> N4["一句话确认根因"]
        N4 --> N5["先写测试再修复"]
        N5 --> N6["回归测试通过 ✅"]
    end

    style 旧方式 fill:#ffebee
    style 新方式 fill:#e8f5e9
```

**核心要点回顾**：

1. **不要跳过观察阶段**——精准的上下文输入决定调试质量的上限
2. **假设先行**——三个假设比一个猜测的命中率高三倍
3. **证据闭环**——每个假设都需要证据确认或排除
4. **一句话根因**——说不清楚就不是真正理解了
5. **测试优先修复**——先捕获 bug 的回归测试，再写最小修复

这套工作流经过 6 周以上的生产环境验证，平均诊断时间从 2 小时降至 40 分钟。关键不是 Claude Code 有多聪明，而是 **你如何引导它思考**。

---

## 参考来源

1. [Claude Code Debugging Workflow: Observation, Reproduction, Fix, Test — ClaudeCodeLab](https://claudecode-lab.com/en/blog/claude-code-debugging-techniques/) — 完整的五阶段调试工作流，含 TypeScript/Vitest 实战代码
2. [Claude Code Debugging Workflow: How I Diagnose and Fix Production Issues 3x Faster — DEV Community](https://dev.to/nextools/claude-code-debugging-workflow-how-i-diagnose-and-fix-production-issues-3x-faster-2l11) — 生产环境调试的四阶段工作流，含假设驱动方法论
3. [Claude Code Autonomous Debugging in 2026: 7 Atomic Task Patterns — Ralphable](https://ralphable.com/blog/claude-code-autonomous-debugging-atomic-tasks) — 七种带通过/失败标准的原子调试任务模式
4. [Claude Code Cheat Sheet - Commands, Shortcuts, Tips — ComputingForGeeks](https://computingforgeeks.com/claude-code-cheat-sheet/) — Claude Code 2.1.x 完整命令速查表，含斜杠命令和 CLI 标志
5. [Advanced Best Practices - Debugging Guide — SFEIR Institute](https://institute.sfeir.com/en/claude-code/claude-code-advanced-best-practices/debugging/) — Claude Code 内置诊断工具的高级使用指南

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
