2026.08.15#LLM#Frontend8分

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

ルールを作る前に基準にした設計原則があります。Functional Core, Imperative Shellはプログラムを2つの領域に分けます。

入力(API · 画面 · イベント)
        ↓
Imperative Shell: 入力を整え、依存を準備する
        ↓
Functional Core: 検証 · 計算 · 判断 · 新しい状態の生成
        ↓
Imperative Shell: DB保存 · API呼び出し · ログ · レスポンス

計算・検証・判断のように同じ入力に同じ結果を返すべきルールは純粋関数(Core)に置き、HTTP・DB・時間・乱数のように外の世界に触れる部分はシェル(Shell)へ押し出します。副作用をなくすのではなく、核心のルールに混ざらないよう境界の外へ分離するのです。

この区分はエージェントにとってより重要です。純粋なコアはサーバーもブラウザもなしにミリ秒単位のテストで検証できるため、エージェントは画面を実行してみなくても、自分の書いたコードを自分で確かめられます。以下の3つのルールは、この原則をアプリケーションレイヤーに移したものです。

ルール3つ
1. 共通部品の優先検出

3か所以上で一緒に変わるコードだけをコンポーネントに統合します。2か所の似たコードは偶然かもしれませんが、3か所で一緒に変わるなら同じ部品です。逆に言えば、再利用箇所が2か所だけのコードは無理に統合しません。

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です。

3. コンポーネントの軽量化

互いに異なる関心事(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件の修正箇所の変化です。

重複統合6件の修正箇所: 統合前の合計75か所が統合後13か所に減った

同じ文言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%の候補リストにすぎず、人のフィルタリングが必要です。

Claude Codeスキル

3つのルールをClaude Codeのスキルにして公開しました。画面コンポーネントを書くとき・直すとき、UIからサーバーへデータを送るときに、エージェントがスキルを読んでルールを適用します。

構成は次のとおりです。

ファイル内容
SKILL.md、SKILL.ko.mdルール3つ
references/evidence.mdこの記事の数値と測定方法
references/audit.md監査の手順
scripts/audit_readiness.pyコンポーネントごとの関心事の種類数を数えるスクリプト
©2026 Imjurney · All Rights Reserved8 Posts | 4 Projects