Shotgun debugging(ショットガンデバッグ)とは?意味と由来、当てずっぽうの修正をやめる手順をgit bisectの実例で解説

Shotgun debugging(ショットガンデバッグ)とは、バグの原因を調べないまま、思いついた変更を手当たり次第にコードへ加えて、たまたまバグが消えるのを期待するデバッグのやり方です。ハッカー用語集のJargon Fileは「ほとんどうまくいかず、たいていバグを増やす」と説明しています。

この記事では、言葉の意味と由来、なぜ時間とお金の無駄になるのか、そして「症状を再現する→仮説を立てる→1つずつ確かめる」という代わりのやり方を、Pythonと git bisect の実際の実行結果つきで紹介します。

Shotgun debuggingの意味と由来

散弾銃(ショットガン)は、狙いを定めずに広い範囲へ弾をばらまきます。そこから、原因に狙いを定めずにコードのあちこちを変えてみるデバッグを、こう呼ぶようになりました。

ハッカー文化の用語を集めたJargon Fileでは、shotgun debugging を「Easter egging(イースターエッギング)のソフトウェア版」とし、バグが揺さぶられて消えることを期待して、あまり狙いのない変更を加えることだと説明しています。比べられているEaster eggingは、ハードウェアの故障で、関係のない部品をほぼ無作為に交換して不具合が消えるのを期待することを指すIBM由来の言葉です(いずれも2026年10月16日確認)。

日本の現場では「当てずっぽう修正」「とりあえず色々いじってみる」と言われる状態とほぼ同じ意味です。

よくある「ショットガン」の例

次のような行動に心当たりがあれば、ショットガンデバッグになっています。

  • エラーメッセージを最後まで読まずに、ネットで見つけた修正をそのまま貼る
  • 型を int から float に変える、round を付ける、try/except で囲む、を順に試す
  • ライブラリのバージョンを上げ下げし、キャッシュを消し、再起動し、を同時に行う
  • 複数の変更を一度に入れて動いたら、どれが効いたのか分からないままコミットする
  • 動かなかった変更を元に戻さず、次の変更を重ねる

最後の2つが特に危険です。どの変更が効いたのか分からないので、同じバグが再発しても直せませんし、効かなかった変更が新しいバグとして残ります。

なぜ非効率で危険なのか

問題 起きること
時間がかかる 変更の候補は無数にあり、当たるまで試し続けることになる。当たらないことも多い
新しいバグを生む 原因と関係のない箇所を変えるので、別の機能を壊しやすい
再発する 原因が分からないまま症状が消えただけなので、条件が変わるとまた出る
レビューできない 「なぜこの変更でバグが直るのか」を説明できないため、チームの誰も判断できない
費用がふくらむ 仕事として修正を受けている場合、作業時間がそのまま工数・費用になる

「デバッグの費用相場」を調べている方もいるようですが、バグ修正の費用は不具合の内容やシステムの規模で大きく変わり、公的な相場はありません。ただ、見積もりの多くは作業時間がもとになるため、原因を特定する手順を踏むかどうかで、かかる時間、つまり費用が変わることは押さえておくとよいでしょう。

代わりにやること:再現・仮説・検証の5ステップ

  1. 再現する:同じ入力で必ずバグが出る手順を見つける。再現できないうちは修正しない
  2. 小さくする:関係のない処理を削り、バグが出る最小の入力とコードまで絞る
  3. 仮説を立てる:エラーメッセージ、ログ、入力と出力の関係から「ここが原因では」という仮説を1つ立てる
  4. 1つずつ確かめる:仮説を確かめる変更や観察は1回に1つだけ。外れたら元に戻してから次へ
  5. テストで固める:直したら、そのバグを再現するテストを残し、二度と戻らないようにする

ポイントは、ショットガンデバッグが「直すための変更」を当てずっぽうに入れるのに対し、こちらは「原因を知るための観察」を先にすることです。

実例1:入力を変えて出力を観察する(Python)

割引後の合計金額を計算する関数で、1,000円と500円の商品に10%引きをかけると1,350円になるはずが、1,485円になるバグがあるとします。

# shop.py(バグあり)
def total(prices, discount=0):
    s = sum(prices)
    return s - s * discount // 1000

ここで「int を float にしてみる」「round を付けてみる」と試すのがショットガンです。代わりに、割引率だけを変えて出力がどう動くかを観察します。

# probe.py
from shop import total

# 仮説:割引の計算がおかしい。入力を変えて出力の変化を見る
for d in [0, 10, 50, 100]:
    print(f"discount={d:3d} -> {total([1000, 500], discount=d)}")

実行結果(Python 3で実行):

discount=  0 -> 1500
discount= 10 -> 1485
discount= 50 -> 1425
discount=100 -> 1350

割引率100のときに、10%引きの正しい答え(1,350)が出ています。つまり割引が10分の1しか効いていない、という具体的な仮説が立ち、割り算の // 1000 が // 100 の誤りだと絞り込めます。丸めや型の問題ではないので、float や round をいくら試しても直りませんでした。

実例2:git bisectで原因のコミットを特定する

