Skills systematic-debugging

Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes - four-phase framework (root cause investigation, pattern analysis, hypothesis testing, implementation). NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST.

install
source · Clone the upstream repo
git clone https://github.com/huangwb8/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/huangwb8/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/awesome-code/agents/systematic-debugging" ~/.claude/skills/huangwb8-skills-systematic-debugging && rm -rf "$T"
manifest: awesome-code/agents/systematic-debugging/SKILL.md
source content

Systematic Debugging - 系统化调试

与 bensz-collect-bugs 的协作约定

  • 因本 skill 设计缺陷导致的 bug,先用
    bensz-collect-bugs
    规范记录到
    ~/.bensz-skills/bugs/
    ,不要直接修改用户本地已安装的 skill 源码;若有 workaround,先记 bug,再继续完成任务。
  • 只有用户明确要求“report bensz skills bugs”等公开上报时,才用本地
    gh
    上传新增 bug 到
    huangwb8/bensz-bugs
    ;不要 pull / clone 整个仓库。

铁律

NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST

违反规则的信件就是违反规则的精神。

无例外

  • 不跳过根因调查直接修复
  • 不基于猜测进行修复
  • 不尝试多个更改同时测试
  • 不在不完全理解的情况下修复

常见合理化

借口现实
"快速修复现在,稍后调查"永不会有"稍后"。技术债积累
"尝试改变 X,看是否有效"猜测不是调试。可能碰巧修复但未理解
"添加多个更改,运行测试"无法确定哪个更改有效。浪费调试时间
"可能大概差不多是 X"模糊理解导致错误修复
"不完全理解但可能有效"理解不完全=修复不完整

红色标志 - 停止并重新开始

  • "快速修复现在,稍后调查"
  • "尝试改变 X,看是否有效"
  • "添加多个更改,运行测试"
  • "可能大概差不多是 X,让我修复"
  • "不完全理解但可能有效"
  • 基于猜测而非证据的修复

所有这些意味着:停止修复。回到根因调查阶段。


四阶段框架

阶段 1:根因调查

任何修复前

  1. 仔细阅读错误消息

    • 不要跳过错误或警告
    • 它们通常包含确切解决方案
  2. 一致复现

    • 你能可靠触发吗?
  3. 检查最近变更

    • 什么变更可能导致此问题?
  4. 多组件系统收集证据

    # 对于每个组件边界:
    - 记录进入组件的数据
    - 记录退出组件的数据
    - 验证环境/配置传播
    - 检查每层状态
    运行一次以收集显示何处中断的证据
    然后分析证据以识别失败组件
    然后调查该特定组件
    
  5. 追踪数据流

    • 向后追踪到坏值起源

阶段 2:模式分析

  1. 找到工作示例
  2. 与参考对比
  3. 识别差异
  4. 理解依赖

阶段 3:假设与测试

  1. 形成单一假设:"我认为 X 是根本原因,因为 Y"
  2. 最小化测试:进行最小可能更改
  3. 验证后再继续
  4. 3+ 次修复失败时:质疑架构

阶段 4:实现

  1. 创建失败测试用例
  2. 实施单一修复
  3. 验证修复
  4. 如果 3+ 次修复失败:质疑架构

核心理念

系统化调试 不是碰运气,而是方法化地解决问题的科学流程。

┌─────────────────────────────────────────────────────────┐
│  收集证据 → 形成假设 → 系统验证 → 定位根因 → 实施修复  │
└─────────────────────────────────────────────────────────┘

核心原则

  • 基于证据,而非猜测
  • 治疗根本原因,而非症状
  • 一次验证一个假设
  • 记录整个过程

何时使用本技能

在以下场景时激活:

  • 出现错误、异常、Bug
  • 需要分析堆栈追踪(Stack Trace)
  • 提到"调试"、"排查问题"、"定位根因"
  • 生产环境问题分析
  • 跨文件或跨模块的问题追踪
  • 间歇性 Bug(时有时无)

调试工作流程

步骤 1:收集证据 📋

信息收集清单

证据类型收集方法重要性
完整堆栈追踪复制完整错误信息⭐⭐⭐⭐⭐
相关日志查看应用日志⭐⭐⭐⭐⭐
复现步骤记录如何触发问题⭐⭐⭐⭐
环境信息OS、版本、依赖⭐⭐⭐
代码变更最近的 Git 提交⭐⭐⭐⭐
用户输入触发问题的数据⭐⭐⭐

堆栈追踪分析示例

# 示例错误
Traceback (most recent call last):
  File "app.py", line 45, in process_order
    result = payment_service.charge(amount)
  File "payment.py", line 78, in charge
    return api_client.post("/charge", data)
  File "api.py", line 23, in post
    response = self.session.request(method, url, **kwargs)
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

分析要点

  1. 从下往上读:最下面是根本原因
  2. 追踪调用链:app.py → payment.py → api.py
  3. 识别错误类型:SSL 证书验证失败
  4. 定位失败点:api.py 第 23 行

步骤 2:形成假设 💡

假设生成规则

  1. 基于证据:不要凭空猜测
  2. 优先考虑最近变更
  3. 从简单到复杂排序
  4. 一次只关注一个假设

假设模板

