mcpField
S
systematic-debugging

systematic-debugging

github.com/ckorhonen開発2252 回インストール5 週間前に更新
リポジトリ

systematic-debugging について

Systematic Debugging は、バグやテスト失敗、性能問題などを調べる開発者向けのスキルです。根本原因の調査、パターン分析、仮説検証、実装の4段階で進め、修正前の証拠収集を重視します。

主な機能

  1. 014段階の調査プロセス
  2. 02再現と証拠の収集
  3. 03変更確認とデータフロー追跡
  4. 04仮説を一つずつ検証
  5. 05根本原因に対する修正の確認

活用シーン

  1. 01本番環境のバグ調査
  2. 02テストやビルドの失敗診断
  3. 03複数コンポーネントにまたがる問題の追跡

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ckorhonen/claude-skills --skill systematic-debugging

インストール後、Claude Code で/systematic-debuggingを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

condition-based-waiting-example.ts
condition-based-waiting.md
CREATION-LOG.md
defense-in-depth.md
find-polluter.sh
root-cause-tracing.md
test-academic.md
test-pressure-1.md
test-pressure-2.md
test-pressure-3.md

詳細

体系的なデバッグ

概要

場当たり的な修正は時間を浪費し、新たなバグを生みます。応急処置的なパッチは根本的な問題を覆い隠してしまいます。

中核原則: 修正を試みる前に、必ず根本原因を特定してください。症状への対処は失敗です。

このプロセスの文言に違反することは、デバッグの精神に違反することです。

鉄則

NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST

フェーズ 1 を完了していなければ、修正を提案することはできません。

使用するタイミング

あらゆる技術的な問題に使用してください:

  • テストの失敗
  • 本番環境でのバグ
  • 予期しない動作
  • パフォーマンスの問題
  • ビルドの失敗
  • 統合の問題

特に次のような場合に使用してください:

  • 時間的な圧力がかかっているとき(緊急時は推測に走りたくなります)
  • 「ちょっとした修正を一つ」が明らかに正解に思えるとき
  • すでに複数の修正を試したとき
  • 前回の修正がうまくいかなかったとき
  • 問題を完全には理解していないとき

次の場合でも省略しないでください:

  • 問題が単純に見えるとき(単純なバグにも根本原因はあります)
  • 急いでいるとき(急ぐと必ず手戻りが発生します)
  • マネージャーが「今すぐ直せ」と求めるとき(体系的に進める方が、やみくもに試すより速いです)

4 つのフェーズ

次のフェーズに進む前に、各フェーズを必ず完了しなければなりません。

フェーズ 1: 根本原因の調査

いかなる修正を試みる前にも:

  1. エラーメッセージを注意深く読む

    • エラーや警告を読み飛ばさないでください
    • 正解そのものが書かれていることが多いです
    • スタックトレースを最後まで読んでください
    • 行番号、ファイルパス、エラーコードをメモしてください
  2. 一貫して再現させる

    • 確実に再現できますか?
    • 正確な手順は何ですか?
    • 毎回発生しますか?
    • 再現しない場合は、推測せずにさらにデータを収集してください
  3. 最近の変更を確認する

    • この問題を引き起こしうる変更は何ですか?
    • Git の差分、最近のコミット
    • 新しい依存関係、設定の変更
    • 環境の違い
  4. 複数コンポーネントから成るシステムで証拠を収集する

    システムが複数のコンポーネントを持つ場合(CI → ビルド → 署名、API → サービス → データベース):

    修正を提案する前に、診断用の計測を追加してください:

    For EACH component boundary:
      - Log what data enters component
      - Log what data exits component
      - Verify environment/config propagation
      - Check state at each layer
    
    Run once to gather evidence showing WHERE it breaks
    THEN analyze evidence to identify failing component
    THEN investigate that specific component
    

    例(多層システム):

    # Layer 1: Workflow
    echo "=== Secrets available in workflow: ==="
    echo "IDENTITY: ${IDENTITY:+SET}${IDENTITY:-UNSET}"
    
    # Layer 2: Build script
    echo "=== Env vars in build script: ==="
    env | grep IDENTITY || echo "IDENTITY not in environment"
    
    # Layer 3: Signing script
    echo "=== Keychain state: ==="
    security list-keychains
    security find-identity -v
    
    # Layer 4: Actual signing
    codesign --sign "$IDENTITY" --verbose=4 "$APP"
    

    これによって判明すること: どの層で失敗しているか(シークレット → ワークフロー ✓、ワークフロー → ビルド ✗)

  5. データフローを追跡する

    エラーがコールスタックの深い位置で発生している場合:

    後方追跡の完全な手法については、このディレクトリにある root-cause-tracing.md を参照してください。

    簡易版:

    • 不正な値はどこから発生しているのか?
    • 不正な値を渡したのは何か?
    • 発生源が見つかるまで追跡を続けてください
    • 症状ではなく発生源で修正してください
