systematic-debugging について
開発者向けに、根本原因の調査、パターン比較、仮説検証、実装の順で進めるデバッグスキルです。修正前に証拠を求め、失敗が続く場合は設計の見直しを促します。
主な機能
- 01エラーを読み問題を再現
- 02データフローをたどり原因を特定
- 03正常系と異常系のパターンを比較
- 04反証可能な仮説を順に検証
- 05修正前に回帰テストを作成
- 06修正の失敗が続けば相談
活用シーン
- 01コード変更前に不具合を調査
- 02不正なデータの流入箇所を特定
- 03小規模なテストで原因候補を検証
- 04修正が続けて失敗した際に方針を再検討
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ bhagyamudgal/ skills --skill systematic-debuggingインストール後、Claude Code で/systematic-debuggingを入力して読み込まれたか確認します。
同梱ファイルTECHNIQUES.md
詳細
系統的なデバッグ
これは、デバッグセッションの中盤で取り組むべき作業です。根本原因を見つけ、証拠で裏付け、一度で修正します。
鉄則
NO FIXES WITHOUT ROOT-CAUSE INVESTIGATION FIRST
理解していない修正は修正ではありません。問題を先送りしているだけです。修正がなぜ機能するのか説明できないなら、原因を見つけていません。作業を止め、実際のコードパスをたどってください。
PR ごとの場当たり的な修正の予算はゼロです。本当に根本原因を見つけられない場合は、その旨を伝えて助けを求めてください。
4 つのフェーズ
次のフェーズに進む前に、各フェーズを完了してください。
フェーズ 1:根本原因の調査
修正を試みる前に、必ず次を行ってください。
- エラーメッセージを読む。これが最初のステップです。スタックトレースは該当する行を示し、メッセージはどの不変条件が破られたかを示します。出力に含まれるすべてのエラーと警告を読んでください。そこに正確な答えが書かれていることもよくあります。
- 常に再現できるようにする。再現ループの構築方法については、
/diagnosing-bugsのフェーズ 2 を参照してください。 - 最近の変更を確認する。git diff、最近のコミット、新しい依存関係、設定の変更、環境の差異を確認してください。
- 境界を計測する。複数のコンポーネントで構成されるシステムでは、各境界に入るものと出るものをログに記録し、環境変数や設定の伝播を確認してください。ログに記録する前に、シークレット、トークン、認証情報、個人データを伏せてください。名前やサイズなどのメタデータは残し、値は残さないでください。例として、CI からビルド、署名までの流れや、API からサービス、データベースまでの流れがあります。1 回実行し、証拠を確認してください。どこで連鎖が途切れているかがわかります。たとえば、シークレットがワークフローには届いているものの、ビルドには届いていないことが証拠からわかる場合があります。その場合は、そのコンポーネントを調査してください。詳しい手法については、TECHNIQUES.mdを参照してください。
- データフローを逆向きにたどる。コールスタックの深い位置でエラーが発生している場合、クラッシュ箇所は症状にすぎません。不正な値はどこで発生したのでしょうか。発生源が見つかるまでさかのぼり、そこで修正してください。
不正な値を生成している行と、その値を不正にした入力を特定できたら、フェーズ 1 は完了です。クラッシュした場所を特定できただけでは、完了ではありません。
次のいずれかに当てはまる場合、フェーズ 1 は未完了です。
- null チェックを追加する。その値がなぜ null なのか、また null であるべきなのかを確認してください。
- 原因不明のエラーを try-catch で囲む。理解していて対処できるエラーだけを捕捉してください。
as any、as unknown、または@ts-ignoreを使う。型をごまかすのではなく、修正してください。- タイムアウトを延ばすか、リトライを追加する。実際に何が遅いのか、あるいは競合しているのかを確認してください。原因が不安定なタイミングテストなら、TECHNIQUES.mdを参照してください。
- 失敗するテストを通るように編集する。テストが正しいことはよくあります。変更する前に、なぜ失敗するのかを理解してください。昨日までは正しかったのに、変更によって失敗したのであれば、その変更が疑わしいと考えてください。
フェーズ 2:パターン分析
修正する前に、パターンを見つけてください。
- 正常に動作する例を見つける。同じコードベース内で、類似した正常動作のコードを探してください。
- リファレンスと比較する。パターンを実装する場合は、リファレンス実装を斜め読みせず、すべての行を最後まで読んでください。部分的な理解は、必ずバグにつながります。
- 正常に動作するものと壊れているものの違いを、どんなに小さくてもすべて列挙する。証拠によって除外されるまでは、どの違いも原因の候補として扱ってください。
比較対象となる正常動作の例を特定し、列挙したすべての違いについて、それぞれ証拠に基づいて除外するか、仮説としてフェーズ 3 に持ち越せたら、フェーズ 2 は完了です。正常動作の例が見つからない場合や、説明のつかない違いが残っている場合は、まだ完了ではありません。
フェーズ 3:仮説とテスト
科学的手法:
- 反証可能な仮説を 3~5 個挙げ、優先度を付けてから、最も優先度の高いものをテストします。それぞれに予測を記述します。たとえば、「X が原因なら、Y を変更するとバグが解消する」のようにします。書き留めてください。予測は外れることがありますが、説明は外れません。
- 最小限のテストを行います。仮説をテストするために必要な最小の変更を加えます。実行ごとに変更する変数を 1 つにし、結果から原因を特定できるようにします。
- 続行する前に検証します。仮説が確認できた場合はフェーズ 4 に進みます。そうでない場合は、次の仮説をテストする前に、失敗した修正を元に戻します。
- 分からないときは、そう伝えます。「X が理解できません」と言うほうが、分かったふりをするより適切です。さらに調査するか、助けを求めてください。
フェーズ 4:実装
症状ではなく、根本原因を修正します。
- 修正の前に回帰テストを書きます。再現手順を失敗するテストにし、まずテストが失敗することを確認します。この順序が重要です。修正後に書いたテストでは、バグの存在を証明できません。具体的な手順は
/diagnosing-bugsのフェーズ 5 で扱います。 - 修正は 1 つだけにします。特定した根本原因に対処します。差分には、根本原因を修正する変更だけを含めます。
- 検証します。テストが成功し、ほかのテストも壊れておらず、問題が実際に解決していることを確認します。その後、
/doneを実行します。 - 試行するたびに、「試行 N | 仮説 | 結果」の形式で記録します。3 回失敗したら修正を止め、ユーザーとアーキテクチャ上の問題について話し合います。
- 修正に 3 回失敗した場合は、仮説が外れたのではなく、アーキテクチャの問題として扱います。兆候としては、それぞれの修正によって、ほかの場所にある新たな共有状態や結合が明らかになること、「大規模なリファクタリング」が必要になること、または修正のたびに別の場所で新たな症状が発生することが挙げられます。このパターンが根本的に妥当なのか、それとも惰性で続けているだけなのかを尋ねてください。
ソースで修正した後に、検証を重ねます。その手法については TECHNIQUES.md を参照してください。
危険信号:作業を止め、フェーズ 1 に戻る
次のような考えが浮かんだら、作業を止めてください。
- 「とりあえず応急処置して、あとで調査しよう」
- 「X を変えて、うまくいくか試してみよう」
- 複数の箇所を変更してからテストを実行する
- 「たぶん X だろう。修正してみよう」と、根拠なしに判断する
- 「完全には理解していないけれど、うまくいくかもしれない」
- データの流れをたどる前に修正案を挙げる
- テストの失敗が「実際には問題にならない」理由をこじつける
- すでに 2 回以上失敗しているのに「もう 1 回だけ修正を試す」
- 修正するたびに別の場所で新たな問題が見つかる
ユーザーからの「それは起きていないのでは?」「それで分かるのでしょうか?」「推測はやめてください」といった反応も、同じことを意味します。フェーズ 1 に戻ってください。
調査の結果「根本原因がない」と分かった場合
体系的な調査によって、問題が本当に環境依存、タイミング依存、または外部要因によるものだと判明した場合は、プロセスを完了しています。調査した内容を記録してから、リトライ、タイムアウト、明確なエラーなどの対処を実装し、次の点を明記します。
- 対処する障害モード
- 上流で防止できない理由
- 対処自体が失敗した場合に起きること
次に発生したときに備えて監視を追加します。「根本原因がない」ケースの大半は、調査が不十分なだけです。ここに至る前に、フェーズ 1 の基準を確認してください。