假设:[问题描述] 的原因是 [根本原因]
触发条件:[什么情况下会出现]
验证方法:[如何证明或否定]

假设示例

假设:SSL 错误的原因是生产环境缺少证书文件
触发条件:部署到生产环境后首次调用 API
验证方法:检查 /etc/ssl/certs/ 目录是否存在证书

常见根因分类

类别典型原因检查方法
配置问题环境变量、配置文件错误检查 .env、config
依赖问题版本冲突、缺失依赖检查 requirements.txt
数据问题空值、格式错误、边界值检查输入数据
并发问题竞态条件、死锁检查锁、线程安全
资源问题内存泄漏、连接池耗尽检查资源使用
逻辑错误边界条件、空值处理代码审查

步骤 3:系统验证 🔍

验证原则

  1. 一次验证一个假设(控制变量)
  2. 设计最小化验证实验
  3. 记录验证过程和结果
  4. 用排除法缩小范围

验证方法

# 方法 1:添加日志
def process_order(order_id):
    logger.info(f"Processing order: {order_id}")
    order = get_order(order_id)
    logger.debug(f"Order data: {order}")
    # ...

# 方法 2:断点调试
import pdb; pdb.set_trace()

# 方法 3:简化输入
def test():
    # 用最小输入验证
    process_order({"id": 1, "amount": 0})

验证记录模板

## 假设验证记录

### 假设 1:证书文件缺失
- **验证方法**:检查文件是否存在
- **验证结果**:❌ 文件存在
- **结论**:假设不成立,排除

### 假设 2:证书过期
- **验证方法**:检查证书有效期
- **验证结果**:✅ 证书已于 3 天前过期
- **结论**:假设成立,定位根因

步骤 4:定位根因 🎯

区分症状与根因

使用五问法(5 Whys)深入挖掘:

问题:订单处理失败

Why 1: 为什么失败?
→ API 调用返回 500 错误

Why 2: 为什么 API 返回 500?
→ 数据库查询超时

Why 3: 为什么查询超时?
→ 缺少索引导致全表扫描

Why 4: 为什么缺少索引?
→ 新增字段时未同步添加索引

Why 5: 为什么未添加索引?
→ 没有 Code Review 流程

根因:缺少 Code Review 机制(而非"API 返回 500")

根因特征

  • 可操作:可以采取措施
  • 可预防:可以防止再次发生
  • 系统级:不是个人失误

步骤 5:实施修复 🔧

修复原则

  1. 最小化修复范围
  2. 添加回归测试
  3. 修复后验证
  4. 更新文档

修复清单

## 修复计划

### 代码修复
- [ ] 修改代码:[具体位置]
- [ ] 添加测试:[测试用例]
- [ ] 运行测试确认通过

### 预防措施
- [ ] 添加检查机制
- [ ] 更新文档
- [ ] 团队分享经验

### 验证
- [ ] 本地验证
- [ ] 测试环境验证
- [ ] 生产环境验证

调试技巧库

技巧 1:二分法调试

当问题可能出现在多个位置时:

# 在中间点添加日志
def complex_function(data):
    logger.info("Step 1: Start")
    result1 = step1(data)
    logger.info(f"Step 1 result: {result1}")

    logger.info("Step 2: Start")
    result2 = step2(result1)
    logger.info(f"Step 2 result: {result2}")

    logger.info("Step 3: Start")
    result3 = step3(result2)
    logger.info(f"Step 3 result: {result3}")

    return result3

技巧 2:最小化复现

# 从复杂场景中提取最小复现用例
def test_minimal_reproduction():
    # 不需要完整的订单数据
    order = {"amount": 100}  # 最小输入
    result = process_payment(order)
    assert result.success

技巧 3:对比法调试

# 对比工作版本和失败版本
def test_working_vs_broken():
    # 工作版本
    result_working = old_api_call(data)

    # 失败版本
    try:
        result_broken = new_api_call(data)
    except Exception as e:
        print(f"Broken: {e}")

    # 对比差异
    compare_results(result_working, result_broken)

技巧 4:环境隔离

# 使用 Docker 隔离环境
docker run -it python:3.11 bash

# 或使用虚拟环境
python -m venv debug_env
source debug_env/bin/activate

常见问题模式

模式 1:时序问题

症状:间歇性失败,时好时坏

根因:竞态条件、异步操作未正确等待

调试方法

# 添加重试和延迟
from tenacity import retry, stop_after_attempt, wait_fixed

@retry(stop=stop_after_attempt(3), wait=wait_fixed(1))
def flaky_operation():
    # 可能失败的操作
    pass

模式 2:内存问题

症状:性能逐渐下降,最终崩溃

根因:内存泄漏

调试方法

import tracemalloc

tracemalloc.start()
# ... 运行代码 ...
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
    print(stat)

模式 3:状态污染

症状:测试单独通过,批量运行失败

根因:测试间共享状态

调试方法

@pytest.fixture(autouse=True)
def reset_state():
    # 每个测试前重置状态
    reset_database()
    reset_cache()
    yield
    cleanup()

验证清单

修复完成后,检查:

  • 根本原因已识别(非症状)
  • 修复方案最小化
  • 添加回归测试
  • 本地验证通过
  • 文档已更新(如需要)
  • 团队已分享经验
  • 预防措施已实施

相关参考