systematic-debugging

star 1

遇到任何 bug、测试失败或异常行为时使用,在提出修复方案之前必须先用

1to9dota By 1to9dota schedule Updated 1/29/2026

name: systematic-debugging description: 遇到任何 bug、测试失败或异常行为时使用,在提出修复方案之前必须先用

系统化调试

概述

瞎猜乱改浪费时间,还会引入新 bug。快速补丁只会掩盖深层问题。

核心原则: 先找根因,再动手修。只治症状 = 失败。

违反流程的字面意思,就是违反调试的精神。

铁律

没有根因调查,不许提修复方案

如果你没完成第一阶段,就不能提修复方案。

何时使用

用于任何技术问题:

  • 测试失败
  • 生产环境 bug
  • 异常行为
  • 性能问题
  • 构建失败
  • 集成问题

尤其要用在:

  • 时间紧迫时(紧急情况最容易让人瞎猜)
  • "就改一下试试"看起来很明显时
  • 已经试了好几个修复方案时
  • 上一个修复没用时
  • 你不完全理解问题时

别跳过:

  • 问题看起来很简单(简单 bug 也有根因)
  • 你很赶(赶只会让你返工)
  • 老板要你马上修好(系统化调试比瞎改更快)

四个阶段

你必须完成每个阶段才能进入下一个。

阶段一:根因调查

在尝试任何修复之前:

  1. 仔细阅读错误信息

    • 别跳过错误或警告
    • 它们通常包含确切的解决方案
    • 完整阅读堆栈跟踪
    • 记下行号、文件路径、错误代码
  2. 稳定复现

    • 能可靠地触发吗?
    • 确切的步骤是什么?
    • 每次都会发生吗?
    • 如果无法复现 → 收集更多数据,别猜
  3. 检查最近的改动

    • 什么改动可能导致这个问题?
    • Git diff,最近的提交
    • 新依赖,配置变更
    • 环境差异
  4. 多组件系统中收集证据

    当系统有多个组件时(CI → 构建 → 签名,API → 服务 → 数据库):

    在提出修复之前,添加诊断埋点:

    对于每个组件边界:
      - 记录进入组件的数据
      - 记录离开组件的数据
      - 验证环境/配置的传递
      - 检查每层的状态
    
    运行一次收集证据,显示哪里出问题
    然后分析证据,定位失败的组件
    然后调查那个特定组件
    

    示例(多层系统):

    # 第一层:工作流
    echo "=== 工作流中可用的密钥: ==="
    echo "IDENTITY: ${IDENTITY:+已设置}${IDENTITY:-未设置}"
    
    # 第二层:构建脚本
    echo "=== 构建脚本中的环境变量: ==="
    env | grep IDENTITY || echo "环境中没有 IDENTITY"
    
    # 第三层:签名脚本
    echo "=== 钥匙串状态: ==="
    security list-keychains
    security find-identity -v
    
    # 第四层:实际签名
    codesign --sign "$IDENTITY" --verbose=4 "$APP"
    

    这能揭示: 哪一层失败(密钥 → 工作流 ✓,工作流 → 构建 ✗)

  5. 追踪数据流

    当错误在调用栈深处时:

    参见本目录的 root-cause-tracing.md 了解完整的回溯技术。

    简化版:

    • 坏数据从哪里来的?
    • 什么调用传入了坏数据?
    • 一直向上追踪直到找到源头
    • 在源头修复,而不是症状处

阶段二:模式分析

修复前先找到模式:

  1. 找到可工作的示例

    • 在同一代码库中找到类似的可工作代码
    • 什么类似的东西是正常工作的?
  2. 与参考实现对比

    • 如果在实现某个模式,完整阅读参考实现
    • 别略读——逐行阅读
    • 在应用之前完全理解该模式
  3. 识别差异

    • 正常工作的和出问题的有什么不同?
    • 列出所有差异,无论多小
    • 别假设"那个不可能有影响"
  4. 理解依赖

    • 这需要哪些其他组件?
    • 需要什么设置、配置、环境?
    • 它做了什么假设?

阶段三:假设与测试

科学方法:

  1. 形成单一假设

    • 清楚地陈述:"我认为 X 是根因,因为 Y"
    • 写下来
    • 要具体,不要模糊
  2. 最小化测试

    • 做最小的改动来测试假设
    • 一次只改一个变量
    • 别同时修多个东西
  3. 验证后再继续

    • 有效吗?是 → 阶段四
    • 没效?形成新假设
    • 别在上面叠加更多修复
  4. 当你不知道时

    • 说"我不理解 X"
    • 别装懂
    • 寻求帮助
    • 继续研究

阶段四:实施

