探偵のようにバグを追う:AIに聞く前の5つのステップ
再現、縮小、仮説、git bisectでの二分探索、確認。自分でできる5つのステップと、その後でAIに渡すバグ報告の書き方を解説します。実際に動かした20コミットの練習用リポジトリ、答え、報告テンプレートつきです。
コードを書く人、読む人なら誰でも、初心者の方でも読めます。高度なGitの知識もデバッグの経験も要りません。用語にはどれも身近なたとえを添えています。計算の部分は印をつけてあるので、不要なら読み飛ばせます。
記事のコマンドと出力は、練習用リポジトリでの実際の実行結果です(Git 2.56.0、Python 3.14.8)。同じものを作れるスクリプトも載せています。コミットのハッシュは時刻に依存するため、あなたの環境では違う値になります。腕のよい人がどうデバッグしているかについて、決まった理論はまだありません。以下の5つのステップは実践の整理であり、対照実験で検証された手順ではありません。「例」と書いたものは作った例で、実在の人物や会社には触れていません。
まず要点#
- デバッグは独立した技術で、AIがあっても不要にはなりません。バグの場所を知らないままAIに聞くと、答えを判断する根拠がなくなります。
- 5つのステップ:バグを確実に再現する、失敗するケースを縮小する、間違いうる仮説を1つ立てる、疑わしい範囲を半分ずつに絞る、直してテストを足して再発を防ぐ。
- 半分に絞る作業にはGit標準の
git bisectがあります。この記事の実際の例では、疑わしい19コミットの中から原因を見つけるのに4回の確認で済みました。1つずつ見ると最大19回です。 - 例の原因は「refactor: simplify discount check」という、一見無害なコミットでした。バグはたった1文字、
>=が>になったことです。 - 5つのステップのあとで、AIに短いバグ報告を渡します(テンプレートは末尾)。そうするとAIの助けは速くなり、説明も確認できます。
- 限界:毎回同じ結果を返すテストが必要です。タイミング、データ、環境に左右されるバグはずっと難しくなります。
むずかしい言葉をやさしく#
| 用語 | やさしい意味 |
|---|---|
| バグ(bug) | プログラムが、期待と違うことをする箇所。 |
| 再現(reproduce) | バグを自分の思いどおりに、毎回同じように起こすこと。事件現場を再現するようなものです。 |
| 最小の再現例(minimal reproduction) | バグがまだ起きる、いちばん小さな例。これ以上減らすとバグが消えます。 |
| 仮説(hypothesis) | 原因についての、間違いだと証明されうる予想。「犯人が裏口から入ったなら、台所の床に泥がついているはず」のようなものです。 |
| 二分探索(bisect) | 確認のたびに疑わしい範囲を半分にします。1から100の数当てで「50より大きい?」と聞くのと同じです。 |
| リグレッション(regression) | 以前は動いていたものが、あとの変更で壊れること。 |
| テスト(test) | 実行して「合格」か「不合格」を返す小さなコード。人が読まなくても結果が出ます。 |
| 終了コード(exit code) | コマンドが終わったときに返す数字。0は正常、それ以外は問題あり。Gitはこれでどのコミットが正常かを判断します。 |
この記事を読むと説明できること#
- バグは直す前に再現して縮小する必要があること、そして「間違いうる」仮説とはどういうものか。
git bisectが半分ずつに絞って原因のコミットを見つける仕組みと、19コミットなら約5回の確認で済む理由。- AIに渡す良いバグ報告に何を入れるか、そしてなぜ自分で範囲を絞ったあとに渡すのか。
1. まずイメージ:現場に着いた探偵#
探偵が現場に着いたところを思い浮かべてください。最初に怪しい人を見つけても、すぐには逮捕しません。現場をそのままにして、何が起きたかを書き留め、関係のない細部を捨てて核心を見やすくし、具体的な仮説を立てます。「犯人が裏口から入ったなら、台所の床に泥がついているはず」。そして、まさにそれを確かめに行きます。違っていたら仮説を捨てて別のものを試します。最後には、他の人も確認できる証拠が残ります。
デバッグも同じです。5つのステップは、それぞれ探偵の仕事に対応します。
| 探偵 | デバッグ |
|---|---|
| 現場をそのまま保つ | バグを確実に再現する |
| 関係のない細部を捨てる | 失敗するケースを縮小する |
| 確認できる仮説を立てる | 仮説を1つ立て、結果を先に予想する |
| 容疑者を消していく | 疑わしい範囲を半分ずつに絞る |
| 事件が再び開かないよう記録する | 直して、テストを足して、書き残す |
教訓: 優れた探偵は最初に当てる人ではなく、違う可能性を順に消す方法を持っている人です。
2. AIがあるのに、なぜまだ学ぶのか#
2.1 この技術がまだ重視されていることを示す調査#
BairesDevのDev Barometer Q2 2026(77か国の開発者1,569人、うち初級1,059人、上級510人、2026年6月公表)では、初級開発者の48%が、採用で最も重要なのは問題解決力と分析的思考だと答えました。AIツールを使いこなす力を挙げたのは18%で、およそ3倍の差です。同じ調査で、初級者が提出するAI生成コードを十分理解していると答えた上級開発者は16%でした。
注意が3つあります。第一に、BairesDevはこのテーマに商業的な利害のあるソフトウェア開発の外注会社です。第二に、元のページを開こうとしたところアクセスを拒否されたため、数字は同社のプレスリリースとデータ資料を引用した検索結果から読んだもので、元の表は確認していません。第三に、16%は上級者による初級者への評価で、理解度そのものの測定ではありません。確かな証拠ではなくシグナルとして読んでください。
2.2 先にAIに聞くと、判断力を失いかねない理由#
壊れているファイルをそのままAIに貼って、修正案が返ってきたとします。バグの場所を知らなければ、選べるのは信じるか信じないかだけです。スタンフォード大学の研究(Perryら、2022年、参加者47人)では、AIアシスタントを使ったグループは5問中4問でより安全でないコードを書いたのに、自分のコードは安全だとより強く信じていました。古いモデルと小さなグループでの結果ですが、確信していることと正しいことは別だという、気にかける価値のあるリスクを示しています。
教訓: AIを使うのは良いことです。ただ、AIの答えを照らし合わせる材料を持てるように、先に自分で疑わしい範囲を絞っておきましょう。
3. 5つのステップと、実際に動かした例#
例は小さな買い物かごのプログラムです。ルールは、100以上の注文は10%割引。テストは、合計がちょうど100の注文に割引がつかないという失敗を報告します。リポジトリは20コミットで、ルールを壊すコミットはちょうど1つです。作るスクリプトは第5章にあります。
ステップ1. 再現:1つのコマンドで、毎回同じ結果#
イメージ: 現場をそのまま保つ。
最初の仕事は直すことではなく、毎回バグを起こす1つのコマンドを持つことです。
python3 test_cart.py
echo "exit=$?"
FAIL total([(50, 2)]) = 100.0, want 90.0
exit=1
この2行が現場です。50の商品2つ(合計100)は90.0になるはずなのに、プログラムは100.0を返しました。終了コードは1、つまり「不合格」です。2回動かして結果が違うなら、まだ再現できていません。それを先に解決します(限界の章を参照)。
ステップ2. 縮小:関係のないものを外す#
イメージ: 関係者だけが残るまで、部屋から人を出していく。
失敗するケースの一部を1つずつ外して、「バグはまだあるか」を確かめます。大きなかごから最小のケースまで、実際に動かした結果です。
| ケース | データ | 割引前の合計 | 結果 | まだ間違い? |
|---|---|---|---|---|
| 3行の商品 | [(25, 2), (25, 2), (0, 5)] |
100 | 100.0 | はい |
| 価格0の行を外す | [(25, 2), (25, 2)] |
100 | 100.0 | はい |
| 1行にまとめる | [(50, 2)] |
100 | 100.0 | はい |
| 商品1つ | [(100, 1)] |
100 | 100.0 | はい |
行の数も、商品の数も、各価格も関係ありませんでした。残ったのは割引前の合計がちょうど100ということです。最小の失敗ケースは1行になりました:total([(100, 1)])。
ステップ3. 間違いうる仮説を1つ立て、先に結果を予想する#
イメージ:「犯人が裏口から入ったなら、台所の床に泥がついているはず」。
ステップ2から立てる仮説は、割引の関数がちょうど100の境界を誤って扱っているです。良い仮説は予想を立てさせてくれます。正しいなら、100未満は割引なし、ちょうど100は割引されるべきなのにされない、100超は割引される、となるはずです。それから実行します。
discount(99.99) # 0.0
discount(100) # 0.0 (本来は10.0のはず)
discount(100.01) # 10.0
予想どおりで、割引は100からではなく100を超えてから始まっていました。仮説は成り立ち、「以上」であるべき「より大きい」を探せばよいと分かります。どんな結果でも「合っている」と言える仮説は役に立ちません。ここでdiscount(100) = 10.0が返っていたら、この仮説は捨てなければなりません。
もう直し方が見えているかもしれません。でも実際のプロジェクトでは、その関数は多くの人が触っているかもしれず、「どこが壊れているか」と同じくらい「どの変更が壊したか」が大事です。それがステップ4です。
ステップ4. 半分に絞る:確認のたびに疑わしい範囲を半分にする#
イメージ: 1から100の数当て。「1ですか、2ですか」とは聞かず、「50より大きい?」と聞きます。1つの質問で半分が消えます。
履歴は20コミットです。最初のコミットは動き、最新は壊れています。疑わしいのは19コミットです。git bisectは真ん中のコミットを選んで切り替え、確認のたびに半分を捨てます。実際の実行はこうです。
git bisect start HEAD $(git rev-list --max-parents=0 HEAD)
status: waiting for both 'good' and 'bad' commits
status: waiting for 'good' commit(s), 'bad' commit known
Bisecting: 9 revisions left to test after this (roughly 3 steps)
[5f01574] feat: add free shipping limit
Gitが真ん中のコミットを選び、そこへ移動してくれました。テストを実行してgit bisect goodかgit bisect badで印をつけます。git bisect runなら、Gitが毎回テストを実行し、終了コードで判断します(Gitのドキュメントでは、0が正常、125を除く1から127が異常)。
git bisect run python3 test_cart.py
running 'python3' 'test_cart.py'
Bisecting: 4 revisions left to test after this (roughly 2 steps)
[f1da7cd] feat: add min order note
running 'python3' 'test_cart.py'
FAIL total([(50, 2)]) = 100.0, want 90.0
Bisecting: 2 revisions left to test after this (roughly 1 step)
[2004cf0] feat: add coupon placeholder
running 'python3' 'test_cart.py'
FAIL total([(50, 2)]) = 100.0, want 90.0
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[6b693ef] refactor: simplify discount check
running 'python3' 'test_cart.py'
FAIL total([(50, 2)]) = 100.0, want 90.0
6b693ef is the first 'bad' commit
commit 6b693ef
refactor: simplify discount check
cart.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
bisect found first 'bad' commit
このセッションの確認は4回でした。真ん中のコミット(合格)、続いて3つとも不合格、最後に1つだけ残りました。原因はrefactor: simplify discount checkです。その変更を見てみましょう。
git diff 6b693ef~1 6b693ef
- if total >= 100:
+ if total > 100:
たった1文字です。コミット名は「整理」と言っていますが、実際には動作を変えていました。コミット名と中身がずれることがあるので、名前を読むのではなくテストでコミットを探すのがよい理由でもあります。
最後にgit bisect resetでセッションを終え、元のブランチに戻るのを忘れないでください。
半分に絞る方法はGitの外でも使える#
「半分を捨てる」という考え方はどこでも使えます。長い関数の半分を一時的に無効にしてバグが残るか見る、処理の途中で値を出力して壊れるのがその前か後かを知る、機能フラグの半分を切る、などです。1行ずつ眺めるより、ずっと早く近づけます。
ステップ5. 直して、確認して、再発を防ぐ#
イメージ: 事件が再び開かないよう記録を残す。
1文字直して(>を>=に戻して)、テストを実行します。
python3 test_cart.py
echo "exit=$?"
exit=0
合格です。隣り合う境界も確認すると、実際の結果は次のとおりでした。
total([(99.99, 1)]) # 99.99 (割引なし)
total([(100, 1)]) # 90.0 (10%割引)
total([(100.01, 1)]) # 90.01 (10%割引)
このテストはリポジトリにもともとありました(ちょうど100の注文を確認します)。だから、誰かがまた壊してもこのバグは捕まえられます。なければテストを足すのがこのステップです。バグは1つずつ、自分用のテストを残すべきです。テストの書き方は記事「AIが書いたコードのテスト」にあります。
教訓: 5つのステップで、「プログラムが間違っている」から「特定のコミットの1文字」まで、各段階に証拠を持って進めました。あとは直すだけです。
4. 自分で確かめる計算:半分に絞るのはどれだけ速いか(読み飛ばしてもOK)#
疑わしいコミットがN個のとき、半分に絞る確認は最大でおよそlog2(N)回(切り上げ)です。実際に計算した結果です。
import math
for n in (19, 99, 999, 9999):
print(n, math.ceil(math.log2(n)), round(math.log2(n), 2))
| 疑わしいコミット数 | 半分に絞る場合の最大確認回数 | log2 |
|---|---|---|
| 19(この記事の練習用リポジトリ) | 5 | 4.25 |
| 99 | 7 | 6.63 |
| 999 | 10 | 9.96 |
| 9,999 | 14 | 13.29 |
実際の実行でGitが必要としたのは5回ではなく4回でした。5は最大値で、実際の回数はバグの位置によります。いちばん古いコミットから1つずつ見ていくと、11番目のコミット(原因)に届くまでに10回、最大では19回かかります。なお、これらの数字は、毎回の確認が信頼でき、すべてのコミットが動くことを前提にしています。ビルドできないコミットがあれば、コード125で飛ばせますが、確認が増えます。
5. 練習問題:自分でやってみる(答えつき)#
次のスクリプトをmake_repo.shとして保存し、bash make_repo.shで実行してください。cart-demoというフォルダに20コミットを作ります。そのうち割引ルールを壊すコミットはちょうど1つです。
#!/usr/bin/env bash
# 20コミットの練習用リポジトリを作る。そのうち1コミットだけが割引ルールを壊す。
set -e
rm -rf cart-demo
mkdir cart-demo
cd cart-demo
git init -q -b main
git config user.name "debug-demo"
git config user.email "demo@example.com"
c() { cat >> "$2"; git add -A; git commit -q -m "$1"; } # c "メッセージ" ファイル < 内容
cat > test_cart.py <<'EOF'
import sys
from cart import total
checks = [
([(10, 3)], 30.0), # 100未満:割引なし
([(50, 2)], 90.0), # ちょうど100:10%割引
([(60, 2)], 108.0), # 100超:10%割引
]
bad = [(i, want, total(i)) for i, want in checks if total(i) != want]
for i, want, got in bad:
print(f"FAIL total({i}) = {got}, want {want}")
sys.exit(1 if bad else 0)
EOF
c "feat: add subtotal, discount, total and a check" cart.py <<'EOF'
def subtotal(items):
return sum(price * qty for price, qty in items)
def discount(total):
if total >= 100:
return round(total * 0.10, 2)
return 0.0
def total(items):
s = subtotal(items)
return round(s - discount(s), 2)
EOF
c "feat: add shipping fee" cart.py <<'EOF'
SHIPPING = 5.0
EOF
c "docs: explain discount rule" cart.py <<'EOF'
# 100以上の注文は10%割引。
EOF
c "feat: add currency format" cart.py <<'EOF'
def money(x):
return f"{x:,.2f}"
EOF
c "feat: add empty cart helper" cart.py <<'EOF'
def is_empty(items):
return len(items) == 0
EOF
c "feat: add item count" cart.py <<'EOF'
def count(items):
return sum(qty for _, qty in items)
EOF
c "feat: add max quantity" cart.py <<'EOF'
MAX_QTY = 99
EOF
c "feat: add tax rate constant" cart.py <<'EOF'
TAX_RATE = 0.08
EOF
c "feat: add receipt title" cart.py <<'EOF'
def title(name):
return f"Receipt for {name}"
EOF
c "feat: add free shipping limit" cart.py <<'EOF'
FREE_SHIPPING_FROM = 200
EOF
perl -pi -e 's/total >= 100/total > 100/' cart.py
git add -A; git commit -q -m "refactor: simplify discount check"
c "feat: add coupon placeholder" cart.py <<'EOF'
COUPONS = {}
EOF
c "feat: add rounding helper" cart.py <<'EOF'
def r2(x):
return round(x, 2)
EOF
c "feat: add item label" cart.py <<'EOF'
def label(price, qty):
return f"{qty} x {price}"
EOF
c "feat: add min order note" cart.py <<'EOF'
MIN_ORDER = 1
EOF
c "feat: add currency code" cart.py <<'EOF'
CURRENCY = "USD"
EOF
c "feat: add store name" cart.py <<'EOF'
STORE = "Demo Shop"
EOF
c "feat: add version string" cart.py <<'EOF'
VERSION = "1.0"
EOF
c "feat: add free item note" cart.py <<'EOF'
FREE_ITEM_NOTE = "none"
EOF
c "chore: add end marker" cart.py <<'EOF'
# end of file
EOF
答えを見る前に試してください。
cart-demoに入り、python3 test_cart.pyを実行して結果を書き留める(ステップ1)。- 失敗するケースを手で縮小し、別の値もいくつか試す(ステップ2)。
- 仮説を1文で書き、99.99、100、100.01について結果を予想して書く(ステップ3)。
git bisectを1回手作業で(コミットごとに印をつけて)実行し、続けてgit bisect run python3 test_cart.pyでもう一度実行する。確認の回数を数える(ステップ4)。- 直してテストを再実行する(ステップ5)。
答え:
- 原因のコミットは
refactor: simplify discount checkで、20コミット中の11番目です。 - その変更は、
cart.pyのdiscount関数で>=が>になったことです。 - 確認の回数は最大5回で、私の実行では4回でした。あなたは4回か5回で、ハッシュも違うかもしれませんが、それで正常です。
- 落とし穴が1つあります。テストは確認するすべてのコミットに存在しなければなりません。ここでは
test_cart.pyが最初のコミットからあるので安全です。もしテストが途中のコミットで初めて追加された場合、それより古いコミットではファイルがないため「異常」と報告され、Gitが誤った結論を出します。避けるには、テストスクリプトをリポジトリの外に置く(Gitのドキュメントもテストスクリプトをリポジトリの外に置くよう助言しています)か、テストが現れたあとの正常なコミットを選びます。
6. AIに渡す短いバグ報告:5ステップのあとで#
5つのステップを終えると、AIが使えて、あなたが確認できるバグ報告を書くのに必要なものが揃います。貼れるテンプレートです。
バグ:<1文>
再現(最小):<コマンドまたはデータ>
期待:<正しい結果>
実際:<出力をそのまま>
除外したこと:<試した仮説とその結果>
疑い:<今の仮説と、その根拠>
範囲:<ファイル、疑わしいコミット>
依頼:直す前に原因を説明し、
最小の変更を提案し、確認していないことも書いてください。
この記事の例では、記入済みの報告はこうなります。
バグ:合計がちょうど100の注文に10%割引がつかない。
再現(最小):total([(100, 1)])
期待:90.0
実際:100.0
除外したこと:行数と各商品の価格は影響しない。
discount(99.99) = 0.0、discount(100) = 0.0、discount(100.01) = 10.0。
疑い:discount()の境界100での比較。
範囲:git bisectは「refactor: simplify discount check」のコミットを指している。
依頼:直す前に原因を説明し、最小の変更を提案してください。
この報告なら、正しい答えはほとんど明らかです。AIは境界での比較を指すでしょう。もっと大事なのは、答えを確認できることです。すでにあるテストと境界値で確かめられます。AIが別の場所の修正を提案したら、ずれているとすぐ分かります。修正にもAIに証拠を出させる方法は、記事「AIの結果に証拠を求める5つの方法」を参照してください。
7. 初心者がつまずきやすいところ#
| よくある間違い | 失敗する理由 | 避け方 |
|---|---|---|
| 一度にたくさん変える | バグは消えても、どの変更が効いたか分からず、新しいバグも入りうる | 1回に1つだけ変え、テストを再実行する |
| 最初の仮説を信じ込む | 最初の仮説は、信じたいものであることが多い | 結果を先に予想し、違ったら仮説を捨てる覚悟を持つ |
| 失敗するケースを残さない | 明日は再現できず、他の人もできない | コマンドとデータを書き、テストを足す |
| 原因でなく症状を直す | 失敗する入力だけ特別扱いする(100なら90を返す、など) | 原因にたどり着くまで「なぜ」を続ける |
| 不安定なテストでbisectする | 1回の誤判定で、二分探索全体が迷子になる | 各ステップでテストを数回実行するか、先にテストを直す |
| 古いコミットにテストがない | Gitはファイルがないことを「異常」と扱う | テストをリポジトリの外に置くか、現れたあとの正常なコミットを選ぶ |
| いきなりファイル全体をAIに貼る | 答えを判断する材料がない | 5ステップをやってから短いバグ報告を渡す |
7.1 例:ある朝、2つのやり方#
これは想定した場面で、実在の人物や製品には触れていません。
やり方1:すぐにAIに聞く。
- 09:30。 ユーザーから「100円の注文に割引がつきません」と報告が来ます。開発者は
cart.py全体をAIに貼って、直し方を聞きます。 - 09:40。 AIは
total関数の中に、合計が100のときだけの特別な条件を足すよう提案します。テストは通り、開発者はマージします。 - 11:15。
discount関数は>のままなので、discountを直接呼ぶ別の箇所(1日の終わりのレポート)はまだ間違います。開発者はまた探すのに時間を使います。
やり方2:5ステップをやってから聞く。
- 09:30。 同じ報告です。開発者はテストを実行し、1つのコマンドでバグを再現できると確認します。
- 09:40。
total([(100, 1)])まで縮小し、3つの境界値を予想してから試して、仮説が成り立ちます。 - 09:50。
git bisect runが、1文字の変更である「refactor: simplify discount check」のコミットを指します。 - 10:00。 開発者は同じ条件を使う箇所がほかにないか確かめるため、AIに短いバグ報告を渡し、根本を直してマージします。
やるべきこと:AIに聞く前に縮小と仮説を立てる習慣をつけ、AIの言うことを確認できるようにしましょう。これは例なので何も証明しません。証拠は引用した研究にあります。Perryら(2022年)はAIアシスタントで過信しやすいことを示し、BairesDevの調査は問題解決力がまだ重視されていることを示唆します。bisectが働けるように保存ポイントを残す方法は、記事「エージェントを使う人のGit入門」も参照してください。
8. バグをAIに渡す前の8つの質問#
- 毎回バグを起こす1つのコマンドはありますか?
- ケースはこれ以上小さくならないところまで縮小しましたか。これ以上外すとバグは消えますか?
- 仮説は何で、結果を先に予想しましたか?
- 結果は予想と合っていましたか。それとも、合うように曲げていませんか?
- これはリグレッション(以前は動いていた)ですか。基準にできる正常なコミットはありますか?
- 何を除外し、それを書き留めましたか?
- バグ報告に期待、実際の結果(そのまま)、除外したことが入っていますか?
- AIが答えたとき、何で確認しますか。テストですか、境界値ですか、それとも感覚だけですか?
9. バランスよく見る:できること、できないこと#
できること: 「プログラムが間違っている」を、証拠つきの具体的な場所に変える、半分ずつに絞って疑わしい範囲を素早く狭める、バグ報告を短く確認しやすくする、そして再発を防ぐテストを残す。
できないこと:
git bisectは毎回同じ結果を返すテストと、動くコミットのある履歴を必要とします。タイミング、実データ、実行順、環境に左右されるバグは、再現するのがずっと難しくなります。- 最初のコミットからバグがあるなら、出発点にできる正常なコミットがありません。履歴が少数の大きなコミットにまとめられていると、二分探索が示すのは大きなかたまりだけです。
- 注意深いコード読みや業務知識が必要なバグもあり、リグレッションではないものもあります。
- AIは、あなたがすべてのステップをやっていなくても、人より速く原因を見つけることがあります。この記事はAIに聞くなと言っているのではなく、確認できるようにあとで聞こうと言っています。
- この5ステップがバグ探しをどれだけ速くするかを測った対照実験は、見つかりませんでした。実践の整理と、私が作った小さなリポジトリでの実際の例が1つあるだけです。
- 実際の例は、20コミットの小さなリポジトリにバグが1つあるだけです。実際のリポジトリはずっと複雑です。
言い過ぎないこと: 5ステップがすべてのバグを見つけることは保証されません。軽く見ないこと: 原因を理解せずに直すコストは、たいてい後で、バグが別の場所で戻ってきたときに現れます。
10. まとめ#
デバッグは探偵の仕事に似ています。現場を保ち、縮小し、間違いうる仮説を立て、半分ずつに絞って消していき、バグが戻らないよう記録を残します。そこまでやれば、AIへのバグ報告は短くなり、AIの答えを確認する材料も手元にあります。20コミットのリポジトリで練習問題を試してみてください。30分ほどで、仕事を通して使える習慣が残ります。
参考資料#
数字は2026年10月時点の公開情報によります。記事のコマンドと出力はGit 2.56.0とPython 3.14.8での実際の実行結果で、コミットのハッシュは実行ごとに変わります。
- Git、git-bisectのドキュメント(使い方、
git bisect run、終了コード0、125を除く1から127、スキップの125) - BairesDev、Dev Barometer Q2 2026とプレスリリース「16 percent of juniors fully understand AI code」(外注会社による調査。元のページがアクセスを拒否したため、数字は検索結果から読みました。引用する前に元の資料を開いてください)
- Perryら、Do Users Write More Insecure Code with AI Assistants?(スタンフォード大学、2022年)
よくある質問
- AIに聞く前に自分でバグを探すのはなぜですか?
- バグの場所が分からないままだと、AIの答えを判断する材料がないからです。AIはもっともらしい修正を出しても、症状を隠すだけのことがあります。再現、縮小、仮説まで進めておけば、短いバグ報告を書け、返ってきた説明も確認できます。
- git bisectとは何で、いつ使いますか?
- git bisectはコミット履歴に二分探索をかけ、何かを壊した最初のコミットを見つけます。正常なコミットと壊れたコミットを1つずつ指定すると、Gitが真ん中のコミットを切り替えてくれます。確認するたびに範囲が半分になります。信頼できるテストと、動くコミットの履歴が必要です。git bisect runならGitがテストを自動で実行します。
- 原因のコミットを見つけるのに何回確認が要りますか?
- 疑わしいコミット数のlog2を切り上げた回数が目安です。19コミットなら最大5回で、この記事の実際の実行ではGitは4回でした。999コミットなら約10回です。古いほうから1つずつ見ると最大999回かかりえます。
- AIにバグ探しを頼むとき、何を渡せばよいですか?
- 最小の再現手順、期待する結果と実際の結果(そのまま)、すでに除外したこと、今の仮説、そして直す前に原因を説明してほしいという依頼です。記事に貼れるテンプレートがあります。
- git bisectはどんなバグにも使えますか?
- 使えません。毎回同じ結果を返すテストと、動くコミットのある履歴が必要です。タイミング、データ、環境に左右されるバグは難しく、最初のコミットからバグがあるなら基準にする正常なコミットがありません。