LOADING
本文へ移動

AIに「ハードコードするな」と何度も言っている人へ——同じ手直しを繰り返さない依頼と検証

「その値は設定から取って」「この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日。送料の条件は説明用の架空例。コードとテストはローカル実行で検証した。

コメント

PAGE TOP
Core
Dashboard
MyRide
Category
タイトルとURLをコピーしました