「前は動いていたのに、いつの間にか壊れた」という場合は、Gitの git bisect が使えます。公式ドキュメントによると、git bisect は二分探索で、バグが入り込んだコミットを見つけるコマンドです。正常なコミットと異常なコミットを1つずつ教えると、その間を半分ずつ絞り込みます(git-bisect Documentation、2026年10月16日確認)。

正しいかどうかを判定するスクリプトを用意すれば、git bisect run で自動化もできます。スクリプトは、正常なら終了コード0、異常なら1〜127(125を除く)で終わるようにします。125は「このコミットはテストできないので飛ばす」の意味です(同ページ)。

# check.py(正常なら何も出さずに終了コード0、異常ならAssertionErrorで終了コード1)
from shop import total
assert total([1000, 500], discount=10) == 1350

8個のコミットのうち、どこかでバグが入ったリポジトリで実行した結果です(git 2.50.1)。

$ git bisect start HEAD 067f87f    # 異常なコミット、正常なコミットの順
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[2e81e8675d9343f310549ff43f0ebd53201d31c6] READMEを更新 3
$ git bisect run python3 check.py
running 'python3' 'check.py'
Bisecting: 1 revision left to test after this (roughly 1 step)
[935f8e47a21f0bda706da081eb43b719bace3336] READMEを更新 4
running 'python3' 'check.py'
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[24112a64879f59bd76010f46761fed319f5dc4db] 割引計算を整理
running 'python3' 'check.py'
24112a64879f59bd76010f46761fed319f5dc4db is the first bad commit
commit 24112a64879f59bd76010f46761fed319f5dc4db
Author: demo <a@example.com>
Date:   Fri Oct 16 20:47:30 2026 +0900

    割引計算を整理

 shop.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
bisect found first bad commit
$ git bisect reset

3回のテストで「割引計算を整理」というコミットが原因だと分かりました。このコミットの差分は1行だけです。

-    return s - s * discount // 100
+    return s - s * discount // 1000

コミットが1,000個あっても、二分探索なら10回ほどの確認で済みます。調べ終わったら git bisect reset で元のブランチに戻ります。Gitの基本操作はGitと開発環境の基礎で整理しています。

ショットガンデバッグに陥りやすい場面と対策

場面 対策
納期が迫っている 焦るほど当てずっぽうになる。最初の10分は再現手順を書き出すことに使う
他人が書いたコードで全体が分からない 入口から出口まで、データがどう変わるかをprintやデバッガーで追う
たまにしか起きないバグ ログを増やして発生時の入力と状態を記録する。直す前に「出る条件」を探す
ネットの解決策を見つけた なぜそれで直るのかを説明できるまで入れない。説明できたら1つだけ入れる
疲れて頭が回らない 今わかっていることを文章にする。人や物に説明する「ラバーダック・デバッグ」も有効

変更を試すときは、Gitでブランチを切るかコミットしておき、外れたらすぐ戻せる状態にしておくと、試した変更が積み重なる事故を防げます。

Pythonには標準でデバッガーの pdb があり、コードの止めたい場所に breakpoint() と書くと、そこで変数の中身を見ながら1行ずつ実行できます(pdb — The Python Debugger)。printを足しては消すよりも、観察できる量が増えます。

関連する言葉

  • ラバーダック・デバッグ:机の上のアヒルのおもちゃなどに、コードを1行ずつ説明していくうちに、自分で原因に気づく方法
  • git bisect:上で紹介した、二分探索でバグが入ったコミットを探すGitのコマンド
  • リファクタリング:外から見た動きを変えずにコードを整理すること。テストがない状態で「ついでに整理」すると、今回の例のようにバグを入れる原因になります。名前だけのリファクタリングがコードを悪くする例はRefuctoring(リファクタリングの改悪)とは?で紹介しています

バグとの付き合い方そのものはバグを愛せ|バグがゼロにならない理由と、一流のプログラマーの向き合い方でも書いています。

デバッグの力は、実際にエラーを出して自分で原因を探す回数で伸びます。対面・通学型のプログラミングスクール「テックジム」(東京本校は永田町)では、自分のペースで課題を進め、どうしても原因が分からないときはトレーナーに質問できます。無料講座もあります。

よくある質問

Q. Shotgun debuggingとはどういう意味ですか?

バグの原因を調べないまま、思いついた変更を手当たり次第にコードへ加え、たまたまバグが消えるのを期待するデバッグのやり方です。散弾銃が狙いを定めずに弾をばらまくことにたとえた言葉です。

Q. ショットガンデバッグで直ったなら問題ないのでは?

原因が分からないまま症状が消えただけなので、条件が変わると再発しやすく、関係のない変更が新しいバグとして残ることがあります。直った場合も、どの変更が効いたのかを1つずつ確かめ、不要な変更は元に戻すべきです。

Q. ショットガンデバッグを避けるには何から始めればいいですか?

まずバグを必ず再現できる手順を見つけることです。次に入力を変えて出力を観察し、原因の仮説を1つ立て、1回に1つの変更で確かめます。以前は動いていた場合は git bisect で原因のコミットを特定するのが近道です。

Q. デバッグの費用相場はありますか?

公的な相場はなく、不具合の内容やシステムの規模によって大きく変わります。多くは作業時間をもとに見積もられるため、原因を特定する手順を踏むかどうかで、かかる時間と費用が変わります。

開発環境とGitの全体像はGitと開発環境の基礎にまとめています。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら