Systematic Debugging
目次
- skill の情報
- 参考: SKILL.md 全文
- 概要
- 鉄の掟
- フィードバックループの決まり
- 使いどころ
- 4 つの段階
- 段階 1: 根本原因の調査
- 1. エラーメッセージをていねいに読む
- 2. きつく絞ったフィードバックループを作る
- 3. 最近の変更を確かめる
- 4. 部品が複数ある系では証拠を集める
- 5. データの流れをたどる
- 段階 1 の確認リスト
- 段階 2: パターンの分析
- 0. 再現を最小にする
- 1. 動いている例を探す
- 2. 手本と見比べる
- 3. 違いを洗い出す
- 4. 依存を把握する
- 段階 3: 仮説と検証
- 1. 反証できる仮説を順位づけて立てる
- 2. 最小の形で試す
- 3. 次に進む前に確かめる
- 4. わからないとき
- 段階 4: 実装
- 1. 失敗するテストを用意する
- 2. 修正はひとつだけ入れる
- 3. 修正を検証する
- 4. 修正が効かないとき — 3 回の掟
- 5. 修正が 3 回以上失敗したら: 設計を疑う
- 危険なサイン — 止まって手順に戻る
- よくある言い訳
- 早見表
- Hermes Agent との組み合わせ
- 調査に使うツール
- delegate_task と組み合わせる
- test-driven-development と組み合わせる
- 実際の効き目
4 つの段階で根本原因を突き止めます。直す前に、まず不具合を理解します。
skill の情報
| 提供元 | 最初から入っています |
| パス | skills/software-development/systematic-debugging |
| バージョン | 1.1.0 |
| 作者 | Hermes Agent(obra/superpowers から取り入れています) |
| ライセンス | MIT |
| 対応プラットフォーム | linux, macos, windows |
| タグ | debugging, troubleshooting, problem-solving, root-cause, investigation |
| 関連 skill | test-driven-development, plan, subagent-driven-development |
参考: SKILL.md 全文
順序立てたデバッグ
概要
当てずっぽうの修正は時間を浪費し、新しい不具合を生みます。その場しのぎの手当ては、奥にある問題を覆い隠します。
基本の考え方: 修正に手をつける前に、必ず根本原因を突き止めます。症状だけを直すのは失敗です。
この手順の文言を破ることは、デバッグの精神を破ることです。
鉄の掟
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST段階 1 を終えていないなら、修正案を出すことはできません。
フィードバックループの決まり
フィードバックループこそがデバッグの本体です。コードを読んで仮説を組み立てる前に、利用者が遭遇したその症状でちゃんと赤(失敗)になり、不具合が直れば緑(成功)になる、きつく絞ったコマンドを用意するか、すでにあるものを見つけます。きつく絞ったループとは、速く、毎回同じ結果になり、エージェントが自分で実行でき、そして「落ちなければよい」ではなくこの不具合をつかまえられるだけの具体性がある、ということです。
きれいな再現が難しいときほど、ループづくりに時間をかけます。赤にできるループがないまま推測することこそ、この skill が防ごうとしている失敗の形です。
使いどころ
技術的な問題なら、どれにでも使います。
- テストの失敗
- 本番で起きている不具合
- 想定と違うふるまい
- 性能の問題
- ビルドの失敗
- 連携まわりの問題
とくに次のときに使ってください。
- 時間に追われているとき(急ぎの場面ほど当て推量に流れます)
- 「ちょっと直すだけ」で済みそうに見えるとき
- すでに複数の修正を試したあと
- 前の修正が効かなかったとき
- 問題をまだ十分に理解できていないとき
次のような理由で飛ばしてはいけません。
- 単純そうに見える(単純な不具合にも根本原因はあります)
- 急いでいる(急ぐほど手戻りが確定します)
- 今すぐ直してほしいと言われている(もがくより順序立てたほうが速いです)
4 つの段階
次の段階に進む前に、必ずその段階を終わらせてください。
段階 1: 根本原因の調査
どんな修正にも手をつける前に:
1. エラーメッセージをていねいに読む
- エラーや警告を読み飛ばさない
- そこに答えそのものが書かれていることがよくあります
- スタックトレースは最後まで読む
- 行番号、ファイルパス、エラーコードを控える
やること: 関係するソースファイルを read_file で読みます。エラー文字列をコードベースから探すには search_files を使います。
2. きつく絞ったフィードバックループを作る
- 利用者が遭遇したその症状を、コマンド 1 本で起こせますか
- そのコマンドは、この不具合があるときに失敗し、直ったときにだけ通りますか
- 何度も回せるくらい速いですか
- 毎回同じ結果になりますか。ときどきしか起きない不具合なら、デバッグできる程度まで再現率を上げられますか
- 再現できないなら、推測せずにデータをもっと集めます。
ループの組み立て方 — だいたいこの順で試します:
- 不具合まで届く継ぎ目に置いた失敗するテスト。単体でも、結合でも、端から端までのものでもかまいません。
- 動かしている開発サーバーに向けた HTTP スクリプトや curl。
- 決まった入力を与える CLI の実行。標準出力・標準エラーを期待した出力と突き合わせます。
- DOM・コンソール・通信を検査するヘッドレスブラウザのスクリプト(Playwright や Puppeteer)。
- 記録した通信の再生。HAR、リクエストの中身、イベントログ、キューのメッセージ、Webhook の本文など。
- 系のうち役に立つ最小の一部だけを起動し、失敗する経路を呼ぶ使い捨ての足場。
- 広い入力空間で、ときどき出力が間違う類いの不具合に対するプロパティテストやファジングのループ。
- 既知の 2 つの状態のあいだで不具合が現れたときに
git bisect runに渡せる二分探索用の足場。 - 旧版と新版、2 つの設定、2 つの提供元、2 つのデータ群を比べる差分ループ。
- 人が介在するスクリプトは最後の手段としてだけ。人の操作手順を書き出し、その結果を記録して、ループとしての形を保ちます。
ループができたら、さらに絞り込みます。
- 速くする: 準備をキャッシュし、範囲を狭め、関係のない初期化を飛ばす。
- 信号を鋭くする: ざっくりした成功ではなく、その症状そのものを検査する。
- 結果を安定させる: 時刻を固定し、乱数の種を決め、ファイルシステムを切り離し、通信を凍結する。
結果が揺れる不具合では、当面の目標は完璧さではなく再現率の高さです。きっかけを 100 回走らせ、並列にし、負荷をかけ、タイミングの窓を狭め、あるいは sleep を挟みます。半分の確率で起きるならデバッグできますが、100 回に 1 回ではたいてい無理です。
やること: terminal ツールで、きつく絞ったループを走らせます。
# Run a specific failing test
pytest tests/test_module.py::test_name -v
# Or run a scripted repro
python scripts/repro_bug.py
# Or run a high-repetition flaky repro
for i in {1..100}; do pytest tests/test_flake.py::test_name -q || break; done3. 最近の変更を確かめる
- これを引き起こしそうな変更は何か
- git の差分、直近のコミット
- 新しく入った依存、設定の変更
やること:
# Recent commits
git log --oneline -10
# Uncommitted changes
git diff
# Changes in specific file
git log -p --follow src/problematic_file.py | head -1004. 部品が複数ある系では証拠を集める
系が複数の部品でできているとき(API → サービス → データベース、CI → ビルド → デプロイ など):
修正案を出す前に、診断用の記録を仕込みます。
部品と部品の境目ごとに、次を行います。
- その部品に入ってくるデータを記録する
- その部品から出ていくデータを記録する
- 環境変数や設定が伝わっているかを確かめる
- 各層で状態を確認する
まず一度走らせて、どこで壊れているかを示す証拠を集めます。 そのうえで証拠を読み、失敗している部品を特定します。 それからその部品を掘り下げます。
5. データの流れをたどる
エラーが呼び出し階層の深いところで起きているとき:
- おかしな値はどこで生まれたのか
- おかしな値を渡してこの関数を呼んだのは誰か
- 出どころが見つかるまで上流へたどり続ける
- 症状の場所ではなく、出どころで直す
やること: search_files で参照をたどります。
# Find where the function is called
search_files("function_name(", path="src/", file_glob="*.py")
# Find where the variable is set
search_files("variable_name\\s*=", path="src/", file_glob="*.py")段階 1 の確認リスト
- [ ] エラーメッセージを最後まで読み、意味を理解した
- [ ] きつく絞ったループのコマンドがあり、少なくとも一度は走らせた
- [ ] ループは赤にできる。近くの別の失敗ではなく、利用者のその症状を検査している
- [ ] ループは毎回同じ結果になる。揺れる不具合なら、デバッグできるだけの再現率がある
- [ ] 最近の変更を洗い出し、目を通した
- [ ] 証拠が集まった(ログ、状態、データの流れ)
- [ ] 問題を特定の部品やコードまで絞り込んだ
- [ ] 根本原因の仮説を言葉にでき、検証できる
ここで止まります: なぜ起きているのかを理解するまで、段階 2 に進まないでください。
段階 2: パターンの分析
直す前に、パターンを見つけます。
0. 再現を最小にする
ループが赤になったら、赤のままでいられる最小の場面まで再現を縮めます。入力、呼び出し元、設定、データ、手順をひとつずつ削り、削るたびにループを回し直します。失敗を成り立たせている要素だけを残します。
残った要素をどれか取り除くとループが緑になる、という状態になれば完了です。最小の再現は仮説の範囲を狭め、そのままいちばんきれいな回帰テストになることもよくあります。
1. 動いている例を探す
- 同じコードベースの中から、似ていて動いているコードを見つける
- 壊れているものと似ていて、動いているものは何か
やること: search_files で近いパターンを探します。
search_files("similar_pattern", path="src/", file_glob="*.py")2. 手本と見比べる
- あるパターンを実装しているなら、手本の実装を最後まで読む
- ざっと見ずに、一行ずつ読む
- 当てはめる前に、そのパターンを完全に理解する
3. 違いを洗い出す
- 動いているものと壊れているもので、何が違うのか
- どんなに小さくても、違いをすべて書き出す
- 「これは関係ないはず」と決めつけない
4. 依存を把握する
- これは他にどんな部品を必要とするのか
- どんな設定や環境が要るのか
- どんな前提を置いているのか
段階 3: 仮説と検証
科学のやり方で進めます。
1. 反証できる仮説を順位づけて立てる
- どれかを試す前に、ありそうな仮説を 3〜5 個出します。
- ありそうさと、反証にかかる手間の軽さで順位をつけます。
- それぞれの仮説が何を予言するかを言葉にします。「X が原因なら、Y を変える(または観測する)と Z が起きるはずだ」という形です。
- 検証できる予言を持たない仮説は、捨てるか、鋭くし直します。
利用者がその場にいるなら、試す前に順位づけた一覧を見せます。その分野の知識から、順位が一気に入れ替わることがあります。利用者が席を外しているなら、自分の順位づけのまま進めます。
2. 最小の形で試す
- 順位がいちばん高い仮説を、できるだけ小さな探りで試します。
- 変える要素は一度にひとつだけにします。
- 複数のことを同時に直さないでください。
- 使えるならデバッガや REPL での確認を優先します。ブレークポイント 1 個は、ログ 10 行に勝ります。
- ログを足すなら、一時的な行すべてに
[DEBUG-a4f2]のような固有の目印を付けます。あとで一度検索するだけで片付きます。
3. 次に進む前に確かめる
- 効きましたか → 段階 4 へ
- 効きませんでしたか → 新しい仮説を立てる
- その上に修正を積み重ねないでください
4. わからないとき
- 「X が理解できていません」と言う
- 知っているふりをしない
- 利用者に助けを求める
- もっと調べる
段階 4: 実装
症状ではなく、根本原因を直します。
1. 失敗するテストを用意する
- できるだけ単純な再現にする
- できるなら自動テストにする
- 直す前に必ず用意する
test-driven-developmentの skill を使う
2. 修正はひとつだけ入れる
- 突き止めた根本原因に対処する
- 一度にひとつの変更だけ
- 「ついでに」の改善はしない
- リファクタリングを一緒に混ぜない
3. 修正を検証する
# Run the specific regression test
pytest tests/test_module.py::test_regression -v
# Run full suite — no regressions
pytest tests/ -q4. 修正が効かないとき — 3 回の掟
- 止まります。
- 数えます。ここまでいくつ修正を試しましたか
- < 3 なら: 段階 1 に戻り、新しくわかったことを踏まえて分析し直します
- 3 以上なら: 止まって、設計そのものを疑います(下の 5 番)
- 設計の話をしないまま 4 つ目の修正に手を出さないでください
5. 修正が 3 回以上失敗したら: 設計を疑う
設計の問題を示すパターン:
- 修正するたびに、別の場所で共有状態や結合が新しく見つかる
- 修正するのに「大がかりな作り直し」が必要になる
- 修正するたびに、よそで新しい症状が出る
止まって、前提そのものを問い直します。
- このやり方は、そもそも筋がよいのか
- 「惰性だけで続けている」のではないか
- 症状を直し続けるのではなく、設計を作り直すべきではないか
これ以上修正を試す前に、利用者と相談してください。
これは仮説がはずれたのではなく、設計が間違っているということです。
危険なサイン — 止まって手順に戻る
自分がこう考えていることに気づいたら、要注意です。
- 「とりあえず今は応急処置で、調査はあとで」
- 「X を変えて、動くか見てみよう」
- 「変更をいくつか入れて、テストを回そう」
- 「テストは省いて、手で確かめればいい」
- 「たぶん X だから、そこを直そう」
- 「よくわかっていないけど、これで動くかもしれない」
- 「パターンは X だけど、少しアレンジして当てはめよう」
- 「主な問題はこれです(調査せずに修正案を並べる)」
- データの流れをたどる前に解決策を出している
- 「あと 1 回だけ修正を試す」(すでに 2 回以上試している)
- 修正するたびに、別の場所で新しい問題が出る
どれも意味するところは同じです。止まって、段階 1 に戻ります。
修正が 3 回以上失敗したら: 設計を疑ってください(段階 4 の 5 番)。
よくある言い訳
| 言い訳 | 実際のところ |
|---|---|
| 「単純な問題だから手順は要らない」 | 単純な問題にも根本原因はあります。単純な不具合なら手順はすぐ終わります。 |
| 「緊急だから手順を踏む時間はない」 | 順序立てたデバッグは、当て推量でもがくより速いです。 |
| 「まずこれを試して、それから調べる」 | 最初の修正がその後の型を決めます。はじめから正しくやります。 |
| 「修正が効くと確かめてからテストを書く」 | 検証していない修正は定着しません。先にテストを書けば証明になります。 |
| 「まとめて直したほうが時間の節約になる」 | 何が効いたのか切り分けられません。新しい不具合の原因にもなります。 |
| 「手本が長いので、要点だけ取り入れる」 | 半端な理解は不具合を確実に生みます。最後まで読んでください。 |
| 「問題が見えたから直そう」 | 症状が見えることと、根本原因がわかることは違います。 |
| 「あと 1 回だけ修正を試す」(2 回以上失敗したあと) | 3 回以上の失敗は設計の問題です。もう一度直すのではなく、やり方を疑ってください。 |
早見表
| 段階 | 主にやること | 達成の目安 |
|---|---|---|
| 1. 根本原因 | エラーを読む、再現する、変更を確かめる、証拠を集める、データの流れをたどる | 何が起きていて、なぜかを理解できている |
| 2. パターン | 動いている例を探す、比べる、違いを洗い出す | 何が違うのかがわかっている |
| 3. 仮説 | 仮説を立て、最小の形で試し、変える要素はひとつずつ | 仮説が確かめられたか、新しい仮説が立った |
| 4. 実装 | 回帰テストを用意し、根本原因を直し、検証する | 不具合が解消し、テストがすべて通る |
Hermes Agent との組み合わせ
調査に使うツール
段階 1 では、次の Hermes のツールを使います。
search_files— エラー文字列を探し、関数の呼び出しをたどり、パターンを見つけますread_file— 行番号つきでソースを読み、正確に分析しますterminal— テストを走らせ、git の履歴を確かめ、不具合を再現しますweb_search/web_extract— エラーメッセージやライブラリの資料を調べます
delegate_task と組み合わせる
部品が絡み合った込み入ったデバッグでは、調査用のサブエージェントを送り出します。
delegate_task(
goal="Investigate why [specific test/behavior] fails",
context="""
Follow systematic-debugging skill:
1. Read the error message carefully
2. Reproduce the issue
3. Trace the data flow to find root cause
4. Report findings — do NOT fix yet
Error: [paste full error]
File: [path to failing code]
Test command: [exact command]
""",
toolsets=['terminal', 'file']
)test-driven-development と組み合わせる
不具合を直すときは、次の順に進めます。
- 不具合を再現するテストを書く(RED)
- 順序立ててデバッグし、根本原因を突き止める
- 根本原因を直す(GREEN)
- そのテストが修正を証明し、再発を防ぐ
実際の効き目
これまでのデバッグ作業から。
- 順序立てたやり方: 15〜30 分で修正
- 当てずっぽうのやり方: 2〜3 時間もがく
- 一度で直る割合: 95% 対 40%
- 新たに生まれた不具合: ほぼゼロ 対 よくある
近道はありません。当て推量もしません。順序立てたやり方が必ず勝ちます。