AI時代のコード腐敗を防ぐ:CIで自動化する衛生管理の極意

AI・テクノロジー
STΛCKHUB ANALYSIS2026.07.19 15:00

AIがもたらす「コードの腐敗」という静かなる脅威

Claude CodeやGitHub CopilotといったAIエージェントが開発現場に浸透した今、我々エンジニアが直面しているのは「書く速度」の限界ではなく、「書かれたコードの品質維持」という新たなボトルネックです。AIは文脈を理解したかのように振る舞いますが、その本質は確率的なテキスト生成であり、コードベース全体の整合性や長期的な保守性までを考慮した設計は行いません。結果として、1つの関数に200行もの処理が詰め込まれた巨大関数、意味をなさない変数名、そして「たまたま似ている」だけの重複コードが、まるでスパゲッティコードの増殖のようにコードベースを蝕んでいきます。

特に恐ろしいのは、これらの問題が「動くコード」として生成される点です。人間がレビューする際、目の前の差分が機能要件を満たしていれば、26万行もの既存コードの中に同じ関数が既に存在しているかなど、記憶だけで追えるはずもありません。この「記憶の限界」を補うために、我々は衛生管理を機械に委ねる必要があります。DRY(Don’t Repeat Yourself)原則は、単なる設計の美学ではなく、AI時代においては「トークン消費の最適化」という極めて実利的な戦略へと昇華しました。重複を排除し、モジュール化されたコードは、AIがコンテキストを読み解く際のノイズを減らし、結果として推論コストの削減と精度の向上に直結するのです。

我々が目指すべきは、AIにコードを書かせることではなく、AIが生成するコードを「規律ある枠組み」の中に閉じ込めることです。そのためには、単一のツールに頼るのではなく、役割の異なる複数の静的解析ツールを多層的に組み合わせる「防衛線」の構築が不可欠です。ESLintによる複雑度管理、SonarJSによる認知的負荷の低減、jscpdによる重複検知、そしてknipによるデッドコードの排除。これら4つの柱をCIパイプラインに組み込むことで初めて、AIの爆速開発とコードの健全性を両立させることが可能となります。

CIで強制する「新規コードの厳格化」と既存負債の返済戦略

既存の巨大なコードベースに、いきなり厳格なLintルールを適用するのは、深夜の障害対応よりも精神を削る作業です。何百件もの違反が一斉にCIを赤く染め、開発者のモチベーションを根こそぎ奪うからです。ここで重要なのは「新規コードには厳しく、既存コードには寛容に」という二段構えの戦略です。ESLintの設定において、基本ルールをエラー(error)としてCIを止める設定にしつつ、既存の違反箇所だけを例外リストとして警告(warn)に落とす手法は、現実的な解として極めて有効です。これにより、負債を増やさないという「防波堤」を築きつつ、少しずつ過去の借金を返済する計画的なリファクタリングが可能になります。

また、AIが生成するコードの質を担保するためには、認知的複雑度(Cognitive Complexity)の管理が鍵となります。循環的複雑度が単なる分岐の数を数えるのに対し、認知的複雑度は「人間がコードを追う際の脳の負荷」を測定します。ネストが深いロジックは、AIにとっても人間にとっても理解のコストを増大させます。さらに、変数名や関数名に「単位」や「状態」を明示させるルールを強制することで、コード自体がドキュメントとして機能する状態を作り出せます。例えば、id-lengthルールで3文字以上の命名を強制し、any型を禁止することで、AIの「面倒だから型を握りつぶす」という悪癖を物理的に封じ込めるのです。

さらに高度な戦略として、設計の依存関係をCIで強制する手法があります。no-restricted-importsを活用し、プラグイン層から本体の内部設定への直接参照を禁止するなど、アーキテクチャの境界をコードレベルで守るのです。これは、AIが「近くにあるから」という理由で安易に依存関係を破壊するのを防ぐための、極めて強力なガードレールとなります。以下に、AI時代の開発で必須となる解析ツールの役割分担を整理しました。

観点 ツール 主な役割
大きさ・複雑度 ESLint 巨大関数、深いネスト、引数過多の抑制
ロジックの質 SonarJS + strict TS 認知的複雑度の低減、anyの排除
重複コード jscpd ファイル横断のトークン一致検知
デッドコード knip 未使用export・ファイル・依存の特定

「止める」と「知らせる」の境界線:信頼されるCIの構築

CIパイプラインを構築する際、すべての検査を「失敗させる(止める)」設定にすることが必ずしも正解とは限りません。信頼性の低い検査でCIを止めれば、開発者は「とりあえず通したい」という動機から、// eslint-disableのような黙らせる注釈を乱発し、検査そのものが形骸化します。真に信頼できる検査のみをゲートとして機能させ、それ以外は「人間が判断するための材料」として提供する。この「止める」と「知らせる」の使い分けこそが、チームの生産性を維持する秘訣です。

例えば、jscpdによる重複検知は、全体率で閾値を設けても新規の重複を止めることはできません。そのため、SARIF形式で結果を出力し、GitHubのCode Scanningに統合することで、「今回のPRで新しく増えた重複」だけを差分として可視化するアプローチが有効です。同様に、knipによるデッドコード検知も、現状では「今ある全部」を報告するため、CIを止めるのではなく、PRの要約欄に警告を出す運用に留めるのが現実的です。CIはあくまで「書いた後の検問所」であり、書く前の予防策として「共有ヘルパーのカタログ」を整備するような人間側の運用ルールと組み合わせることで、初めてシステムは完成します。

最後に、我々エンジニアに突きつけられた問いを投げかけます。AIがコードを生成する速度が人間のタイピング速度を遥かに凌駕する今、我々の価値は「コードを書くこと」から「コードの品質を定義し、それを守る仕組みを設計すること」へと完全にシフトしました。あなたは、AIが吐き出すコードの海に溺れるのをただ眺めているのか、それとも、AIを規律ある開発者へと調教するための「CIという名の檻」を設計し続けるのか。明日から、あなたのプロジェクトのCI設定を見直し、AIが生成するコードの「質」を機械的に定義し直すことから始めてみてはどうでしょうか。その一歩が、数年後のコードベースの寿命を決定づけるのです。

Published at 15:00

コメント

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