AIコーディング時の修正量を最適化する、3つのアプリケーションコードルール
AIコーディングエージェントは、ファイルを1つ直すたびに会話全体を送り直します。直すファイルの数はそれ自体でコスト増の根源になります。 この記事は、変更が波及する範囲を減らす作成ルールを3つ定め、実際のリポジトリで修正箇所75か所が13か所に減ることを測定した記録です。ルールはエビデンスと一緒にClaude Codeのスキルとして公開しました。
PoCは要求に速く応える必要があります。スキーマとワークフローが頻繁に変わり、そのたびにアプリケーションコードが膨らみ、いつの間にかコードレビューが不可能になります。実際の社内PoCリポジトリでは、画面1つが1,065行まで肥大化していました。
人にとっても問題ですが、エージェントにとってはコスト構造そのものです。エージェントのセッション2,149ターンを調査すると、387件の修正がすべて「1ターンに1ファイル」方式で処理されており、リクエスト1回あたり平均39万トークンのコンテキストが再送信されていました。
エージェントはファイルを1つ直すたびに1ターンを使い、毎ターン会話全体を送り直します。同じ修正が5つのファイルに散らばっていれば、トークンコストも5倍になります。
必要なのは、変更が波及する範囲を減らす作成ルールでした。
| 代案 | 限界 |
|---|---|
| 全面リファクタリング | 再利用箇所が少ないコードまで書き直し、投資が回収できない場所が生まれる |
| コードレビューの強化 | コードを読むだけでは捕まえられず、画面を実行してネットワークタブまで見る必要がある |
| ESLintの強制ルール | 「一緒に変わるか」の判断にはドメイン知識が必要で、自動検出の精度は8~30% |
| 「コード整理」の指針 | 漠然としていて効果を検証する基準がない |
共通の結論は2つでした。全部を直さず回収できる場所だけ直すこと、そして自動化は候補探しまでに使い、判断は人がすること。
ルールを作る前に基準にした設計原則があります。Functional Core, Imperative Shellはプログラムを2つの領域に分けます。
入力(API · 画面 · イベント)
↓
Imperative Shell: 入力を整え、依存を準備する
↓
Functional Core: 検証 · 計算 · 判断 · 新しい状態の生成
↓
Imperative Shell: DB保存 · API呼び出し · ログ · レスポンス計算・検証・判断のように同じ入力に同じ結果を返すべきルールは純粋関数(Core)に置き、HTTP・DB・時間・乱数のように外の世界に触れる部分はシェル(Shell)へ押し出します。副作用をなくすのではなく、核心のルールに混ざらないよう境界の外へ分離するのです。
この区分はエージェントにとってより重要です。純粋なコアはサーバーもブラウザもなしにミリ秒単位のテストで検証できるため、エージェントは画面を実行してみなくても、自分の書いたコードを自分で確かめられます。以下の3つのルールは、この原則をアプリケーションレイヤーに移したものです。
3か所以上で一緒に変わるコードだけをコンポーネントに統合します。2か所の似たコードは偶然かもしれませんが、3か所で一緒に変わるなら同じ部品です。逆に言えば、再利用箇所が2か所だけのコードは無理に統合しません。
サーバーへ送るデータを画面コンポーネントの中で組み立てません。組み立てはアダプター(画面とサーバーの間でデータの形を変換する層)に分離し、アダプターにはインタフェース仕様に合わせた仕様テスト(サーバーが期待する形と一致するかを確認するテスト)を必須にします。
// 画面の中で組み立てず、純粋関数に分離する
function buildOrder({ cart, orderId, createdAt }) {
return { id: orderId, createdAt, total: calculateTotal(cart) };
}
// 保存・送信のような副作用は外側で
async function createOrder(cart, { idGenerator, clock, orderRepository }) {
const order = buildOrder({
cart,
orderId: idGenerator.generate(),
createdAt: clock.now().toISOString(),
});
await orderRepository.save(order);
return order;
}buildOrderは同じ入力に常に同じ結果を返す純粋関数なので、サーバーなしでテストできます。先の原則で言えば、組み立てはCore、保存と送信はShellです。
互いに異なる関心事(1つのコンポーネントが抱えている役割の種類)が4種以上で、300行を超えるコンポーネントを分離対象と見ます。直す場所を速く見つけるためのルールです。
「関心事4種」という数字は勘ではなく分布から出ました。カットラインごとに引っかかるファイル数を数えました。
| カットライン | 引っかかるファイル数 |
|---|---|
| 1種以上 (中央値) | 204個 (61%) |
| 2種以上 | 111個 (33%) |
| 3種以上 | 84個 (25%) |
| 4種以上 | 29個 (9%) |
| 5種以上 | 20個 (6%) |
カットラインを中央値(1種)に置くと、定義上つねに半分以上が引っかかります。監査の目的は普通のファイルではなく、普通から大きく外れたケースを捉えることなので、1人でレビューできる数になる地点(4種、29個)にカットラインを置きました。
2026年6月から8月まで、React 19 + TypeScript構成で実測しました。
重複統合6件の修正箇所の変化です。
同じ文言1つを変えるのに22か所を直していたエラーバナーが1か所になり、合計では13.1倍の削減です。
アダプター導入の効果は別に測定しました。導入前は誤ったペイロードのコード8件のうち1件しか捕まりませんでしたが、導入後は8件すべてがテストで遮断され、画面を実行してネットワークタブを開く代わりに、196msのテスト1回で確認が終わりました。サーバー契約が全面改編されたとき、画面コードの変更量は0行でした。
数を数えるルールにした理由が2つあります。
1つは、統合するかどうかが好みの問題から外れたことです。「このコードは重複しているっぽい」という表現には合意しにくいですが、「このコードにより変更が及ぶ箇所は3つ確認されている」という状況では、数を数えれば終わります。関心事4種と300行も勘ではなく、分布を見て引いた線です。レビューで争う代わりに、数えて次へ進みます。
もう1つは、確認する対象が狭まったことです。アダプターにインターフェース仕様のテストが付いていれば、ペイロードが正しいか見るために画面を立ち上げてネットワークタブを開く必要がありません。関心事のカットラインは監査対象を29個のファイルに限定します。コードベースを隅々まで知らない人でも、何を見るかは特定できます。ただし、その中で統合するかどうかを判断するには、やはりドメイン知識が必要です。
このルールはアプリケーションレイヤー(画面コンポーネント、APIアダプターなど、外側とドメインの接続部)にだけ適用します。ドメインコアは変更が波及する仕方が違うため、必ず同じルールが適用できるとは限りません。
数値の限界も残しておきます。この記事では、同じ作業を2つの構造で比較実験したわけではなく、統合前後の「直すべきファイルの集合」を計算したものであり、事例6件、リポジトリ1つ、TypeScript限定なので、数値はリポジトリごとに異なりえます。共通部品の自動検出も精度8~30%の候補リストにすぎず、人のフィルタリングが必要です。
3つのルールをClaude Codeのスキルにして公開しました。画面コンポーネントを書くとき・直すとき、UIからサーバーへデータを送るときに、エージェントがスキルを読んでルールを適用します。
構成は次のとおりです。
| ファイル | 内容 |
|---|---|
SKILL.md、SKILL.ko.md | ルール3つ |
references/evidence.md | この記事の数値と測定方法 |
references/audit.md | 監査の手順 |
scripts/audit_readiness.py | コンポーネントごとの関心事の種類数を数えるスクリプト |
- agent-change-cost (MIT)