O

systematic-debugging

作者 obra

一个分为四个阶段的调试工作流,要求在任何修复前先完成根因排查,并提供针对 flaky 测试、校验和测试污染的实用工具。

Stars0
收藏0
评论0
收录时间2026年3月27日
分类调试
安装命令
npx skills add https://github.com/obra/superpowers --skill systematic-debugging
概览

概览

systematic-debugging 是什么?

systematic-debugging 技能提供了一个结构化的四阶段流程,用来调试任何技术问题:测试失败、线上 bug、flaky 测试、性能问题、构建错误或集成失败等。

与其在第一行报错处“先改再说”或尝试一个“快速修复”,这个技能要求你先查清根因,再设计并验证一个可长期稳定的修复方案。它基于真实代码库中的实践模式和脚本,包括用于 TypeScript 的基于条件等待 helper,以及用于查找测试污染源的 Bash 脚本。

核心原则与铁律

systematic-debugging 的核心非常简单:

  • 核心原则: 必须先找到根因,再尝试任何修复。只处理症状而不解决根因的修复都视为失败。

  • 铁律(The Iron Law):

    NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
    

如果你尚未完成调查阶段,就不能提出或实施任何修复。这条规则的目的,是帮助你抵抗时间压力、拍脑袋决策,以及“就这一次”的自我辩解。

适用人群

systematic-debugging 适用于:

  • 希望获得可靠、可复现修复方案的 JavaScript/TypeScript 开发者
  • 需要处理 flaky 行为、随机超时和测试状态污染的 测试与 QA 工程师
  • 诊断间歇性构建或集成失败的 CI/CD 和工具链工程师
  • 希望团队共享一套严谨调试流程的 技术负责人和代码评审者

如果你更重视高质量的根因分析,而不是只图“快速止血”,这个技能会非常契合。

它能解决哪些问题?

在以下场景,你可以使用 systematic-debugging 技能:

  • 调查在“修复”后仍反复出现的 测试失败
  • 排查依赖随即超时或竞态条件的 flaky 测试
  • 不是简单屏蔽报错,而是深入理解 意外行为 的真正原因
  • 诊断 性能问题 或对时间敏感、执行缓慢的流程
  • 调试只在 CI 或高负载下出现的 构建或集成失败
  • 找出是哪一个 测试污染了状态,或留下了多余的文件/目录

代码库中还包含:

  • condition-based-waiting.mdcondition-based-waiting-example.ts,用于在 TypeScript 中用稳定的基于条件等待替代 setTimeout/sleep
  • defense-in-depth.md,介绍如何在多个层级增加校验,让某些类型的 bug 在结构上变得“不可能发生”
  • find-polluter.sh,一个 Bash helper,用来定位究竟是哪一个测试产生了不需要的文件或状态
  • root-cause-tracing.md,教你沿调用链向上回溯,将错误追踪到真正的源头

什么时候适合使用 systematic-debugging?

在这些情况下,systematic-debugging 特别适合:

  • 你正承受时间压力,容易冲动地凭猜测去修
  • 某个问题多次尝试修复后仍然反复出现
  • 你希望为自己或团队建立一套可重复使用的调试工作流
  • 你正在稳定一个 flaky 的测试集,或清理被污染的测试环境

在以下情况,它可能没那么适合

  • 你只需要一次性的代码生成或简单重构,不打算做系统性调查
  • 你要调试的内容不在本技能关注的技术领域内(例如非技术流程问题)
  • 你不愿按步骤执行流程,更偏好完全随意的试错式调试

如果你的目标是“先快速打补丁,回头再重做也行”,这个流程会显得有点严苛。如果你的目标是“只修一次、修对为止”,那 systematic-debugging 正是为你设计的。

使用方式

安装和配置

要在兼容的 agent 或工具环境中安装 systematic-debugging 技能,请使用提供的 npx 命令:

npx skills add https://github.com/obra/superpowers --skill systematic-debugging
``

该命令会从 `obra/superpowers` 仓库中拉取技能定义及其配套文档和脚本,路径位于 `skills/systematic-debugging`。

安装完成后:

1. 在你的 agent 或 skills UI 中打开 `systematic-debugging` 技能。
2. 确保当前工作区可以访问你的项目代码库、测试命令和日志。
3. 确认你能查看打包在技能中的 markdown 指南和脚本:
   - `SKILL.md`
   - `condition-based-waiting.md`
   - `defense-in-depth.md`
   - `root-cause-tracing.md`
   - `find-polluter.sh`

### 需要重点了解的文件和组件
在安装决策和日常使用中,以下文件最为关键:

- **`SKILL.md`** – 定义完整的四阶段系统化调试流程、规则和反模式,这是技能的核心文档。
- **`condition-based-waiting.md`** – 讲解如何用基于条件的等待替代测试中的随意超时等待。
- **`condition-based-waiting-example.ts`** – 提供 `waitForEvent` 等 TypeScript 工具,基于 `ThreadManager` 和事件类型实现 condition-based waiting。
- **`defense-in-depth.md`** – 展示如何在入口、业务逻辑、环境防护、日志等多个层级增加校验,让整类 bug 从结构上被杜绝。
- **`find-polluter.sh`** – 一个 Bash 脚本,通过遍历测试来定位究竟哪个测试创建了特定路径(状态污染)。
- **`root-cause-tracing.md`** – 演示如何从 stack trace 一路回溯到最初触发点,并结合 defense-in-depth 进行修复设计。
- **`CREATION-LOG.md`** – 说明该框架是如何被抽取与打磨的 meta 日志,主要用于理解设计意图,对日常使用不那么关键。

对于 **JavaScript/TypeScript** 开发者,其中的 TypeScript 示例和 Bash 脚本通常可以直接移植或改造后用于你的项目。

### 运行四阶段工作流
`systematic-debugging` 过程被组织为四个必须完成的阶段(详见 `SKILL.md`)。在进入下一阶段前,你必须完成当前阶段。

#### 阶段 1 – 根因调查(Root cause investigation)
在修改任何代码、配置或测试之前:

- **认真阅读错误信息和日志。**
  - 不要只粗略扫一眼 stack trace 或警告。
  - 记录行号、文件路径和错误码。
- **确保问题可被稳定复现。**
  - 找到一个最小且稳定的复现命令。
  - 记录复现所需的输入和环境因素。

在这一阶段,你不提出、不实施任何修复。目标是搞清楚:究竟哪里在失败、失败的具体表现是什么、在什么条件下会失败。

#### 阶段 2 – 模式分析(Pattern analysis)
当你已经能可靠复现问题后:

- 一次只改变一个因素(输入、配置、环境等),观察结果是否变化。
- 寻找边界:在哪些取值或条件下,行为会从“通过”变为“失败”。
- 使用日志、断言或临时埋点来缩小出问题的执行路径。

在阶段 2 结束时,你应当对问题的“轮廓”和“触发条件”有清晰理解,而不仅仅是记住一个最后的错误信息。

#### 阶段 3 – 假设与设计(Hypothesis and design)
此时可以形成假设:

- 明确说明你认为根因是什么(数据流问题、缺少校验、时序问题、路径错误等)。
- 设计一个**单一且聚焦**的改动,直接针对这个根因。
- 规划如何通过之前的复现步骤来验证假设是否成立。

如果假设被证伪,你需要回到调查/分析阶段,而不是在已有修复上叠加更多“猜测式”改动。

#### 阶段 4 – 实施与验证(Implementation and validation)
只有在有明确假设之后,才可以修改代码或配置:

- 实施尽可能小而直接的改动,只针对已确认的根因。
- 先运行最小复现命令,再运行更完整的测试集。
- 按需参考:
  - `defense-in-depth.md`,在多个层级添加校验
  - `root-cause-tracing.md`,确保你修复的是问题的真正源头
- 确认修复足够稳健(例如在高负载、CI 环境以及阶段 2 中识别的边界场景下都表现正常)。

如果修复效果与预期不符,你**不要**继续堆更多改动,而是退回前面的阶段。

### 在项目中使用调试工具

#### 用基于条件的等待处理 flaky 测试(JavaScript/TypeScript)
依赖 `setTimeout` 或 `sleep` 的 flaky 测试,常常在机器变慢、变快或有负载时出问题。`condition-based-waiting.md` 和 `condition-based-waiting-example.ts` 提供了更好的模式:等待你真正关心的“条件”,而不是猜时间。

