「その値は設定から取って」「このIDだけの分岐を作らないで」。AIに実装を任せるたび、同じ指摘をしていないだろうか。
修正を頼めば、その場では直る。けれど次の変更では、また似たコードが出てくる。生成は速くても、毎回こちらが細部を確認するなら、任せた実感は持ちにくい。
この記事は、AIが書いたコードの採用判断をするエンジニアや開発リーダーに向けて書いている。ハードコーディングという言葉は知っているが、禁止事項を増やしても手直しが終わらない人が対象だ。
持ち帰ってほしいのは、注意書きをさらに増やす方法ではない。繰り返した指摘を一つ選び、次回から同じ失敗を検出できる合格条件に変える方法だ。まず症状を切り分け、依頼文へ書き込み、条件を変えたテストで確認する。
1.同じ指摘になる原因を、三つに切り分ける
「またハードコードした」と感じたら、まず何が起きたかを見る。次の表は原因を断定するものではなく、調べる順番を決めるための手がかりだ。
| 見えている症状 | 最初に確認すること | 次の依頼に加えること |
|---|---|---|
| 数字を定数にしただけで、設定を変えても結果が変わらない | 値の意味を明示することと、設定に追従することを混同していないか | 変わる値と、その取得元 |
| 設定ファイルはできたが、古い動作のまま | 実際の呼び出し元が、その設定を渡しているか | 設定を読む入口からの確認 |
| サンプルでは通るが、別の日付やIDでは失敗する | サンプルの値だけを特別扱いする分岐がないか | 同じ規則が適用される別入力のテスト |
「ハードコーディング禁止」という一文だけでは、どこまで変化へ対応すべきかを決められない。定数化すれば十分なのか、環境ごとに切り替わるのか、業務担当者が更新できる必要があるのかで、適切な実装は違う。
もちろん、仕様を書いてもAIが従わないことはあり得る。だから、依頼を詳しくするだけで終えず、実装が満たしたかを確認できる条件まで用意する。
2.次の依頼に、合格条件を四行だけ足す
最初から大きな設計書を作る必要はない。直近で繰り返した指摘について、次の四行を埋めてみる。
変わるもの:何が、どのタイミングで変わるか。
取得元:その値は、既存のどの設定・入力から受け取るか。
変更時の期待:何を変えたら、どの結果がどう変わるか。
取得できない場合:エラーにするか、定めた既定値を使うか。
たとえば、送料計算ならこう書ける。以降の金額・条件は説明用の架空例であり、実際の障害記録ではない。
変わるもの:送料無料の基準と通常送料。変更は次の呼び出しから反映する。
取得元:呼び出し元が渡す config.shipping。
変更時の期待:基準を5,000円から8,000円に変えると、
6,000円の注文には通常送料がかかる。
取得できない場合:必須設定の欠落・不正値はエラーにする。
この四行は、完全な仕様を置き換えるものではない。「次回も同じ指摘をすることになる箇所」を、既存仕様に追加するためのものだ。この例なら、金額は0以上の整数の円、基準以上なら送料無料、といった計算の約束も必要になる。
取得元が分からないときは、AIに新しい設定ファイルを即座に作らせる前に、既存の設定と呼び出し元を調べてもらう。既存の管理元と別の値を作ると、どちらを変えればよいか分からなくなる。
3.「定数にしたから直った」を、合格にしない
現在の設定が「5,000円以上で送料無料、それ未満は500円」だとする。次のコードは、4,000円と5,000円の注文だけを試すと、期待どおり動いて見える。
function shippingFeeBad(subtotalYen, policy) {
return subtotalYen >= 5000 ? 0 : 500;
}
しかし、渡された policy を使っていない。基準を8,000円へ変えても、6,000円の注文は無料のままだ。
5000 を FREE_SHIPPING_THRESHOLD という定数へ置き換えると、値の意味は読みやすくなる。それでも、呼び出し元の設定が使われていなければ、今回の要求は満たさない。
一方で、コード内の数字をすべて追い出す必要もない。送料無料を表す 0 や、テストに書く期待値まで環境変数にする理由はない。定数化は意味を読みやすくする。設定化は変更する場所を分ける。 必要なのは、変わる約束をした値が、その管理元に追従することだ。
JavaScriptのESLintには、数値の意味を定数で示すための no-magic-numbers がある。公式の実装とテストでも、数値を定数として宣言するケースは許容されている。Lintは補助になるが、業務設定の反映は別に確かめたい。
4.レビューでは「条件を変えた証拠」を受け取る
送料計算なら、普段の注文に加えて、まず次の二つを確認する。
| 一つだけ変える条件 | 入力する注文金額 | 期待する送料 |
|---|---|---|
| 送料無料の基準を5,000円から8,000円へ | 6,000円 | 500円 |
| 通常送料を500円から300円へ | 4,000円 | 300円 |
最初の固定値の実装を実行すると、上のケースは0円、下は500円を返した。通常のサンプルでは見えなかった不具合を、条件を変えることで再現できる。
ここで計算関数に正しい設定を直接渡すテストだけを作ると、呼び出し元の不備を見逃す。利用者が変更する管理元から、結果が返るところまでを確認する。設定ファイルで管理する機能なら、その読み込み経路を含むテストが必要だ。
別の日付やIDを試すときも、「違う値なら何でもよい」とはしない。同じ業務ルールが適用される入力を選び、期待結果を先に決める。仕様にない新しい条件を、その場で追加しない。
テストの期待値は、仕様から決める。本番コードと同じ計算式で期待値を作ると、両方が同じ間違いをすることがある。また、テストが緑になったときは、失敗していた期待値を書き換えて通したのか、実装が直ったのかを差分で区別する。
Anthropicのエージェント評価の解説も、コードによる判定、モデルによる判定、人による確認を区別している。ここでは、数値や境界条件はテストで確かめ、取得元や例外分岐の妥当性はレビューで確かめる、という役割分担にする。
別のAIにレビューを頼むなら、「品質を見て」に加えて、「設定を無視する経路がないか」「サンプルIDだけの分岐がないか」を渡す。レビュー担当を変えただけでは、同じ前提の見落としを防げるとは限らない。
5.自分の案件へ持ち帰るための依頼文
次の文面をコピーし、角括弧の箇所を埋めてから使ってほしい。依頼する側が決める値を、AIに推測で埋めさせないことが大切だ。
今回、繰り返したくない不具合は[具体的な症状]です。
既存の仕様と設定の管理元、実際の呼び出し元を確認してから修正してください。
変わるもの:[対象の値と、変更を反映するタイミング]
取得元:[既存の設定・入力・APIなど]
変更時の期待:[一つの条件を変えたときの入力と期待結果]
取得できない場合:[エラー/合意済みの既定値とその適用条件]
検証では、通常ケースに加えて次を確認してください。
- 上記の条件だけを変えても、期待どおり動くこと。
- 実際の呼び出し元から、その値が渡されていること。
- 境界値、不正入力、既存機能への影響。
- サンプル固有の値にだけ対応する分岐が増えていないこと。
取得元や仕様が不明なら、不明点を示してください。
既存テストの削除・無効化・期待値の変更は理由を示してください。
完了時は、変更箇所、実行コマンドと結果、未検証の範囲を報告してください。
受け入れるときは、次の五点を確認する。
- 管理元:変わる値は、合意した設定や入力から来ているか。
- 接続:実際の呼び出し経路で、その値が使われているか。
- 変更への追従:一つの条件だけを変えたテストがあるか。
- 失敗時の動作:欠落や不正値を、根拠のない既定値で隠していないか。
- 検証の維持:合格させるために、テストや検査を弱めていないか。
最初から全機能へ適用する必要はない。直近で二度指摘したことを一つ選び、四行を書き、再発を検出するテストを残す。それなら、次回は同じ問題について、記憶と目視だけに頼らず確認できる。
6.検証用コード:設定を受け取り、変えて確かめる
ここからは、送料の例を手元で再現したい人向けの実装とテストだ。上の依頼文を自分の案件へ使うだけなら、このコードへ置き換える必要はない。
設定ファイルの読み込みは呼び出し元の責務とし、ここでは quoteShipping が渡された設定を計算処理へつなぐ。金額は0以上の安全な整数とし、文字列・欠落・不正値は例外にする。既定値を使う設計が必要なら、その値と条件を別途合意する。
次の二つのコードブロックを、順に同じ shipping-check.mjs ファイルへ保存する。
function requireYen(value, name) {
if (!Number.isSafeInteger(value) || value < 0) {
throw new TypeError(`${name} must be a non-negative safe integer`);
}
}
function shippingFee(subtotalYen, policy) {
requireYen(subtotalYen, "subtotalYen");
if (policy === null || typeof policy !== "object") {
throw new TypeError("shipping policy is required");
}
const { freeShippingThresholdYen, standardFeeYen } = policy;
requireYen(freeShippingThresholdYen, "freeShippingThresholdYen");
requireYen(standardFeeYen, "standardFeeYen");
return subtotalYen >= freeShippingThresholdYen ? 0 : standardFeeYen;
}
function quoteShipping(subtotalYen, config) {
return shippingFee(subtotalYen, config?.shipping);
}
次のテストでは、送料無料の基準と通常送料を別々に変える。境界値と、設定が欠けた場合も確認する。
import assert from "node:assert/strict";
const original = {
shipping: { freeShippingThresholdYen: 5000, standardFeeYen: 500 },
};
const changedThreshold = {
shipping: { freeShippingThresholdYen: 8000, standardFeeYen: 500 },
};
const changedFee = {
shipping: { freeShippingThresholdYen: 5000, standardFeeYen: 300 },
};
assert.equal(quoteShipping(0, original), 500);
assert.equal(quoteShipping(4999, original), 500);
assert.equal(quoteShipping(5000, original), 0);
assert.equal(quoteShipping(5001, original), 0);
assert.equal(quoteShipping(6000, changedThreshold), 500);
assert.equal(quoteShipping(4000, changedFee), 300);
assert.throws(() => quoteShipping(4000, {}), TypeError);
assert.throws(() => quoteShipping(4000, {
shipping: { freeShippingThresholdYen: 5000 },
}), TypeError);
assert.throws(() => quoteShipping(-1, original), TypeError);
assert.throws(() => quoteShipping(1.5, original), TypeError);
assert.throws(() => quoteShipping(4000, {
shipping: { freeShippingThresholdYen: 5000, standardFeeYen: "500" },
}), TypeError);
assert.throws(() => quoteShipping(4000, {
shipping: { freeShippingThresholdYen: -1, standardFeeYen: 500 },
}), TypeError);
console.log("12 checks passed");
保存したファイルは、次のコマンドで実行できる。
node shipping-check.mjs
Node.js v22.22.0で、掲載した12チェックが通ることを確認した。さらに、設定を固定値に戻した版と、境界条件を >= から > へ変えた版では、同じテストが失敗することを確認した。
確認したのは、この説明用コードにおける動作だ。実サービスの設定読み込みやキャッシュ、再起動が必要な構成は、実際の環境で別途確かめる必要がある。AI全般の失敗率や、手戻り時間の削減効果を測った結果ではない。
次の修正依頼で、一つだけ変える
同じ注意を繰り返しているなら、その一つを「どの条件を変えたとき、何が起きるべきか」に書き換える。その期待をテストに残せば、次の修正でも確認し直せる。
任せる範囲がコードの修正を超え、本番操作や業務全体へ広がる場合は、AIを任せる前の10問で、完了条件・権限・停止条件まで整理しておきたい。
生成された変更が増え、レビュー待ちや誤検知が課題になっている場合は、AIコードレビューを導入する7原則で、機械検査・AI・人間の役割を分けている。
一次資料確認日:2026年9月25日。送料の条件は説明用の架空例。コードとテストはローカル実行で検証した。




コメント