AI時代のコード品質:型設計で「死んだ分岐」を根絶する

AI・テクノロジー
STΛCKHUB ANALYSIS2026.08.27 02:00

AIが生成する「増え続ける分岐」の正体

深夜の障害対応で、ログを追いながら「なぜこんなif文がここに存在するのか」と頭を抱えた経験はないだろうか。現代のソフトウェア開発において、我々エンジニアはAIコーディングエージェントという強力な相棒を手に入れた。しかし、その恩恵の裏側で、コードベースには「死んだ分岐」が急速に蓄積されている。Google Cloudの2025年DORAレポートが示す通り、AIの導入はデリバリー量を増幅させるが、同時に変更失敗率という安定性の指標を悪化させるリスクを孕んでいる。LinearBの2026年Software Engineering Benchmarks Reportによれば、AI支援を受けたPRは平均408行と、支援なしの157行と比較して約2.6倍に肥大化しており、レビュー待ち時間は5倍以上にまで膨れ上がっている。この事実は、我々が「書く速度」の向上に浮かれている間に、「読むコスト」が限界を超えつつあることを如実に物語っている。

なぜ、AIはこれほどまでに冗長なコードを生成するのか。それはAIの指示が悪いからではない。型システムが「不正な状態」を許容しているからだ。例えば、従業員一覧の状態を管理する際、isLoading、employees、errorというフラグを個別に定義すると、型上は「読み込み中であり、かつエラーも発生しており、データも存在する」という、現実にはあり得ない組み合わせが合法となってしまう。AIは律儀にその「型が許した状態」を処理する分岐を生成する。これはAIにとって正しい仕事であり、人間が「防御的に書くな」と指示しても、型がその状態を許している限り、根本的な解決にはならない。我々エンジニアが直面しているのは、AIの生成能力と、それを制御する型設計の間の深刻なミスマッチである。レビューにおいて、その分岐が「実際に起こりうるもの」なのか「型が緩いせいで書かれただけの死んだコード」なのかを判別するには、呼び出し元をすべて追跡するしかない。この確認作業はコードに記録されず、次のエンジニアがまた同じ苦労を繰り返す。この無限ループこそが、現代の技術負債の正体である。

判別共用体による状態の制約と網羅性検査

では、この「読むコスト」を劇的に減らすにはどうすればよいか。答えはシンプルだ。状態をフラグの組み合わせ(直積)で管理するのをやめ、判別共用体(Discriminated Union)という「直和」の概念へ移行することである。これにより、表現できる状態の数を、実際に起こりうる状態の数と完全に一致させることができる。例えば、statusというタグを軸に、idle、loading、loaded、failedという状態を定義すれば、TypeScriptのコンパイラはタグに基づいた絞り込みを行い、無効なフィールドへのアクセスを型レベルで遮断する。これにより、if (!data)のような、型が緩いからこそ必要だった「死んだ分岐」をコードから一掃できる。レビュアーは、状態の遷移が妥当かどうかを型定義から読み取るだけで済み、個別の分岐が到達可能かを検証する不毛な作業から解放される。

さらに強力なのが、網羅性検査(Exhaustiveness Checking)の活用だ。状態を一つ追加した際、判別共用体を使っていれば、コンパイラは即座にすべてのswitch文やcase文でエラーを吐き出す。これは単なる警告ではない。エージェントに対する「修正すべき箇所の一覧」そのものだ。AIに「この状態を追加して」と指示した際、直し忘れが発生しても、型エラーがそれを機械的に指摘する。このフィードバックループこそが、AI時代の開発における最強の制御装置である。以下の表は、従来のフラグ管理と判別共用体の設計思想の違いをまとめたものである。

比較項目 フラグ管理(直積) 判別共用体(直和)
状態の表現 フラグの組み合わせで表現 排他的な状態として定義
不正な状態 型として許容される 型として表現不可能
分岐の網羅性 人間が管理(漏れやすい) コンパイラが強制(漏れない)
AIとの相性 死んだ分岐を量産する 必要な分岐のみを生成する

もちろん、型を厳しくすればよいという単純な話ではない。Armin Ronacherが指摘するように、条件型や深いジェネリクスを駆使した「型パズル」は、AIにとって理解不能なブラックボックスとなり、かえって性能を低下させる。我々が目指すべきは、名前が付いていて、定義が局所にまとまっており、エラーメッセージが「どこを直すべきか」を明確に指し示す、素直な型設計である。型で書けない規約はLintに落とし、型はあくまで「レビューで確かめる項目の数」を減らすための道具として割り切るべきだ。

境界でのParseとエンジニアの処方箋

最後に、外部からの入力に対する「検証済みという事実」の扱いについて触れたい。Alexis Kingが提唱した「Parse, don’t validate」の原則は、AI時代においてより重要度を増している。多くのコードベースでは、関数内で毎回if (email.includes('@'))のような検証を行っているが、これは検証したという事実が型として運ばれていないために起こる無駄な重複である。境界で一度parseEmailAddressのような関数を通し、検証済みの型(Branded Typeなど)に変換してしまえば、内側の関数では検証の必要性が消滅する。これにより、AIが生成するコードから「検証の重複」というノイズを排除できる。ただし、名前を付けただけのラッパーでは不十分だ。コンストラクタを閉じた設計にしなければ、結局は型安全の皮を被っただけの脆弱なコードになる。

我々エンジニアが明日から取るべき実践的な処方箋は明確だ。まず、既存のフラグ管理を判別共用体へリファクタリングし、AIが「死んだ分岐」を生成する余地を物理的に奪うこと。次に、外部入力の境界を明確にし、検証済みの事実を型として運ぶ設計を徹底すること。そして、AIが生成したコードをレビューする際、「この分岐は型で表現できないか?」という視点を常に持つことだ。AIは我々の思考を増幅するが、その思考の質が低ければ、増幅されるのは負債である。

ここで、業界への問いを投げかけたい。我々は、AIが生成する膨大なコードを「人間がレビューし続ける」という前提に固執しすぎていないだろうか。もし、型システムがコードの正当性を担保する範囲を極限まで広げたとき、人間がレビューすべき対象は「ロジックの正しさ」ではなく「ドメインモデルの妥当性」へとシフトするのではないか。AIがコードを書く時代において、エンジニアの価値は「いかに速く書くか」から「いかに型という言語でドメインの制約を定義し、AIを正しく制約するか」へと完全に移行している。あなたは、AIにコードを書かせるための「型」を設計できているだろうか。それとも、AIが生成した死んだ分岐を、今日もまたレビューし続けるつもりだろうか。

Published at 02:00

コメント

タイトルとURLをコピーしました