AIが作ったコードをAIにデバッグさせる実践的方法論
AIが生成した小規模ソフトウェアは「大まかには動く」状態でも、そのまま公開するのは危険である可能性があります。エラーが出ていない段階でも潜むリスクと、それをAI自身に検証させる方法をまとめます。
AIが作るコードのバグに関する主要な研究とその結果
1. 最も包括的な査読論文(2026年3月・arXiv)
「Debt Behind the AI Boom」― シンガポール経営大学ほか
GitHub上の6,275リポジトリから30万4,362件のAI作成コミットを収集し、静的解析で各コミットの前後を比較した大規模実証研究。 arXiv
主な結果:
- すべてのAIツールにおいて、15%以上のコミットが少なくとも1件の問題を導入している arXiv
- 追跡した問題の24.2%が最新リビジョン時点でもまだ残存している――つまり4件に1件は修正されないまま蓄積する
AIが生成したコードに固有のバグパターン
人間が書いたコードとAIが書いたコードでは、バグの出方が違います。AIに固有のパターンを知っておくことが、効果的なデバッグの前提になります。
「動いているように見える」系
- テストケースへの過剰適合――よくある入力には正しく動くが、その条件から外れると崩れる。人間なら「なぜそう動くか」を理解して書くが、AIはパターンマッチで書くため
- ハッピーパス偏重――正常系は丁寧に書くが、失敗・例外・中断の処理が「とりあえず形だけある」状態になりやすい
- 条件分岐の穴――if/elseが一見網羅されているように見えて、特定の組み合わせが抜けている
一貫性のなさ系
- 同じ処理を別々の方法で実装――コードの前半と後半で同じ目的の処理が違う書き方になっており、片方だけバグを持つ
- 変数名・関数名の意味のズレ――名前と実際の動作が微妙に違う。
getUser()が実はDBに書き込んでいたりする - 設定値や定数のばらつき――タイムアウト値やリトライ回数などが、ファイルをまたいで微妙に違う数字になっている
「それっぽいが間違い」系
- 古いAPIの使用――学習データに古いコードが多いため、非推奨・廃止済みのメソッドを自信満々に使う
- off-by-oneエラーの多さ――ループの境界値(
<vs<=、0始まりか1始まりか)のミスが人間より多い傾向 - コピペ的な文脈ズレ――似た処理を参考にして生成するため、前の文脈の変数名・前提条件が残り込む
特に見つけにくいもの
- 非決定的な動作――たまにだけ失敗する処理(タイミング依存・乱数依存)のケアが薄い
- データが大きくなったときの劣化――少量データでは動くが、件数が増えると極端に遅くなる・メモリを食い尽くすアルゴリズムを平気で使う
- 副作用の見落とし――関数が「返り値を返す」だけでなく外部状態を変えているのに、呼び出し側がそれを知らない設計
一番厄介なのは「動いているが間違っている」パターンで、テストで引っかからず、しばらく使って初めて発覚します。
エラーが出ていない段階でAIに検証させるべき項目
「エラーなし・本格テスト前」は最もAIが活きる場面です。優先度の高い検証項目と、具体的な質問の仕方をまとめます。
1. ロジックの自己矛盾チェック(最優先)
「動くが間違っている」系バグの温床。
- 「このコードが意図通り動く前提条件を全部列挙して」
- 「このアルゴリズムを日本語で説明して」→ 説明が怪しければロジックも怪しい
- 「この関数の入力と出力の関係を表で整理して」
2. 境界値・エッジケースの網羅
エラーは出ないが、特定の値で壊れる系。
- 「この関数が失敗しそうな入力値を20個挙げて」(数を多めに指定するのがコツ)
- 「0・空・null・最大値・負の数を渡したらどうなるか、コードを読んで答えて」
- 「ユーザーが意地悪な操作をするとしたらどんな操作か」
3. 暗黙の前提・依存関係の可視化
「呼び出し順序の前提」「グローバル状態への依存」など、コードに書かれていない制約を炙り出す。
- 「この関数を単体で呼び出したら何が必要か、前提を全部出して」
- 「このコード全体で、順番を間違えると壊れる処理はどこか」
- 「外部の状態(DB・ファイル・グローバル変数)に依存している箇所を全部リストアップして」
4. 異常系・失敗時の挙動確認
ハッピーパス偏重の弱点を突く。
- 「各関数が失敗したとき、呼び出し元はどう処理するか確認して」
- 「処理が途中で止まったとき、データはどういう状態になるか」
- 「エラーが握りつぶされている箇所はあるか」(try/catchで何もしていない箇所)
5. データが増えたときの挙動予測
- 「データが1万件になったとき、計算量的に問題になる処理はあるか」
- 「ループのネスト構造を確認して、O(n²)以上の処理がないか指摘して」
質問の仕方の共通コツ
- ラバーダック形式――「このコードを、コードを知らない人に説明するように解説して」。AIが理解せず書いた箇所は説明が曖昧になる
- レビュアー役を明示――「シニアエンジニアとして、このコードの問題点だけを指摘して。褒めなくていい」
- 検証する順番――「暗黙の前提を出す → ロジックを説明させる → エッジケースを列挙 → 異常系を確認」の順が効果的
「不変条件スキャン法」――芋づる式にバグを引き出す方法論
特定の視点でコード全体を走査させると、その視点に引っかかる関連箇所が芋づる式に浮上してきます。これを意図的に使う方法論です。
ステップ1:不変条件を言語化する
「このコードが正しく動くために、常に成立していなければならない条件」を1文で定義します。
例:
- イベントリスナーはリセット後に1つだけ存在する
- 同一ユーザーIDのセッションは同時に1つだけ
- この配列は常にソート済みである
コツ:「このコードの不変条件を列挙して」とAIに聞くと出発点になります。
ステップ2:その条件が破れる経路を全列挙させる
「この条件が破れるとしたら、どの処理・どのタイミングか全部出して」
ここで関連コードが自然に引き込まれ、副次的なバグが浮上することがあります。
関数単位で検証させる汎用プロンプトテンプレート
走査範囲が広がりすぎて終わらないようにするため、関数を1つずつ貼り替えて順次実行するのがコツです。
基本テンプレート
以下の関数を検査してください。
【対象関数】
[関数名・コードをここに貼る]
【検査項目】
1. この関数が正しく動くための前提条件(呼び出し前に成立すべきこと)
2. この関数が保証すべき事後条件(呼び出し後に成立すべきこと)
3. 上記の条件が破れるケースを具体的に列挙
4. 関数内で外部状態を読み書きしている箇所
5. 問題があれば指摘(なければ「問題なし」と明記)
上記の結果を見て最終的に人間が判断します。
使い方
関数を1つずつ貼り替えて順次実行するだけです。
- 1回目:関数Aを貼って実行
- 2回目:関数Bを貼って実行
- 全部終わったら:「上記の検査で出た問題点を一覧にして優先順位をつけて」
最後にまとめさせるプロンプト
ここまでの検査結果をまとめてください。
【出力形式】
- 問題あり:関数名・問題の概要・深刻度
- 問題なし:関数名のみ
- 修正推奨順:深刻度順に並べる
推測や補完は不要。検査済みの内容だけをまとめること。
【制約】の「外に出ない」「不明と書く」が最重要です。これを入れないとAIが勝手に呼び出し元を推測して範囲が広がります。
まとめ
AIが生成したコードのデバッグで一番大切なのは、「AIを賢い検索エンジンとして使うのではなく、情報を正確に渡した上で論理を言語化させる道具として使う」ことです。
特に効果的な流れは以下の通りです。
- 暗黙の前提を出す――前提が間違っていたらロジック検証が意味をなさない
- ロジックを説明させる――理解せず書いた箇所は説明が曖昧になる
- エッジケースを列挙させる――数を多めに指定する(20個など)
- 異常系を確認させる――失敗時にデータがどんな状態になるかを確認
- 関数単位で繰り返す――範囲を絞って「外に出ない」制約をつける


