systematic-debugging について
障害、バグ、性能問題、連携不具合を調べる開発者向けの体系的なデバッグスキルです。症状だけを修正せず、根本原因の調査と証拠収集を先に行い、最小の修正を検証します。
主な機能
- 01問題を再現しエラーの証拠を確認
- 02コンポーネント間のデータフロー追跡
- 03正常系と故障系のパターン比較
- 04根本原因の仮説を一つずつ検証
- 05修正前に回帰テストを追加
活用シーン
- 01失敗したテストやビルドの調査
- 02サービス間の連携障害の追跡
- 03一貫しないAPIや検索結果の診断
- 04症状だけの修正を避けた本番バグ対応
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ magnus919/ agent-skills --skill systematic-debuggingインストール後、Claude Code で/systematic-debuggingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdevals/evals.jsonreferences/dependency-source-example.mdmacos-sandbox-debug-example.md詳細
系統的なデバッグ
概要
場当たり的な修正は時間を無駄にし、新たなバグを生みます。応急処置は根本的な問題を覆い隠します。
基本原則: 修正を試みる前に、必ず根本原因を見つけてください。症状だけを修正するのは失敗です。
鉄則
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
フェーズ 1 を完了していない場合、修正を提案してはいけません。
使用する場面
あらゆる技術的な問題に使用します。
- テストの失敗
- 本番環境のバグ
- 予期しない動作
- パフォーマンスの問題
- ビルドの失敗
- 統合に関する問題
特に次の場合:
- 時間に追われている場合(緊急時は当てずっぽうで解決したくなります)
- 「手っ取り早く修正すればよい」と思える場合
- すでに複数の修正を試した場合
- 以前の修正が機能しなかった場合
- 問題を十分に理解できていない場合
次の場合も省略しないでください:
- 問題が単純に見える場合(単純なバグにも根本原因があります)
- 急いでいる場合(焦ると必ずやり直しが発生します)
- 誰かが今すぐ修正するよう求めている場合(体系的に進めるほうが、試行錯誤を繰り返すより速く解決できます)
4 つのフェーズ
次のフェーズに進む前に、各フェーズを完了してください。
フェーズ 1:根本原因の調査
修正を試みる前に必ず行うこと:
1. エラーメッセージを注意深く読む
- エラーや警告を読み飛ばさないでください。正確な解決策が記載されていることがよくあります
- スタックトレースを最後まで読みます。行番号、ファイルパス、エラーコードを確認してください
- 実施事項: エラー箇所に関連するソースファイルを読み、コードベース内でエラー文字列を検索して、関連するすべてのコードパスを見つけます。
2. 一貫して再現する
- 問題を確実に発生させられますか?具体的な手順は何ですか?
- 再現できない場合は、推測せず、さらにデータを集めます
- 実施事項: 失敗するテストを実行するか、バグを発生させます。
pytest tests/test_module.py::test_name -v --tb=long
3. 最近の変更を確認する
- 原因となり得る変更は何ですか?Git の差分、最近のコミット、新しい依存関係、設定の変更を確認します
- 実施事項:
git log --oneline -10
git diff
git log -p --follow src/problematic_file.py | head -100
4. 複数のコンポーネントで構成されるシステムの証拠を集める
システムに複数のコンポーネント(API → サービス → データベース、CI → ビルド → デプロイ)がある場合:
修正を提案する前に、診断用の計測を追加します。コンポーネント間の境界ごとに、次のことを行います。
- コンポーネントに入るデータを記録する
- コンポーネントから出るデータを記録する
- 環境や設定が引き継がれていることを確認する
- 各レイヤーの状態を確認する
証拠を集めるために一度実行し、問題が発生する箇所を特定します。その後、分析して障害が発生しているコンポーネントを特定します。
5a. スキーマと環境の差異を確認する
本番環境ではバグを再現できるが、テストでは再現できない場合:
- テスト用フィクスチャのスキーマと本番スキーマを比較します。
PRAGMA table_info()、\.schema、または同等の方法を使用します - NOT NULL 列、外部キー、一意制約、デフォルト値の不足を確認します
- SQLite のジャーナルモード、接続フラグ、PRAGMA 設定の違いを確認します
- テストデータが列名だけでなく制約も含め、本番環境のデータ構造と一致していることを確認します
簡略化されたテスト用スキーマは、見えにくいバグのよくある原因です。本番テーブルには model TEXT NOT NULL があり、テストテーブルには vector BLOB しかない場合、NOT NULL 制約違反でのみ発生するバグは、テストを問題なく通過します。
実施事項: 両方のデータベースに対して PRAGMA table_info(table_name) を実行し、出力を並べて差分を確認します。
5b. フォールバック処理における例外型の具体性を確認する
try/except のフォールバック処理で、ログに表示されたエラーを捕捉できない場合:
try:
# Primary path — can raise OperationalError
conn.execute("INSERT INTO t (model_name) VALUES (?)", ...)
except sqlite3.OperationalError:
# Fallback — can raise IntegrityError (sibling, not child)
conn.execute("INSERT INTO t (vector) VALUES (?)", ...)
OperationalError と IntegrityError は同じ階層のクラスです。どちらも DatabaseError を継承し、DatabaseError は Error を継承します。一方を捕捉しても、もう一方は捕捉されません。
修正方法は次のいずれかです。
- どちらの経路でも異なるサブタイプのエラーが発生する可能性がある場合は、親クラス(
except sqlite3.DatabaseError)を捕捉する - 最後の手段として
Exceptionを捕捉する(範囲は広いものの、捕捉漏れより安全です) - 想定されるエラー型をそれぞれ明示的に処理する
5c. 段階的な特性評価 — ツールと API の動作
空または一貫性のない結果を返す検索システム、API、ナレッジグラフを調査する場合:
動作するものから始め、失敗するまで範囲を広げます。これにより、失敗の原因となっている変数を特定できます。
| 入力 | 期待値 | 実際の結果 | 診断 |
|---|---|---|---|
既知の単一用語(例:python) | ヒット | ヒット | ツールは動作し、接続も正常です |
同じドキュメント内の2語の語句(type safety) | ヒット | ヒット | 短い語句の検索は動作します |
関連する2語の語句(generic types) | ヒット | 0件 | 境界を特定 — 問題は語句固有です |
| 同じ用語を使った、より長いクエリ | 0件 | 0件 | 偶然ではないことを確認できます |
特性評価を省略しないでください: いきなり「埋め込みモデルが壊れている」と判断するのは推測です。この表によって、変数を1つずつ除外できます。
5d. ライブラリコードを修正する前に依存関係の取得元を確認する
バグの原因がサードパーティの依存関係にあると突き止めた場合:
ライブラリコードにパッチを適用する前に、どこからインストールされているかを確認します。
pip show <package-name>
重要な項目:
- Editable project location — 表示されている場合、これは
pip install -eでインストールされた開発用コピーです。意図的に設定したものですか? - Location — site-packages(本番環境)と一時/開発用ディレクトリのどちらにありますか?
- Version — PyPI の最新バージョンと比較します:
pip index versions <package-name>
ルール: パッケージが編集可能な開発用コピーとしてインストールされていて、意図的に設定したものでない場合は、作業を止めて確認してください。原因は、コードが上流のコードから乖離していることかもしれません。その場合の適切な修正は、フォークにパッチを適用することではなく、本番用パッケージに切り替えることです。
例: バックグラウンドワーカーが table X has no column named Y というエラーでクラッシュしました。調査の結果、/private/tmp/some-fork/ から編集可能なパッケージがインストールされていることが分かりました。開発用フォークでは INSERT 文に列名が追加されていましたが、スキーマのマイグレーションは追加されていませんでした。正しい修正はフォークにマイグレーションを追加することではなく、本番用の PyPI パッケージに切り替えて、開発用コピーを削除することでした。
6a. 推測する前に調査する — 系統的なウェブ検索
ローカルで確認できる証拠をすべて集めても、根本原因が分からない場合:
解決策を推測しないでください。構造化されたウェブ調査を行います。
- 正確なエラーメッセージで検索する — エラーを引用し、エラーコードと関数名を含めます
- 症状とプラットフォームの情報を組み合わせて検索する — 発生した問題と OS/ツールのバージョンを組み合わせます
- コンポーネント/サービス名で検索する — XPC サービス、デーモン、サブシステムには、文書化されたバグがあることがよくあります
- 最近のスレッドを優先する — 現行バージョンに絞り込みます。古い解決策は当てはまらない場合があります
- 解決策を提案する前にスレッド全体を読む — 一部だけ読むと、修正も不完全になります
- 最初の診断だけでなく、スレッドの最後に確定した回避策がないか確認する
6b. macOS アプリのトラブルシューティング — サンドボックス化されたアプリケーション
macOS アプリ(特に Books、Music、App Store アプリなど、サンドボックス化されたアプリ)をデバッグする場合:
アプリは ~/Library/Containers/<bundle-id>/ 配下のサンドボックスコンテナ内に隔離されています。
コンテナを特定する
ls ~/Library/Containers/<bundle-id>/
# Data/Library/ — preferences, caches, databases
# Data/Documents/ — user-visible content, import queues
XPC サービスの連携先を確認する
多くの Apple アプリでは、ファイル操作にバックグラウンドの XPC サービスを使用します。
# XPC services live in the framework bundle or app bundle:
/System/Library/PrivateFrameworks/<Framework>.framework/XPCServices/
/System/Applications/<App>.app/Contents/XPCServices/
ps aux | grep -i "<service-name>"
データベースを直接読み取る
サンドボックス化されたアプリでは、SQLite/CoreData がよく使用されます。
sqlite3 ~/Library/Containers/<bundle-id>/Data/Documents/<path>.sqlite ".tables"
sqlite3 ~/Library/Containers/<bundle-id>/Data/Documents/<path>.sqlite "SELECT * FROM ZTABLE LIMIT 10;"
システムログを確認する
log show --predicate 'process == "AppName"' --last 10m --style compact
log stream --predicate 'process == "AppName"' --style compact
TCC のアクセス許可をリセットする
アプリがサンドボックスの外部にあるファイルにアクセスできない場合(インポートが通知なしで失敗する場合):
tccutil reset All com.apple.bundle-id
I/O の境界を把握する
- セキュリティスコープ付きブックマーク(ドラッグ&ドロップまたは
openコマンドで取得)は1回限りです。インポートに失敗するとブックマークは消費され、以降の試行も通知なしで失敗します。 - NSOpenPanel(「ファイル」>「読み込む」ダイアログ)は新しいブックマークを作成するため、テストではこちらのほうが信頼性があります。
open -b bundle-id file.extが「ダウンロード」からは動作するのに「デスクトップ」からは動作しない場合、TCC/階層化されたアクセス権の問題である可能性が高いです(macOS では「ダウンロード」へのアクセス権がより緩やかに設定されています)。
ローカル同期とクラウド同期の破損を見分ける
コンテナをリセットするとローカル状態は修復されますが、iCloud 同期の破損は修復されません。クラウド側に問題がある兆候:
- コンテナを完全に削除して再インストールしても問題が続く
- 再起動後は一度インポートできるが、その後劣化する
- 複数のデバイスで同じ問題が発生する
対応: ローカルのリセットで修復されない場合、iCloud の同期状態が破損している可能性があります。システム設定 → Apple ID → iCloud → ストレージを管理 → [App] → すべてのデータを削除は、最後の手段です。
復旧方法(上から順に試す)
- XPC サービスを終了して再起動します(
kill -9 <PID>)— 一時的な対応です。XPC は再生成されます - アプリのコンテナをリセットします(
rm -rf ~/Library/Containers/<bundle-id>/) - TCC 権限をリセットします(
tccutil reset All <bundle-id>) - Mac を再起動します
- アプリの iCloud データを削除します
- macOS のテストユーザーを作成します — そこでアプリが動作する場合、問題はシステムではなくユーザーライブラリにあります
6c. データフローを追跡する
呼び出しスタックの深い場所にエラーがある場合:
- 不正な値はどこで発生していますか?
- どの処理が、この関数に不正な値を渡しましたか?
- 発生源が見つかるまで上流へ追跡します
- 症状ではなく発生源を修正します
対応: コードベースで関数の参照箇所や変数への代入を検索し、データの経路を追跡します。
フェーズ 1 完了チェックリスト
- エラーメッセージをすべて読み、理解した
- 問題を一貫して再現できた
- 最近の変更を特定して確認した
- 証拠(ログ、状態、データフロー)を収集した
- 問題を特定のコンポーネント/コードに切り分けた
- 根本原因の仮説を立てた
停止: なぜ発生しているのかを理解するまで、フェーズ 2 に進まないでください。
フェーズ 2:パターン分析
修正する前にパターンを見つけます:
1. 動作する例を見つける
- 同じコードベース内で、類似する動作中のコードを見つけます
- 壊れているものと似ていて、正常に動作するものは何ですか?
2. 参照実装と比較する
- パターンを実装する場合は、参照実装を完全に読んでください。ざっと読むだけではいけません
- 適用する前に、パターンを十分に理解します
3. 相違点を特定する
- 動作するものと壊れているものの違いは何ですか?
- どんなに小さくても、すべての違いを書き出します
- 「これは関係ない」と決めつけないでください
- 対応: コードベースで類似のパターンを検索し、比較します。
4. 依存関係を理解する
- これには他にどのコンポーネントが必要ですか?
- どの設定、構成、環境が必要ですか?
- どのような前提を置いていますか?
フェーズ 3:仮説とテスト
科学的手法:
1. 仮説を1つ立てる
- 「Y が原因なので、根本原因は X だと思います」と明確に述べます
- 書き留めます。曖昧にせず、具体的にします
2. 最小限のテストを行う
- 仮説を検証するために、可能な限り最小の変更を行います
- 一度に変更するのは1つの変数だけです
- 一度に複数のことを修正しないでください
3. 続ける前に検証する
- 動作しましたか? → フェーズ 4
- 動作しませんでしたか? → 新しい仮説を立てます
- 修正をさらに重ねないでください
4. わからないとき
- 知っているふりをせず、「X が理解できません」と伝えます
- 助けを求めます。さらに調査します
フェーズ 4:実装
症状ではなく根本原因を修正します:
1. 失敗するテストケースを作成する
- 可能な限り簡単に再現します。可能であれば自動テストにします
- 修正前に必ず用意します
- テスト環境は本番環境を反映している必要があります。実際のスキーマに照らしてテストフィクスチャを監査します
2. 修正を1つ実装する
- 特定した根本原因に対処します。一度に変更するのは1つだけです
- 「ついでに」改善することはしません。リファクタリングもまとめて行いません
3. 修正を検証する
# Run the specific regression test
pytest tests/test_module.py::test_name -v
# Run full suite — no regressions
pytest tests/ -q
4. 修正がうまくいかない場合 — 3回のルール
- 停止します。
- これまでに試した修正の回数を数えます
- 3回未満の場合:フェーズ 1 に戻り、新しい情報を踏まえて再分析します
- 3回以上の場合:停止してアーキテクチャを見直します(下記の手順 5)
- アーキテクチャについて話し合う前に、修正 4 を試さないでください
5. 修正に3回以上失敗した場合:アーキテクチャを見直す
アーキテクチャの問題を示すパターン:
- 修正するたびに、別の場所で新たな共有状態や結合が見つかる
- 修正の実装に「大規模なリファクタリング」が必要になる
- 修正するたびに、別の場所で新たな症状が発生する
作業を止め、基本を見直してください:
- このパターンは根本的に妥当ですか?
- 「ただ惰性で続けている」だけではありませんか?
- 症状の修正を続けるのではなく、アーキテクチャをリファクタリングすべきではありませんか?
これ以上修正を試みる前に話し合ってください。 これは仮説が外れたのではなく、アーキテクチャが間違っているということです。
危険信号 — 作業を止め、手順に従う
次のように考えていることに気づいた場合:
- 「とりあえず手早く修正して、調査は後で」
- 「Xを変更して、うまくいくか試してみよう」
- 「変更をいくつか加えて、テストを実行しよう」
- 「テストは省略して、手動で確認しよう」
- 「おそらくXだろう。修正してみよう」
- 「完全には理解していないけれど、うまくいくかもしれない」
- 「主な問題は次のとおりです:[調査せずに修正案を列挙する]」
- データフローを追跡する前に解決策を提案する
- 「もう一度だけ修正を試す」(すでに2回以上試している場合)
- 修正のたびに別の場所で新たな問題が見つかる
どれも、作業を止めてフェーズ1に戻る必要があることを意味します。
修正に3回以上失敗した場合: アーキテクチャを見直してください(フェーズ4、ステップ5)。
調査の進め方 — 前進し続ける
調査中にユーザーが「進展はあったけれど、まだ終わっていない」のように言った場合:
確認の質問をして流れを止めないでください。調査を続けてください。さらに証拠を集め、次の診断ステップを試し、別の観点から確認してください。「Xを試したとき、何が起きましたか?」と尋ねて調査を中断すると、ユーザーの注意を無駄にします。
調査を続けるべきサイン:
- 「進展はあったけれど、まだ終わっていない」 — 調査を続ける
- ユーザーが確認の質問に答えない — 質問ではなく、対応を求めている
- 「わかった」または「まだ直っていない」 — 許可を求めず、次のステップを試す
代わりにすべきこと: ログ、データベース、ファイルの状態から情報を導き出してください。ユーザーを計測手段として使わないでください。入力がなければ本当に先に進めず、かつ自分でできる方法をすべて試した場合にのみ質問してください。
よくある正当化
| 言い訳 | 実際 |
|---|---|
| 「問題は簡単だから、手順は不要」 | 簡単な問題にも根本原因があります。手順に従えば、簡単なバグは素早く解決できます。 |
| 「緊急なので、手順を踏む時間がない」 | 系統的なデバッグは、当てずっぽうで試行錯誤するより速く進みます。 |
| 「まずこれを試してから調査しよう」 | 最初の修正が、その後の進め方を決めます。最初から正しく行ってください。 |
| 「修正が機能すると確認してからテストを書こう」 | テストしていない修正は定着しません。まずテストを書けば、それを確認できます。 |
| 「一度に複数の修正をすれば時間を節約できる」 | 何がうまくいったか切り分けられず、新たなバグの原因になります。 |
| 「問題がわかった。修正しよう」 | 症状が見えていることと、根本原因を理解していることは別です。 |
| 「もう一度だけ修正を試す」(2回以上失敗した後) | 3回以上の失敗は、アーキテクチャの問題を示します。パターンを見直してください。 |
クイックリファレンス
| フェーズ | 主な作業 | 成功基準 |
|---|---|---|
| 1. 根本原因 | エラーを読む、再現する、変更を確認する、証拠を集める、データフローを追跡する | 何が、なぜ起きているかを理解する |
| 2. パターン | 動作する例を探す、比較する、違いを特定する | 何が異なるか把握する |
| 3. 仮説 | 理論を立て、最小限のテストを行い、一度に1つの変数を試す | 仮説を確認するか、新たな仮説を立てる |
| 4. 実装 | 回帰テストを作成する、根本原因を修正する、検証する | バグが解決し、すべてのテストが成功する |
参考資料
- references/dependency-source-example.md — ライブラリの依存関係でスキーマの不整合を引き起こした、編集可能な開発用フォークを検出する実例です。フェーズ1のステップ5dで、バグの原因としてサードパーティパッケージが疑われた場合に読んでください。
- references/macos-sandbox-debug-example.md — Apple Books.app のインポート失敗をデバッグする完全な手順を示し、フェーズ1のステップ6bで扱う macOS サンドボックスの手法を説明します。macOS のサンドボックス化されたアプリケーションをデバッグする場合に読んでください。