📑 この章の目次
title: デバッグの作法 icon: 🐛 card_desc: バグの原因を論理的に切り分ける手順 — 再現/切り分け/仮説検証・二分法・最小再現例・root causeの追求 slug: 11-debugging
11 デバッグの作法 — バグの原因を論理的に切り分ける手順
プログラムは必ずバグ(不具合)を出す。重要なのは「直すこと」でなく「なぜ起きるかを突き止めること」。当てずっぽうでなく、論理的に原因を切り分ける作法。
一言でいうと
バグは必ず発生します。素人は「とりあえず直す」を繰り返し、プロは「なぜ起きるかの原因(根本原因)」を突き止めてから直します。この章では、原因を論理的に切り分ける3ステップ(再現 → 切り分け → 仮説検証)と、原因のありかを絞る手法を扱います。
1. なぜデバッグの作法が必要なのか
「当てずっぽう修正」の罠
バグを見つけた時、こう直したくなります。
- 「たぶんここが悪い」→ 直してみる → 直った気がする
- 別の場所で別のバグが発生(直したつもりが壊した)
- 元のバグがまた出た(表面だけ消して原因は残った)
これが「当てずっぽう修正」の罠です。原因を分からずに直すと、別の問題を生んだり、同じバグが何度も再発したりします。
「直す」より「特定する」が難しい
バグ修正の時間の大部分は、コードを直す作業でなく、原因を見つける作業に使われます。「どこが悪いか」が分かれば直すのは早い。だからこそ「原因を論理的に特定する作法」が問われます。
2. デバッグとは —— バグの原因を特定する作業
言葉の意味
- バグ(
bug)— プログラムの不具合(意図と違う動き) - デバッグ(
debug)— バグの原因を特定し、修正する作業
「デバッグ」という言葉は、虫(バグ)を取り除く(除去する)イメージから来ています(1940年代、実際の虫がコンピュータの継ぎ目に入って故障した逸話が由来とされます)。
修正より「特定」が本質
デバッグで時間がかかるのは「どこに原因があるか」を絞り込む作業です。コード全体から1箇所の原因を探すのが難しく、ここを論理的にやるか当てずっぽうにやるかで、修复までの時間が何倍も違います。
3. 🔑 核心の3ステップ —— 再現・切り分け・仮説検証
ステップ1: 再現(確実に同じ症状を起こす)
まず「どんな手順で起きるか」を確定させます。「たまに落ちる」では直せません。誰がやっても同じく起きる再現手順を作ります。再現できないバグは直せない(再現しないと確認できないから)。
ステップ2: 切り分け(原因のありかを絞る)
「どこが悪いか」を消去法で絞ります。「ここではない」「あそこでもない」と範囲を狭め、最終的に原因の1箇所を残します(次節の手法で行います)。
ステップ3: 仮説検証(「ここが原因」と実験で確かめる)
「〇〇が原因だろう」という仮説を立て、それを実験で確かめます。「この行を直したら直るはず」→ 実際に直す → 直ったら仮説正解(新たな問題が出ていないかも確認)・直らなければ別の原因。仮説を行き当たりばったりでなく、証拠に基づいて立てるのが作法です。
例えていうと: 医者の診断
医者は「とりあえず薬を出す」でなく、**問診(症状を聞く=再現)→ 検査(異常の場所を絞る=切り分け)→ 診断(〇〇病だろう=仮説)→ 治療(薬で治るか確かめる=検証)**と進めます。デバッグも同じです。「症状を直す」前に「原因を診る」のがプロの作法。
4. 切り分けの手法
二分法(バイセクション)
原因のありかを半分ずつ絞る手法です。「前半のコードか後半か」を確かめ→悪い方をさらに半分→…と絞り込み、最短で原因にたどり着きます。Git では git bisect(良い版と悪い版を指定し、原因コミットを二分探索で特定)という機能が定番です。
ログと print で状態を可視化
プログラムの中の状態(変数の値・どこまで進んだか)を外から見えなくするため、ログ(→ 第8章)や print で出力して可視化します。「ここまでは合っている・ここで変わった」が見えると、原因のありかが一気に絞れます。
「直近の変更」に着目
「昨日まで動いていた」なら、原因は最近の変更にあります。最近変えたコミット・設定・ライブラリ更新(→ 第10章)を疑うのが鉄則です。長く動いていたものが突然壊れる場合、新しく入れたものが原因であることが多い。
5. 🔑 再現性と最小再現例
再現性 —— 毎回起きる vs たまに起きる
- 安定して再現(毎回同じ手順で起きる)→ 原因特定は比較的容易
- 不安定に再現(たまにしか起きない・環境次第)→ 原因特定が困難。こうしたテストは flaky test(→ 第1章)と呼ばれ、CI の信頼を損ねる原因になります
不安定なバグは「条件」を疑います(タイミング・データの内容・同時アクセス等)。何が揃った時に起きるかを突き止めると、再現性が上がります。
最小再現例(MRE)
バグを起こす手順から不要な部分を削ぎ落とし、「これだけで起きる」最小の手順・コードを作ります。これを最小再現例(MRE/エムアールイー/Minimal Reproducible Example)と呼びます。
最小例を作る過程そのものが原因特定になります。「この部分を消してもまだ起きる」→ その部分は無関係、「消したら起きなくなった」→ その部分が原因、と消去法で原因が際立つからです。質問サイト等で助けを求める時も、最小例があると回答がつきやすい。
6. なぜ「論理的デバッグ」が開発の作法なのか
作法として位置づけられる理由
- 同じバグの再発防止: 原因(根本原因/
root cause/ルートコーズ)を突き止めて直さないと、表面を消しても同じバグが何度も出る。原因まで直して初めて「再発しない」 - 引き継ぎ・協働の前提: 「なぜこう直したか」が分からない修正は、他の人が触れられない。原因の論理が説明できる修正だけが、チームで保守できる
- 時間の節約: 当てずっぽうは一見早いが、別バグを生み・再発して結果的に何倍も時間がかかる。論理的デバッグは最初は地道だが、長期で最も早い
面接・実務での意味
- 「バグを見つけたらどうやって原因を突き止めますか?」は開発面接の定番質問です。「再現→切り分け→仮説検証」と答えられるか、当てずっぽうと答えるかで、実務経験の有無が分かれます
- 「root cause を追求する」姿勢は、現場で最も評価される態度の1つです。「直った」で終わらず「なぜ起きたか」を説明できる人が信頼される
- バグ報告を書く時も「再現手順・期待動作・実際の動作」の3点セット(最小再現例付き)が基本です。これが揃っていると、他者がすぐに調査に入れる
7. よくある落とし穴と対処
| 落とし穴 | 何が起きるか | 正しい対応 |
|---|---|---|
| 当てずっぽうに直す | 別のバグを生む・同じバグが再発 | 原因を仮説検証してから直す・「なぜ直ったか」を説明する |
| 症状だけ直す | 根本原因が残り、別の場所で再発 | root cause(根本原因)まで遡って直す |
| 再現しないまま進める | 「たまに落ちる」が永遠に直らない | 確実な再現手順を先に作る・条件を特定する |
| ログを見ずに推測 | 外れの仮説で時間を浪費 | ログ・print で状態を可視化してから仮説を立てる |
| 直近の変更を疑わない | 「急に壊れた」の原因を見逃す | 最近の変更(コミット・設定・ライブラリ)を先に疑う |
| 直したら終わり | なぜ直ったか不明・再発時にまた迷う | 再発防止のテストを残す・原因を記録に残す |
💡 ラバーダック・デバッグ(rubber duck debugging/ゴムアヒル技法): バグに詰まったら、机の上のゴムアヒル(または誰か)に向かって、コードを1行ずつ口頭で説明するという古典的技法。「人に説明するつもりで話す」と、自分の勘違いや飛ばしに気づくことが多く、説明中に原因に気づくことがよくあります。1人で詰まった時の強い味方です。
まとめ
- バグは必ず発生する。重要なのは「直す」でなく「なぜ起きるか(root cause)」を突き止めること
- 3ステップ: 再現(確実に同じ症状を起こす)→ 切り分け(原因のありかを絞る)→ 仮説検証(「ここが原因」を実験で確かめる)
- 切り分けの手法: 二分法(半分ずつ絞る・
git bisect)・ログ/print で状態可視化・直近の変更を疑う - 再現性が鍵。たまにしか起きないバグは条件を特定して再現性を上げる
- 最小再現例(MRE)を作る過程で原因が際立つ。質問・報告の基本セット
- 当てずっぽうは結果的に遅い。論理的に原因まで直すのが、再発せず引き継げる作法
💡 次のステップ: バグの原因を論理的に突き止める手順が見えました。次は、そもそもバグを出しにくくする仕組みです。第12章「開発プロセス・品質」では、テスト・Lint・型チェックで、バグを未然に防ぎ・早く見つける品質の作法を扱います。