フェーズ 2: パターン分析

修正する前にパターンを見つけてください:

  1. 動作する例を見つける

    • 同じコードベース内で、類似した動作するコードを探してください
    • 壊れている部分に似ていて、動作しているものは何ですか?
  2. 参照実装と比較する

    • パターンを実装する場合は、参照実装を最後まで完全に読んでください
    • 流し読みせず、すべての行を読んでください
    • パターンを完全に理解してから適用してください
  3. 相違点を特定する

    • 動作しているものと壊れているものの違いは何ですか?
    • どんなに小さくても、すべての相違点を書き出してください
    • 「それは影響しないはずだ」と決めつけないでください
  4. 依存関係を理解する

    • このコンポーネントには、他にどのコンポーネントが必要ですか?
    • どのような設定、構成、環境が必要ですか?
    • どのような前提を置いていますか?
フェーズ 3: 仮説とテスト

科学的方法:

  1. 単一の仮説を立てる

    • 「Y という理由で X が根本原因だと考える」と明確に述べてください
    • 書き出してください
    • 曖昧にせず、具体的にしてください
  2. 最小限のテストを行う

    • 仮説を検証するために、可能な限り最小の変更を加えてください
    • 一度に変更する変数は 1 つだけにしてください
    • 複数のことを同時に修正しないでください
  3. 続行する前に検証する

    • うまくいきましたか? はい → フェーズ 4 へ
    • うまくいきませんでしたか? 新しい仮説を立ててください
    • 既存の修正の上に、さらに修正を重ねないでください
  4. わからないときは

    • 「X がわからない」と述べてください
    • 知っているふりをしないでください
    • 助けを求めてください
    • さらに調査してください
フェーズ 4:実装

症状ではなく、根本原因を修正します:

  1. 失敗するテストケースを作成する

    • 可能な限りシンプルに再現する
    • 可能であれば自動テストにする
    • テストフレームワークがない場合は、一度限りのテストスクリプトを作成する
    • 修正前に必ず作成する
    • 適切な失敗テストを書くには、superpowers:test-driven-development スキルを使用する
  2. 修正を 1 つだけ実装する

    • 特定した根本原因に対処する
    • 一度に変更するのは 1 つだけ
    • 「ついでの」改善はしない
    • リファクタリングをまとめて行わない
  3. 修正を検証する

    • テストは通るようになったか?
    • ほかのテストは壊れていないか?
    • 問題は実際に解決したか?
  4. 修正がうまくいかない場合

    • 停止する
    • これまでに何回修正を試したか数える
    • 3 回未満の場合:フェーズ 1 に戻り、新しい情報をもとに再分析する
    • 3 回以上の場合:停止して、アーキテクチャを再検討する(下記のステップ 5)
    • アーキテクチャについて話し合う前に、修正を 4 回目も試してはいけない
  5. 修正に 3 回以上失敗した場合:アーキテクチャを再検討する

    アーキテクチャの問題を示すパターン:

    • 修正するたびに、別の箇所で新たな共有状態/結合/問題が明らかになる
    • 実装に「大規模なリファクタリング」が必要になる
    • 修正するたびに、別の場所で新たな症状が発生する

    停止して、根本的な前提を問い直す:

    • このパターンは根本的に妥当か?
    • 「ただ惰性で続けている」だけではないか?
    • 症状の修正を続けるのではなく、アーキテクチャをリファクタリングすべきではないか?

    これ以上修正を試みる前に、人間のパートナーと話し合ってください

    これは仮説の失敗ではありません。アーキテクチャが間違っています。

危険信号 — 停止して手順に従う

