「最小の再現コード」をAIと作る:長いプログラムから不具合だけを切り出す手順
AIを使いこなすClaudeで執筆し、GPTで自動校閲・修正しています。

結論から言うと、手順は次の3つです。 1. 削る前に「同じ不具合」の基準(エラーメッセージや出力)を決めて記録する。 2. 1回に1か所だけ削り、そのたびに実行して、基準と一致するか確かめる。 3. 一致しなくなったら、その削除を取り消す。 AIには「どこを削れそうか」の候補出しと差分づくりを任せます。実行して症状を見比べる作業は、人が自分の環境で行います。AIは実行していなくても「この削除後も再現するはずです」と言い切ることがあるためです。 MDNのブラウザ不具合の報告に関する解説も、最小テストケースは小さく独立させ、外部依存や無関係なコードを含めず、関係するコードを残すよう説明しています。この記事では、その考え方をAIが書いた長いコードに当てはめます。
この記事の目次
先に決める:「同じ不具合」の基準を1行で書く
削り始めると、別の不具合に置き換わっていても気づきにくくなります。最初に基準を決めておきます。
- 実行コマンドを1つにする(例:node repro.js)。
- エラーなら、メッセージの1行目と種類を控える。
- エラーが出ない不具合なら、「期待した出力」と「実際の出力」を2行で書く。
- 使っている言語やパッケージのバージョンを控える。
この4点を、削る作業のあいだ変えずに使います。作業用のブランチかコピーも用意し、元のコードには手を付けません。
削る順序のチェックリスト
外側から内側へ進めます。各項目を終えるたびに実行して、基準と照らします。
- 起動経路を短くする:画面操作やサーバー経由で起きる不具合は、不具合のある関数を直接呼ぶ数行のスクリプトに置き換えます。
- 外部とのやり取りを固定データにする:DB、ネットワーク、ファイル読み込みを、ダミーの値に置き換えます。値の型と形(文字列か数値か、配列か単体か)は元と同じにします。
- 大きなブロックから消す:関係なさそうな機能や関数を、まとめて1つずつ消します。
- 1回に1か所だけ:2か所同時に消すと、どちらが効いたのか分かりません。
- 基準と一致したら採用して保存、違えば即座に戻す:採用のたびにコミットかファイル保存をしておくと戻しやすくなります。
- 細部を整理する:未使用の import、設定、変数を消します。
- 依存を減らす:外部パッケージは、不具合に関わるものだけを残してバージョンを記録します。
- 最終確認:次の節の確認をします。
AIには、手順3を「ここまでのコードのうち、不具合に無関係そうなブロックを、削る優先順に3つ挙げて」と頼むと進めやすくなります。
削りすぎの見分け方
症状が消えた、あるいは別物になったときの判断表です。
- 不具合が出なくなった、疑うこと:不具合の原因を作る入力や状態まで消した、対処:直前の削除を戻し、その削除が何を変えたか考える
- 別のエラーに変わった、疑うこと:必要な定義や import を消しただけ、対処:基準と違う以上、採用しない。戻す
- 固定データにしたら再現しなくなった、疑うこと:元データの型や形、長さ、空値などを再現できていない、対処:元の実データの形を確認し(機密は伏せる)、同じ形で置き換える
- 実行のたびに結果が違う、疑うこと:実行順序、時刻、乱数、非同期処理が絡んでいる、対処:それらを残すか、固定してもなお起きるか調べる
- 自分の環境でしか起きない、疑うこと:環境依存の可能性、対処:空のフォルダで依存を入れ直して試す
仕上げの確認は2つです。
- 別の場所で動かす:最小コードを新しい空のフォルダに置き、依存を入れ直して実行し、基準どおりの症状が出るか確かめます。
- 修正と対にする:原因と思われる箇所を直したとき、最小コードの症状が消えるか確かめます。元のプログラムでも同じ修正で直るなら、切り出しは成功です。
AIへの依頼文テンプレート
1. 削る候補を出させる
次のコードから、不具合だけを残した最小の再現コードを作りたいです。
一度に大きく書き換えず、1回に1か所だけ削る形で進めてください。
【同じ不具合の基準】
- 実行コマンド:(例)node repro.js
- エラー1行目:(そのまま貼る)
- 期待した動作:(1行)
- 実際の動作:(1行)
- 環境:(言語とバージョン、主要パッケージ)
【お願い】
1. 不具合に無関係そうな部分を、削る優先順に最大3つ挙げ、理由を書いてください。
2. 各候補を削ったとき、基準と症状が変わるリスクも書いてください。
3. 削った後のコードは、あなたが実行した結果としては扱わないでください。
「実行すれば確認できる点」だけを書いてください。
【コード】
(機密を除いたコードを貼る)候補を1つ採用して実行し、結果を次のメッセージで返します。「基準と一致」「別のエラー(全文)」「症状が消えた」のどれかを書けば、AIが次の候補を出しやすくなります。
2. 完成前の点検をさせる
最小の再現コードができました。次の点を点検して、指摘だけ出してください。
- 不具合と関係のない処理や import が残っていないか
- 固定データの型や形が、元のデータと違う可能性がないか
- 実行のたびに結果が変わる要素(時刻、乱数、非同期)がないか
- 他の人が再現するために足りない情報(バージョンや前提)はないか
コードの書き換えはしないでください。貼る前に置き換えるもの
APIキー、トークン、個人名、社内のURLやテーブル名、顧客データは貼りません。ダミーの値に置き換えても症状が変わらないかも、確認項目の一つです。
説明用の例
次は、症状の消え方を示すために作った架空のコードです。実際には実行していません。
元のプログラムでは、検索画面から渡されるIDが文字列の "1" だったとします。最小化の途中で、次の形にまでたどり着いたとします。
const users = [{ id: 1, name: "A" }];
const findUser = (id) => users.find((u) => u.id === id);
const idFromQuery = "1"; // 元の画面では文字列で渡ってくる
console.log(findUser(idFromQuery).name);想定する基準は、Cannot read properties of undefined (reading 'name') という種類のエラーです。
ここで idFromQuery を数値の 1 に直して「整理」すると、find が見つかるので症状が消えます。これが削りすぎの例です。原因は「関数の中身」ではなく「渡される値の型」だったので、固定データの型を残す必要がありました。
最近壊れた場合は、コードを削る前にコミットを絞る
昨日までは動いていた場合、コードを削る前に、どの変更から壊れたかを git bisect で絞る方法があります。Gitの公式ドキュメントでは、git bisect start、git bisect bad、git bisect good <コミット> で範囲を指定し、候補のコミットを順に確かめる流れが説明されています。git bisect run <コマンド> を使うと、終了コードが0なら良い状態、1〜127(125を除く)なら悪い状態、125なら判定不能としてスキップし、それ以外なら探索を中断します。探索を終えたら git bisect reset で開始前の状態に戻します。
前の節で作った1コマンドの再現スクリプトは、そのまま判定用にも使えます。ただし、スクリプト自体を探索の対象のコミットに含めない(作業ツリーの外に置く)ようにします。範囲が絞れたら、そのコミットの差分だけをAIに見せると、切り出す対象も小さくなります。
できあがった最小コードに添えるもの
誰かに報告するときは、MDNが挙げている項目に沿って、次を最小コードと一緒に書きます。
- 期待した結果と、実際の結果
- 環境(言語、ランタイム、ブラウザのバージョンなど)
- 再現の手順(実行コマンドを含む)
- エラーメッセージの全文
これで、受け取った人が同じ症状を再現できます。AIに修正を頼むときも、元の長いコードではなくこの最小コードを渡すほうが、原因の切り分けがしやすくなります。