典型迁移方式:

```typescript
// ❌ 之前:凭感觉估一个时间
await new Promise(r => setTimeout(r, 50));
const result = getResult();
expect(result).toBeDefined();

// ✅ 之后:等待条件达成
await waitFor(() => getResult() !== undefined);
const result = getResult();
expect(result).toBeDefined();

提供的 condition-based-waiting-example.ts 中包含类似这样的 helper:

export function waitForEvent(
  threadManager: ThreadManager,
  threadId: string,
  eventType: LaceEventType,
  timeoutMs = 5000
): Promise<LaceEvent> { /* ... */ }

将这些模式适配到你自己的测试基础设施中:

  1. 将这些工具复制或重写到你项目的测试 helper 中。
  2. 用基于条件的等待替换掉任意的 setTimeout/sleep 调用。
  3. 重新运行测试集,确认 flaky 情况减少。

这直接帮助实现 systematic-debugging 的目标:消除根因,而不是随意拉长超时时间。

使用 find-polluter.sh 查找测试污染源

如果你的测试会遗留多余的文件或目录,或以其他方式污染状态,find-polluter.sh 可以帮你定位具体是哪一个测试造成的。

在项目根目录下运行(按需调整参数):

./find-polluter.sh <file_or_dir_to_check> <test_pattern>

# 示例
./find-polluter.sh '.git' 'src/**/*.test.ts'

该脚本会:

  • 找出符合匹配模式的测试文件
  • 使用 npm test <file> 逐个运行
  • 在每次运行后检查目标文件或目录是否出现
  • 一旦发现是哪个测试创建了该文件/目录,就停止并输出该测试以及复现、分析该测试的命令

这与 systematic-debugging 的阶段 1 和阶段 2 紧密配合,为状态污染类问题提供可靠的复现和隔离手段。

应用 defense-in-depth 校验

当调查发现问题是由无效数据或错误假设引起时,defense-in-depth.md 建议在多个层级落实校验:

  • 入口校验(Entry point validation) – 在 API、CLI、UI handler 等边界处拦截明显无效的输入。
  • 业务逻辑校验(Business logic validation) – 确保数据在特定业务操作中的语义是合理的。
  • 环境防护(Environment guards) – 防止在错误的环境或路径下执行危险操作。
  • 诊断日志(Diagnostic logging) – 在问题发生时提供有用上下文,便于排查。

例如,对 workingDirectory 参数进行校验:

function createProject(name: string, workingDirectory: string) {
  if (!workingDirectory || workingDirectory.trim() === '') {
    throw new Error('workingDirectory cannot be empty');
  }
  if (!existsSync(workingDirectory)) {
    throw new Error(`workingDirectory does not exist: ${workingDirectory}`);
  }
  if (!statSync(workingDirectory).isDirectory()) {
    throw new Error(`workingDirectory is not a directory: ${workingDirectory}`);
  }
  // ... proceed
}

在阶段 4 实施修复时使用这些模式,可以降低同一类 bug 通过其他代码路径再次出现的概率。

什么时候不必使用这个技能

在这些情况下,你可以考虑跳过或延后使用 systematic-debugging

  • 你目前处于快速原型期,接受一次性代码和明显 bug 的存在
  • 问题非常简单且你已完全理解(例如开发过程里刚打出来就发现的拼写错误),不值得动用完整的四阶段流程
  • 你只想借用其中的某个工具(如 condition-based waiting),而不是整套流程

即便如此,Iron Law 仍然是一个有价值的自检标准:如果你发现自己开始不断叠加“猜测式修复”,就该切换到 systematic-debugging 流程了。

常见问题(FAQ)

systematic-debugging 实际上会怎样改变我的工作流程?

systematic-debugging 会让你不再从错误信息直接跳到改代码,而是先经过调查、模式分析、假设,再到实施。这在实践中的表现是:

  • 在动代码前,你先拿到一个可靠的复现方式
  • 你会刻意改变条件,来摸清问题空间的边界
  • 每次只针对一个已验证的假设实施一项聚焦的修复

结果是:更少的回滚、更少的隐性回归,以及更可预期的调试耗时。

我该如何安装 systematic-debugging 技能?

使用 npx skills 命令:

npx skills add https://github.com/obra/superpowers --skill systematic-debugging

安装完成后,在你的 agent 或技能目录中打开该技能,然后阅读 SKILL.md 获取完整流程,并查看配套的 markdown 文件了解具体模式和示例。

systematic-debugging 支持 JavaScript 和 TypeScript 调试吗?

支持。虽然整个框架本身与语言无关,但仓库中提供了面向 JavaScript/TypeScript 的具体工具:

  • condition-based-waiting-example.ts,用于在测试中实现基于条件的等待
  • defense-in-depth.mdroot-cause-tracing.md 中的模式,以 TypeScript 示例进行说明
  • find-polluter.sh 默认假设使用 npm test,非常适合典型的 JS/TS 测试 runner

你可以根据自己的项目结构和工具链对这些工具进行适配。

我可以用 systematic-debugging 解决 flaky 测试自动化问题吗?

可以,这正是它最擅长的场景之一。你可以结合:

  • SKILL.md 中的四阶段流程,系统性调查和理解 flakiness
  • condition-based-waiting.md 和 TypeScript 示例,用 condition-based waiting 替代凭感觉设定的超时时间
  • find-polluter.sh,查找污染状态或产生意外文件的测试

组合使用这些工具,可以将不稳定的测试转化为稳定、可重复的检查。

systematic-debugging 只适用于测试吗?能处理线上 bug 吗?

这套流程适用于任何技术问题:

  • 测试失败与 flaky 问题
  • 线上 bug 和事故
  • 性能问题
  • 构建和集成失败

仓库中的示例和工具更偏向测试与开发工作流,但四个阶段和相关原则同样明确覆盖了生产环境场景。

如果我时间很紧,只想快速修一下怎么办?

这个技能的设计初衷之一,就是对抗“就先随便改一下”的冲动。它的观点是:

  • 在未做根因调查的前提下匆忙修复,会导致反复折腾和返工
  • 只处理症状的修复往往会带来新的问题

在实际使用中,即使在紧急事故中,花几分钟按阶段 1 和阶段 2 的方式调查,通常也能整体节省时间。

我必须每次都完整走完四个阶段吗?

systematic-debugging 的意图是:跳过阶段应该是例外,而不是常态。对于一些非常小且你立即就看懂的错误(例如一次轻微重构中立刻发现的笔误),你可以适度压缩步骤。但如果:

  • 问题是反复出现的
  • 你并不真正清楚它为什么会发生
  • 之前的修复尝试已经失败过

……那么就强烈建议你完整走完四阶段流程。

根因追踪(root-cause tracing)与 defense-in-depth 有什么关系?

root-cause-tracing.md 帮你从可见错误出发,沿调用链一路回溯到最初触发点;然后 defense-in-depth.md 展示如何通过以下方式防止类似 bug 再次出现:

  • 在真正的源头位置进行修复
  • 在多个层级增加校验

例如,当 git init 在错误目录中执行而失败时,root-cause tracing 会告诉你是哪一个函数传入了错误路径;而 defense-in-depth 则会在更早的地方增加路径校验和防护,让无效路径无法被传入。

不拷贝仓库里的代码,只用流程可以吗?

可以。systematic-debugging 的核心价值,是 SKILL.md 中描述的结构化调试流程。你可以:

  • 直接在自己的环境中遵循这些阶段和规则
  • 将调查与分析模式应用到任何语言或技术栈

TypeScript 和 Bash 工具只是可选加速器,尤其适合 JS/TS 和类 Unix 环境。

我在哪里可以查看 systematic-debugging 的所有内容?

安装完成后,在 obra/superpowersskills/systematic-debugging 路径下打开文件或仓库视图。建议重点查看:

  • SKILL.md
  • condition-based-waiting.md
  • condition-based-waiting-example.ts
  • defense-in-depth.md
  • root-cause-tracing.md
  • find-polluter.sh

这些文件覆盖了完整的 systematic-debugging 工作流,以及用于调试和稳定测试的具体工具。

评分与评论

暂无评分
分享你的评价
登录后即可为这个技能评分并发表评论。
G
0/10000
最新评论
保存中...