次のように考えていることに気づいたら:

  • 「ひとまず手早く直して、調査は後で」
  • 「X を変えて、うまくいくか試してみよう」
  • 「変更を複数加えてから、テストを実行しよう」
  • 「テストは省略して、手動で確認しよう」
  • 「おそらく X だろう。修正してみよう」
  • 「完全には理解していないけれど、これでうまくいくかもしれない」
  • 「パターンでは X とあるけれど、別の方法に変えてみよう」
  • 「主な問題はこれです:[調査せずに修正を列挙]」
  • データフローを追跡する前に解決策を提案する
  • 「修正をもう 1 回試そう」(すでに 2 回以上試している場合)
  • 修正するたびに、別の箇所で新たな問題が明らかになる

これらはすべて、停止してフェーズ 1 に戻るべきサインです。

修正に 3 回以上失敗した場合: アーキテクチャを再検討してください(フェーズ 4.5 を参照)

人間のパートナーが示す、進め方が間違っているサイン

次のような指摘があったら注意してください:

  • 「それは実際に起きていないのでは?」 — 確認せずに決めつけています
  • 「それで分かるのは……?」 — 証拠を集めるべきでした
  • 「当てずっぽうはやめて」 — 理解せずに修正を提案しています
  • 「徹底的に考えて」 — 症状だけでなく、根本的な前提を問い直してください
  • 「行き詰まっているのでは?」(苛立った様子)— その進め方はうまくいっていません

このような反応があったら: 停止して、フェーズ 1 に戻ってください。

よくある正当化

言い訳実情
「問題は単純だから、手順は不要」単純な問題にも根本原因があります。単純なバグなら、手順に従ったほうが早く解決できます。
「緊急事態だから、手順に従う時間はない」体系的なデバッグは、当てずっぽうの試行錯誤を繰り返すより速く進みます。
「まずこれを試してから調査しよう」最初の修正が、その後の進め方を決めます。最初から正しく進めてください。
「修正がうまくいくと確認してからテストを書こう」テストされていない修正は定着しません。先にテストを書けば、その効果を確認できます。
「一度に複数修正すれば時間を節約できる」何が効いたのか特定できず、新たなバグの原因になります。
「参照資料が長すぎるから、パターンを応用しよう」部分的な理解は、必ずバグにつながります。最後まで読んでください。
「問題が分かった。修正しよう」症状が見えていることと、根本原因を理解していることは別です。
「修正をもう 1 回試そう」(2 回以上失敗した後)3 回以上失敗したら、アーキテクチャに問題があります。再度修正するのではなく、パターンを問い直してください。

よくある落とし穴

デバッグの実践で確認されているアンチパターンです。認識したら、すぐに進め方を正してください。関連する指針については、危険信号 と よくある正当化 も参照してください。

1. 再現する前に解決策に飛びつく(早まった修正)

アンチパターン:

Error log shows "timeout" → assume network issue → add retry logic
(But you never actually reproduced the timeout)

問題:

  • 検証していない症状に対処する修正になっています
  • 実際の問題は修正されず、「修正」によって隠れたままです
  • 症状が再発すると、自分のパッチのデバッグに時間を費やすことになります

回避方法:

  • **フェーズ 1 の要件:**既知の手順でバグを確実に発生させられますか?
  • 正確な再現手順を記録します:「X を Y に設定して npm test を実行する」→ バグが発生する
  • 再現できなければ、修正が機能するか検証できません
  • 再現性は必須です。発生させられるまで、さらにデータを集めます

対処方法:

  • 作業を止めて、フェーズ 1 に戻ります
  • 正確な再現手順を見つけます
  • その後、フェーズ 2 に進みます

2. 一度に複数の変更を行う(根本原因を切り分けられない)

アンチパターン:

Change 3 things:
  - Update dependency version
  - Refactor retry logic
  - Add fallback handler
Run tests → passes

問題:

  • どの変更で修正されたのか分かりません
  • 変更の 1 つが微妙なバグを引き起こしても、数か月間気づかない可能性があります
  • 今後、このコードパスでバグが発生したときに、原因の特定がさらに難しくなります

回避方法:

  • フェーズ 3 の要件:「最小限のテストを行い、可能な限り最小の変更を加えます」
  • 一度に変更するのは 1 つの変数だけです
  • 変更するたびにテストします
  • 何が修正したのか切り分けられない場合、その修正についての理解が不十分です

対処方法:

  • すべての変更を元に戻し、やり直します
  • 1 つだけ変更して、テストします
  • 「この変更だけで修正できた理由は[理由]です」と記録します
  • 次の仮説についても同様に繰り返します

