systematic-debugging
作成者 obraどんな修正より先に原因究明を必ず行う、4フェーズ構成のデバッグワークフロー。フレークテストやバリデーション、テスト汚染に対処する具体的なツール付き。
概要
systematic-debugging とは?
systematic-debugging スキルは、あらゆる技術的な問題をデバッグするための、構造化された 4 フェーズのプロセスです。対象となるのは、テスト失敗、本番バグ、フレークテスト、パフォーマンス問題、ビルドエラー、インテグレーション失敗などです。
「とりあえず一発修正」や、最初に目に入ったエラーログ周辺のコードをいきなり書き換える代わりに、このスキルはまず根本原因の究明を強制し、そのうえで長持ちする修正を設計・検証させます。実際のコードベースを元にした実践的なパターンとスクリプトに支えられており、TypeScript の条件ベース待機ヘルパーやテスト汚染を検出する Bash スクリプトなどが含まれます。
中核となる原則と鉄則
systematic-debugging の核はシンプルです。
-
中核となる原則: 修正を試みる前に、必ず根本原因を突き止める。症状だけをいじる修正は失敗とみなす。
-
鉄則 (Iron Law):
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
調査フェーズを完了していない状態では、修正案を出したり適用したりしてはいけません。これは時間的プレッシャーや当てずっぽう、「今回だけは」のような言い訳に抗うための仕掛けです。
このスキルの対象者
systematic-debugging は次のような人を想定しています。
- 信頼できる再現性のある修正を行いたい JavaScript/TypeScript 開発者
- フレークな挙動、ランダムなタイムアウト、テスト状態の汚染に悩む テスト/QA エンジニア
- 断続的なビルドやインテグレーション失敗を追いかける CI/CD・ツール担当エンジニア
- デバッグ作業に共通の厳密なプロセスを導入したい テックリードやコードレビュー担当者
目先の症状を素早くふさぐことよりも、根本原因の解明と高品質な修正を重視する環境に特にフィットします。
どのような問題に効くのか
systematic-debugging スキルが役に立つのは、例えば次のような場面です。
- 「修正」後も何度も再発する テスト失敗 の原因調査
- 適当なタイムアウト値やレースコンディションに依存している フレークテスト の追跡
- エラーを握りつぶすのではなく、予期しない挙動の真の理由 を理解したいとき
- パフォーマンス問題 や時間依存のフローのボトルネック調査
- CI や高負荷時だけ発生する ビルド/インテグレーション失敗 のデバッグ
- どの テストが状態を汚染しているか、不要なファイル/ディレクトリを残しているかを特定したいとき
リポジトリには、次のようなファイルも含まれています。
- TypeScript で
setTimeout/sleepを堅牢な条件ベース待機に置き換えるためのcondition-based-waiting.mdとcondition-based-waiting-example.ts - 特定カテゴリのバグを構造的に不可能にする、多層バリデーションの考え方をまとめた
defense-in-depth.md - 不要なファイルや状態を作り出すテストを特定する Bash ヘルパー
find-polluter.sh - エラーをコールチェーンの奥までさかのぼって真の発生源まで追跡する
root-cause-tracing.md
systematic-debugging が向いているケース
systematic-debugging が特に力を発揮するのは、次のような状況です。
- 強い時間的プレッシャーがあり、当てずっぽうで修正したくなっている
- 同じ問題が何度も再発し、すでに複数回「修正」したつもりになっている
- 自分やチームのために、再現性のあるデバッグワークフローを確立したい
- フレークなテストスイートを安定化させたい、あるいは汚れたテスト環境をクリーンアップしたい
逆に、次のような場合は あまり向かない かもしれません。
- 調査不要な、単発のコード生成やリファクタだけを行いたい
- ここで説明している範囲外の、非技術的なプロセス問題などを扱っている
- 段階的なプロセスに従うつもりはなく、行き当たりばったりの試行錯誤を好む
目標が「とにかく今だけ動けばいい、あとで直す」であれば、このプロセスは厳しく感じられるでしょう。目標が「一度で正しく直したい」であれば、systematic-debugging はまさにそのために設計されています。
使い方
インストールとセットアップ
systematic-debugging スキルを対応したエージェントやツール環境に導入するには、用意されている npx コマンドを実行します。
npx skills add https://github.com/obra/superpowers --skill systematic-debugging
``
このコマンドにより、`obra/superpowers` リポジトリ内の `skills/systematic-debugging` からスキル定義と、関連ドキュメント・スクリプトが取得されます。
インストール後は次のステップを実施します。
1. 利用中のエージェントまたはスキル 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`** – 4 フェーズから成る systematic debugging プロセス全体の定義、ルール、アンチパターンを記載した中核ドキュメント。
- **`condition-based-waiting.md`** – テスト内の行き当たりばったりなタイムアウトを、条件ベース待機に置き換える方法を解説。
- **`condition-based-waiting-example.ts`** – `ThreadManager` とイベント種別に対して条件ベース待機を実装する `waitForEvent` などの TypeScript ユーティリティ。
- **`defense-in-depth.md`** – エントリポイント、ビジネスロジック、環境ガード、ログといった複数レイヤーでバリデーションを行い、バグの発生余地を構造的に潰す方法を紹介。
- **`find-polluter.sh`** – テストを順番に実行し、どのテストが特定のパス(状態汚染)を作るかを突き止める Bash スクリプト。
- **`root-cause-tracing.md`** – スタックトレースから元のトリガーまでバグをたどり、さらに defense-in-depth と組み合わせて対処する手順を解説。
- **`CREATION-LOG.md`** – このフレームワークをどのように抽出・強化してきたかのメタ的なログ。設計意図を理解するには有用だが、日常利用では必須ではない。
**JavaScript/TypeScript** 開発者であれば、TypeScript のサンプルと Bash スクリプトをそのまま、あるいは最小限の変更でプロジェクトに組み込めるケースが多いでしょう。
### 4 フェーズのワークフローを回す
`systematic-debugging` のプロセスは、`SKILL.md` に記載された必須の 4 フェーズから成ります。各フェーズを完了してから、次のフェーズに進む前提です。
#### フェーズ 1 – 根本原因の調査
コードや設定、テストに手を入れる前に、まず次を行います。
- **エラーメッセージとログを丁寧に読む。**
- スタックトレースや警告を流し読みしない。
- 行番号、ファイルパス、エラーコードを押さえる。
- **問題を安定して再現する。**
- 最小の、確実に再現するコマンドを確立する。
- 必要な入力や環境要因を記録する。
このフェーズでは、修正案を出したり実装したりしてはいけません。目的は「何が」「どこで」「どの条件下で」壊れているのかを正確に理解することです。
#### フェーズ 2 – パターン分析
再現が安定したら、次の段階です。
- 入力、設定、環境などの要因を一度に 1 つずつ変え、結果がどう変化するかを見る。
- 成功と失敗の境界(どの値・条件で挙動が切り替わるか)を探す。
- ログやアサーション、一時的な計測・ログ出力などを活用し、失敗する経路を絞り込む。
フェーズ 2 の終わりまでには、単に最後のエラーメッセージだけでなく、「どのような条件でどんな形の問題が起きるのか」を把握している状態を目指します。
#### フェーズ 3 – 仮説と設計
ここで初めて仮説を立てます。
- 根本原因が何であると考えているかを明確に言語化する(データフロー、バリデーション不足、タイミングの問題、パスの誤りなど)。
- その根本原因を解消する **単一でフォーカスした** 変更案を設計する。
- 再現ステップを使って、どのように仮説を検証するかを計画する。
仮説が外れた場合は、憶測の修正を積み上げず、調査・分析フェーズに戻ります。
#### フェーズ 4 – 実装と検証
明確な仮説が立ってから、初めてコードや設定を変更します。
- 根本原因に直接対応する、可能な限り最小の変更だけを加える。
- 最小再現ケースを実行し、その後により広いテストスイートを走らせる。
- 次のガイドも併用する。
- 複数レイヤーでバリデーションを追加するための `defense-in-depth.md`
- 真の発生源を修正しているか確認する `root-cause-tracing.md`
- 高負荷時や CI、フェーズ 2 で洗い出したエッジケースでも問題なく動作するかを確認し、修正が十分に堅牢であることを確かめる。
修正が期待通りに振る舞わない場合は、変更を積み増しせず、前のフェーズに戻ります。
### プロジェクトでデバッグ用ユーティリティを使う
#### フレークテスト向けの条件ベース待機 (JavaScript/TypeScript)
`setTimeout` や `sleep` に頼るフレークテストは、マシンが速すぎたり遅すぎたり、負荷がかかったりすると壊れがちです。`condition-based-waiting.md` と `condition-based-waiting-example.ts` では、「待ちたい条件が満たされるまで待つ」パターンを紹介しています。
典型的な移行イメージは次のとおりです。
```typescript
// ❌ Before: guessing timing
await new Promise(r => setTimeout(r, 50));
const result = getResult();
expect(result).toBeDefined();
// ✅ After: waiting for condition
await waitFor(() => getResult() !== undefined);
const result = getResult();
expect(result).toBeDefined();
提供されている condition-based-waiting-example.ts には、次のようなヘルパーが含まれます。
export function waitForEvent(
threadManager: ThreadManager,
threadId: string,
eventType: LaceEventType,
timeoutMs = 5000
): Promise<LaceEvent> { /* ... */ }
これらのパターンを自分のテスト基盤に合わせてアダプトする手順は以下の通りです。
- ユーティリティをプロジェクトのテストヘルパーにコピーするか、同等の実装を用意する。
- 任意の
setTimeout/sleep呼び出しを、条件ベース待機に順次置き換える。 - テストスイートを再実行し、フレークテストの減少を確認する。
これは、タイムアウトを闇雲に伸ばすのではなく、根本原因を取り除くという systematic-debugging の思想を直接サポートするアプローチです。
find-polluter.sh で汚染テストを特定する
テストが不要なファイルやディレクトリを残す、あるいは状態を汚染する場合、find-polluter.sh によって原因テストを特定できます。
使い方(プロジェクトルートから、必要に応じて引数を変更):
./find-polluter.sh <file_or_dir_to_check> <test_pattern>
# Example
./find-polluter.sh '.git' 'src/**/*.test.ts'
スクリプトは次のことを行います。
- パターンに一致するテストファイルを列挙する
npm test <file>でそれらを 1 つずつ実行する- 各テストの後で、対象ファイルやディレクトリが作成されたかをチェックする
- 最初にそのパスを作成したテストを特定し、再実行や詳細調査に必要なコマンドを表示する
これは、状態汚染の再現・切り分けを行ううえで、systematic-debugging のフェーズ 1 とフェーズ 2 にそのまま組み込めるツールです。
defense-in-depth バリデーションの適用
調査の結果、無効なデータや誤った前提が原因だった場合、defense-in-depth.md では複数レイヤーにバリデーションを配置することを推奨しています。
- エントリポイントのバリデーション – API・CLI・UI ハンドラなど外部との境界で明らかにおかしな入力を弾く。
- ビジネスロジック層のバリデーション – 特定の処理に対してデータが妥当かどうかを検証する。
- 環境ガード – 誤った環境やパスで危険な操作が行われるのを防ぐ。
- 診断ログ – 万一すり抜けた場合でも原因追跡に役立つ情報を記録する。
例えば 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 の修正を実装する際にこのようなパターンを取り入れることで、別のコードパスから同じ種類のバグが再発しにくくなります。
このスキルを使わない方がよい場面
次のような場合は、systematic-debugging をあえて使わない、あるいは後回しにする方がよいこともあります。
- 試作段階で、使い捨てコードやバグをあえて許容している
- 問題が極めて単純で原因も完全に分かっており(例: 開発中に見つけた明白なタイプミス)、4 フェーズの厳密さに見合わない
- 条件ベース待機など一部のユーティリティだけが必要で、プロセス全体までは導入しない
それでも、Iron Law(鉄則)は良いセルフチェックとして機能します。もし自分が複数の「当てずっぽう修正」を積み重ねていると気づいたら、systematic-debugging に切り替えるタイミングです。
FAQ
systematic-debugging はワークフローの何を変えるのですか?
エラーメッセージからいきなりコードを書き換えるのではなく、systematic-debugging は実装に入る前に、調査・パターン分析・仮説立案のステップを必ず踏ませます。実務上は次のような変化になります。
- コードに触る前に、安定した再現手順を確保する
- 条件を変えながら問題領域を理解する
- 検証済みの仮説 1 つにつき、集中した修正 1 つだけを書く
その結果、リバートや隠れたリグレッションが減り、デバッグにかかる時間の見通しも立ちやすくなります。
systematic-debugging スキルはどうやってインストールしますか?
npx skills コマンドを使用します。
npx skills add https://github.com/obra/superpowers --skill systematic-debugging
インストール後は、エージェントやスキルディレクトリでスキルを開き、SKILL.md でプロセス全体を確認してください。各種パターンやサンプルコードは、同梱の markdown ファイルから参照できます。
systematic-debugging は JavaScript / TypeScript 向けのデバッグもサポートしていますか?
はい。フレームワーク自体は言語非依存ですが、リポジトリには JavaScript/TypeScript を念頭に置いた具体的なユーティリティが含まれています。
- テストで条件ベース待機を行う
condition-based-waiting-example.ts - TypeScript の例を用いて解説している
defense-in-depth.mdとroot-cause-tracing.md内のパターン - デフォルトで
npm testを前提としており、一般的な JS/TS テストランナーと相性の良いfind-polluter.sh
これらはプロジェクトの構成やツールチェーンに合わせてアダプト可能です。
フレークテストの自動化改善にも systematic-debugging を使えますか?
はい。むしろ最も強力なユースケースの 1 つです。次の組み合わせで活用できます。
- フレークの原因を調査・理解するための
SKILL.mdの 4 フェーズプロセス - タイミングの当て推量をやめ、条件ベース待機に置き換えるための
condition-based-waiting.mdと TypeScript サンプル - 状態汚染や予期しないファイルを作成するテストを見つける
find-polluter.sh
これらを組み合わせることで、不安定なテストを安定した決定的なチェックへと変えていけます。
systematic-debugging はテスト専用ですか? 本番のバグにも使えますか?
このプロセスは、あらゆる技術的な問題に適用できます。
- テスト失敗やフレーク
- 本番バグやインシデント
- パフォーマンス問題
- ビルドやインテグレーションの失敗
同梱の例やユーティリティはテスト・開発ワークフロー寄りではあるものの、フェーズ構成と原則自体は本番シナリオも明示的にカバーしています。
時間がないときに、さっと修正だけしたい場合はどうすれば?
このスキルは、まさにその「さっと一発修正したい」衝動に逆らうために書かれています。主張はシンプルです。
- 根本原因の調査なしに急いでも、空回りや手戻りを招きやすい
- 症状だけを押さえ込む修正は、別の問題を生みやすい
実務的には、少し時間を取ってフェーズ 1 とフェーズ 2 を踏んでおく方が、緊急対応の最中であってもトータルの時間節約になるケースがほとんどです。
毎回きっちり 4 フェーズすべてを踏む必要がありますか?
systematic-debugging の意図は、フェーズを飛ばすのは例外であって標準ではない という点にあります。例えば、小さなリファクタ中にすぐ原因が分かった軽微なミスなどであれば、ステップを圧縮してもよいでしょう。しかし次のような場合は、フルの 4 フェーズを強く推奨します。
- 問題が再発している
- なぜ起きているかを完全には理解できていない
- 過去の修正がうまくいっていない
このような状況では、プロセスを省略するとむしろ遠回りになりやすくなります。
root-cause tracing と defense-in-depth はどう関係していますか?
root-cause-tracing.md は、目に見えているエラーからコールチェーンをたどり、真のトリガーまでバグをさかのぼるための手引きです。一方 defense-in-depth.md は、次のような形で同種のバグを予防する方法を示します。
- 真の発生源で修正する
- 複数レイヤーにバリデーションを追加する
例えば git init が間違ったディレクトリで失敗しているケースでは、tracing によって誤ったパスを渡した関数が特定され、defense-in-depth によって不正なパスをより早い段階で弾くガードやバリデーションが追加されます。
リポジトリのコードをコピーせずに、このスキルだけ使うことはできますか?
はい。systematic-debugging の核となる価値は、SKILL.md に記載された構造化されたデバッグプロセスそのものです。
- 自分の環境で、そのフェーズとルールに従って進める
- 調査・分析のパターンを、任意の言語・スタックに適用する
TypeScript や Bash のヘルパーは、特に JS/TS と Unix 系環境にとって有用な「加速装置」ではありますが、必須ではありません。
systematic-debugging に含まれるすべてを確認するには?
インストール後、obra/superpowers の skills/systematic-debugging を Files またはリポジトリビューで開いてください。特に見ておくべきファイルは次の通りです。
SKILL.mdcondition-based-waiting.mdcondition-based-waiting-example.tsdefense-in-depth.mdroot-cause-tracing.mdfind-polluter.sh
これらを読むことで、systematic-debugging のワークフロー全体と、デバッグやテスト安定化のための具体的なツール群を把握できます。