修复根因,而非症状:

  1. 创建失败的测试用例

    • 最简单的复现
    • 如果可能就自动化测试
    • 没有框架就写一次性测试脚本
    • 修复前必须有
    • 使用 superpowers-zh:test-driven-development 技能来编写正确的失败测试
  2. 实施单一修复

    • 解决已识别的根因
    • 一次只改一处
    • 别"顺手"做改进
    • 别捆绑重构
  3. 验证修复

    • 测试现在通过了?
    • 没有破坏其他测试?
    • 问题真的解决了?
  4. 如果修复没用

    • 停下
    • 数一数:你已经试了多少个修复?
    • 如果 < 3:返回阶段一,用新信息重新分析
    • 如果 ≥ 3:停下来质疑架构(见下面第 5 步)
    • 没有架构讨论,别尝试第 4 个修复
  5. 如果 3+ 个修复都失败了:质疑架构

    表明架构问题的模式:

    • 每个修复都在不同地方暴露新的共享状态/耦合/问题
    • 修复需要"大规模重构"才能实现
    • 每个修复都在别处产生新症状

    停下来质疑根本:

    • 这个模式从根本上合理吗?
    • 我们是不是"因为惯性而坚持"?
    • 应该重构架构还是继续修症状?

    在尝试更多修复之前与你的搭档讨论

    这不是假设失败——这是架构错误。

红旗 - 停下来遵循流程

如果你发现自己在想:

  • "先快速修一下,以后再调查"
  • "试着改一下 X 看看行不行"
  • "一起改几个地方,然后跑测试"
  • "跳过测试,我手动验证"
  • "可能是 X,让我修一下"
  • "我不完全理解但这可能有用"
  • "模式说是 X 但我想换个方式"
  • "主要问题是这些:[没调查就列修复方案]"
  • 没追踪数据流就提解决方案
  • "再试一个修复"(已经试了 2+ 个)
  • 每个修复都在不同地方暴露新问题

以上全部意味着:停下。返回阶段一。

如果 3+ 个修复失败了: 质疑架构(见阶段 4.5)

搭档发出的"你做错了"信号

注意这些提示:

  • "那不是没发生吗?" - 你没验证就假设了
  • "这能让我们看到...?" - 你应该添加证据收集
  • "别猜了" - 你在没理解的情况下提修复方案
  • "深入想想" - 质疑根本,而不只是症状
  • "我们卡住了?"(沮丧)- 你的方法不管用

当你看到这些: 停下。返回阶段一。

常见借口

借口 现实
"问题很简单,不需要流程" 简单问题也有根因。流程对简单 bug 很快。
"紧急情况,没时间走流程" 系统化调试比瞎猜乱改更快。
"先试这个,然后再调查" 第一个修复定下模式。从一开始就做对。
"确认修复有效后再写测试" 没测试的修复不可靠。先测试才能证明。
"一次改多个省时间" 无法隔离哪个有效。还会引入新 bug。
"参考太长了,我改改这个模式" 部分理解保证出 bug。完整阅读。
"我看到问题了,让我修一下" 看到症状 ≠ 理解根因。
"再试一个修复"(已失败 2+ 个) 3+ 次失败 = 架构问题。质疑模式,别再修了。

快速参考

阶段 关键活动 成功标准
1. 根因 读错误、复现、检查改动、收集证据 理解是什么和为什么
2. 模式 找可工作的示例、对比 识别差异
3. 假设 形成理论、最小化测试 确认或形成新假设
4. 实施 创建测试、修复、验证 bug 解决,测试通过

当流程显示"没有根因"时

如果系统化调查显示问题确实是环境相关的、时序依赖的或外部的:

  1. 你已完成流程
  2. 记录你调查了什么
  3. 实施适当的处理(重试、超时、错误信息)
  4. 添加监控/日志以便未来调查

但是: 95% 的"没有根因"案例是调查不完整。

支持技术

这些技术是系统化调试的一部分,在本目录中可用:

  • root-cause-tracing.md - 通过调用栈向后追踪 bug 找到原始触发点
  • defense-in-depth.md - 找到根因后在多层添加验证
  • condition-based-waiting.md - 用条件轮询替代任意超时

相关技能:

  • superpowers-zh:test-driven-development - 用于创建失败测试用例(阶段四,步骤 1)
  • superpowers-zh:verification-before-completion - 在声称成功之前验证修复有效

实际影响

来自调试会话的数据:

  • 系统化方法:15-30 分钟修好
  • 瞎改方法:2-3 小时瞎折腾
  • 一次修好率:95% vs 40%
  • 引入新 bug:几乎为零 vs 经常发生
Install via CLI
npx skills add https://github.com/1to9dota/superpowers-zh --skill systematic-debugging
Repository Details
star Stars 1
call_split Forks 1
navigation Branch main
article Path SKILL.md
More from Creator