3. バグの原因は最近の変更だと思い込む(環境やデータを無視する)

アンチパターン:

Feature shipped 3 days ago → bug happened today → bug must be in that feature
(But you didn't verify the feature code against current data/environment)

問題:

  • 最近のコードは怪しく見えても、環境の変更には気づきにくいものです
  • 実際には問題のないコードのデバッグに、何日も費やすことになります
  • 実際の原因は、データの破損、環境設定の誤り、依存関係の更新かもしれません

回避方法:

  • フェーズ 1 の要件:「最近の変更の確認」には次の項目が含まれます
    • コードに Git の差分がありますか?はい。
    • 依存関係の更新はありますか?はい。
    • 環境の変更はありますか?はい。
    • データのマイグレーションはありますか?はい。
    • 設定の変更はありますか?はい。
  • 仮説を検証します。最近のコード変更を元に戻し、バグが続くか確認します
  • 最近の変更を元に戻してもバグが発生する場合、原因は環境にあります
  • 複数のコンポーネントで構成されるシステムでは、診断用の計測機能を使います(フェーズ 1 のステップ 4 を参照)

対処方法:

  • 過去 1 週間の変更をすべてリストアップします:コード、依存関係、設定、環境、データ
  • 環境やデータに起因する原因を体系的に除外します
  • その後で初めて、コードに注目します

4. 再現手順を記録しない(修正を検証できない)

アンチパターン:

"Fixed the bug" (but didn't write down how to trigger it)
Weeks later: "Wait, does this still happen? I don't remember how to trigger it"

問題:

  • 修正が本当に機能するか検証できません
  • 同僚にテスト方法を伝えられません
  • リグレッションを検出できません

回避方法:

  • フェーズ 1 の要件:「一貫して再現する」
    • 正確な手順を書き留めます
    • 形式:「再現手順:[手順 1]、[手順 2]、[手順 3]」
  • フェーズ 2 に進む前に、これらの手順を PR/Issue のコメントに追加します
  • 修正を実装した後、同じ手順を再実行して検証します
  • 再現手順をテストケースにします(フェーズ 4、ステップ 1)

対処方法:

  • 今すぐ再現手順を記録します
  • 正確な手順を再実行するまでは、修正が完了したと主張しないでください
  • 再現手順を記録しており、修正後にバグを再現できなくなった場合、修正が機能していることの確認になります

5. ログや可観測性なしでデバッグする(手探りで進める)

アンチパターン:

Error happens in production
You: "Probably a race condition?"
You don't have logs showing what was happening at that moment
You guess at fixes for 3 hours

問題:

  • 根拠なしに判断しています
  • 修正を推測すると、新たなバグが発生します
  • 原因を理解していないため、同じエラーが再発します

回避方法:

  • フェーズ 1 の要件:「複数のコンポーネントで構成されるシステムの証拠を集める」
  • デバッグを始める前に、「どのようなログがありますか?」と確認します
  • ログが不足している場合は、一時的なログを追加して、ローカルで再現します
  • 何が起きているかを示す証拠なしに、修正を提案しないでください
  • 本番環境の問題では、障害発生時のログやメトリクスを取得します

具体的な方法:

# Reproduce with logging
DEBUG=* npm test           # Enable all logs
strace node app.js         # System call tracing
tcpdump -i any -w dump     # Network tracing

対処方法:

  • いったん中断し、重要な判断ポイントにログを追加します
  • ログを強化した状態で問題を再現します
  • これで仮説を立てるための証拠が得られます
  • その後、修正を提案します

6. 失敗するテストケースを用意しない(検証のふり)

アンチパターン:

Implement fix
Run manual test: "Looks good"
Commit and push
(No automated test proving bug existed)

問題点:

  • 何かを「修正」したものの、それが壊れていたことを一度も証明していません
  • 後で回帰テストを行うと、バグがまだ発生することが分かります
  • 「修正できた」のか「運よく直った」のかを区別できません

回避方法:

  • フェーズ 4 の要件:「失敗するテストケースを作成する」
    • 修正前にテストが失敗することが必須です
    • 修正後にテストが成功することが必須です
    • テストによってバグが存在したことと、修正によってバグが解消されたことを証明します
  • 失敗するテストなしに修正を実装しないでください
  • 自動テスト(ユニットテスト、統合テスト、一時的なスクリプト)を手動検証より優先します

復旧方法:

  • 修正を元に戻します
  • バグを再現するテストを書きます
  • テストが失敗することを確認します
  • 修正を実装します
  • テストが成功することを確認します

7. 失敗しているコンポーネントを切り分けない(すべてを責める)

アンチパターン:

Database slow? Maybe it's the query. Maybe it's the network. Maybe it's CPU.
Let me add caching, fix indexes, reduce batch size, and upgrade servers.

問題点:

  • 壊れていないものの修正にリソースを浪費します
  • 実際のボトルネックを把握できません
  • 何を最適化すべきか分からないため、今後の最適化が失敗します

回避方法:

  • フェーズ 1 の要件:「複数コンポーネントのシステムで証拠を集める」
    • 各コンポーネントの境界で、入ってくるデータと出ていくデータを記録します
    • どのコンポーネントが遅い、または失敗しているのかを特定します
    • そのコンポーネントに調査を集中します
    • 5 つのことを最適化するのではなく、ボトルネックを最適化します

例:

# API slow. Which part?
time curl /api/endpoint              # Total time
# Add logging to API entry point
# Add logging before DB query
# Add logging after DB returns
# Add logging before response

# Now you know: API fast (10ms), DB query slow (2s) → focus on DB

復旧方法:

  • 診断用の計測機能を使って、実際にどのコンポーネントが失敗しているのかを特定します
  • 特定のコンポーネントを切り分けるまでは、修正を提案しないでください

8. 修正が完了したと思い込む(エッジケースの見落とし)

アンチパターン:

Test case passes for happy path
Deploy without testing error cases
(Bug appears 2 weeks later under edge condition you didn't think to test)

問題点:

  • ある状況に対する修正が、ほかの状況を壊します
  • バグの 80% はエッジケースに潜んでいます

回避方法:

  • フェーズ 4 の要件:「修正を検証する」
    • 成功するケースをテストします
    • エラーケースをテストします
    • 境界条件をテストします
    • 無効なデータ、空のデータ、極端なデータでテストします
    • ほかのコンポーネントとの相互作用をテストします
  • デプロイ前に包括的なテストカバレッジを確保します
  • 修正した各バグについて回帰テストを追加します

復旧方法:

  • エッジケースのテストを追加します
  • 包括的なテストスイートを再実行します
  • すべてのケースに合格するまで、修正が完了したと宣言しないでください

これらの落とし穴は、一度きりのミスではなく、繰り返し現れるパターンです。自分がその一つに陥っていると気づいたら、作業を止めて手順に立ち戻ってください。

クイックリファレンス

フェーズ主な作業成功基準
1. 根本原因エラーを読み、再現し、変更を確認し、証拠を集める何が、なぜ起きたかを理解する
2. パターン動作する例を探し、比較する相違点を特定する
3. 仮説理論を立て、最小限のテストを行う仮説を確認するか、新たな仮説を立てる
4. 実装テストを作成し、修正し、検証するバグが解消され、テストに合格する

プロセスの結果「根本原因なし」と判明した場合

体系的な調査によって、問題が本当に環境依存、タイミング依存、または外部要因によるものだと判明した場合:

  1. プロセスを完了しています
  2. 調査した内容を記録します
  3. 適切な対処(再試行、タイムアウト、エラーメッセージ)を実装します
  4. 今後の調査に備えて、監視やログ記録を追加します

ただし: 「根本原因がない」ケースの 95% は、調査が不十分です。

補助的な手法

これらの手法は体系的なデバッグの一部であり、このディレクトリから利用できます:

  • root-cause-tracing.md - コールスタックをさかのぼってバグを追跡し、最初の引き金を特定します
  • defense-in-depth.md - 根本原因を見つけた後、複数の層に検証を追加します
  • condition-based-waiting.md - 任意のタイムアウトを条件ポーリングに置き換えます

関連するスキル:

  • superpowers:test-driven-development - 失敗するテストケースを作成する場合(フェーズ 4、ステップ 1)
  • superpowers:verification-before-completion - 成功を宣言する前に修正が機能したことを検証します

実際の効果

デバッグセッションでの比較:

  • 体系的なアプローチ:修正まで 15~30 分
  • 手当たり次第に修正するアプローチ:2~3 時間の試行錯誤
  • 初回で修正できる率:95% 対 40%
  • 新たに発生するバグ:ほぼゼロ 対 頻繁に発生

コメント