大規模なシステム開発において、ディレクトリ構造の変更やライブラリのメジャーバージョンアップは、避けて通れない「鬼門」と言えます。
「もっときれいに整理したい」
「古いコードが抱える技術的負債を解消したい」
そう願ってコードの整理(リファクタリング)に着手した瞬間、画面を埋め尽くす赤文字のエラーログ。数百、数千件に及ぶ Module not found(モジュールが見つかりません)や Cannot find name(名前が見つかりません)の群れ。
これを見た瞬間に「やっぱりやめようか」と心が折れかけた経験は、多くのエンジニアが共有するものではないでしょうか。
今回は、実務の現場でしばしば直面する「5万行規模の古いコードの移行」という難題と、それを解決するためのAIによるエラー一括検知・修正ワークフローについて、論理的かつ実践的な視点から解説します。
「AIにコードを書かせる」という話はよく耳にしますが、今回は「AIに壊れたコードを直させる」、それも人間の手では追いきれない規模の依存関係(ファイル同士の繋がり)の解決に焦点を当てます。普段使っているエディタの機能や従来のプログラムではなぜ対応しきれないのか、そしてAIをどのようにシステムに組み込めば実用的なのか。その裏側を、技術的な視点から分かりやすく掘り下げていきます。
1. プロジェクト背景:5万行のレガシーコード移行という「泥沼」
一般的な事例として、5年以上運用されているシステムで、見た目を作るフロントエンド部分だけで約5万行に達するケースを考えてみましょう。技術スタックは古いバージョンのフレームワーク(Vue.js 2系など)とJavaScriptで書かれており、これを最新の環境(Vue 3系およびTypeScript)へ完全移行するというミッションです。
機能追加を阻む複雑なディレクトリ構造
最大の問題となるのは、長年の継ぎ足し開発によって複雑怪奇になったフォルダ(ディレクトリ)構造です。
特定のフォルダの下に数百のファイルが平坦に並んでいたり、便利ツールをまとめたフォルダの役割分担が曖昧だったりと、新しいメンバーが「どこに何があるか」を把握するだけで1ヶ月かかってしまうような状態です。
これを、機能や目的ごとに整理された構造(ドメイン駆動設計ライクな構造)へ再配置するとします。つまり、ほぼすべてのファイルを別の場所へ移動させるということです。
手動修正における「3つの壁」
ファイルを移動すれば、当然ながら他のファイルを読み込むための import パス(ファイルの場所を示す道筋)はすべて壊れてしまいます。
開発現場ではしばしば「エディタの自動修正機能を使えばなんとかなるだろう」と楽観視されがちですが、現実はそう甘くありません。
- 動的な読み込みの崩壊: プログラムの実行時にファイルを読み込む仕組み(動的インポート)は、エディタの追跡機能が効かないケースが多発します。
- 設定ファイルのパス切れ: システムの設定ファイルに書かれたパスは、単なる文字列として扱われるため自動修正されません。
- 循環参照の発生: 構造を変えたことで、「ファイルAがファイルBを呼び出し、ファイルBがファイルAを呼び出す」という堂々巡り(循環参照)が新たに生まれ、システムが動かなくなります。
一般的な試算では、これらを手作業で修正し動作確認をするだけで、エンジニア2名がかりで丸2週間(約160時間)ほどかかるケースも珍しくありません。新しい機能の開発を止めてこれだけの時間を割くことは、ビジネスの観点から現実的ではないでしょう。
ここで、一つの仮説が浮かび上がります。
「この単調かつ複雑なパズル解きこそ、生成AIに任せるべきではないか?」
2. 課題の深掘り:なぜ既存のツールだけでは不十分なのか
AIを使う前に、なぜ既存のツールでは解決できないのかを理論的に整理しておく必要があります。ここを理解していないと、AIへの的確な指示出し(プロンプトエンジニアリング)も行えないからです。
エディタの解析機能(LSP)の限界点
現代の開発環境は、LSP(Language Server Protocol)という技術によって支えられています。エディタが「定義へ移動」や「変数の名前変更」をできるのは、このLSPがコードの構造を裏側で解析しているからです。
しかし、LSPは基本的に「静的解析」の世界で動いています。つまり、コードが文法的に正しく読み取れる状態であることが前提です。
大規模なファイル移動を行って一時的にコードが壊れている状態や、型の定義が曖昧な状態では、LSPは「解析不能」として機能しなくなってしまいます。
また、LSPは「プロジェクト全体の文脈」を理解しているわけではありません。「この部品は本来、在庫管理のグループに属するはずだから、パスはこうなるべきだ」といった意味を汲み取った推論はできないのです。
「文脈」を理解しない一括置換のリスク
では、単純な文字列の一括置換はどうでしょうか?
これも非常にリスクの高い行為です。
例えば、User という部品が複数の場所に存在する場合、単純な文字列の置き換えでは、誤った方のファイルを読み込んでしまう危険性があります。
ここに必要なのは、「エラーの内容を読み解き、プロジェクト全体の構造という文脈を理解した上で、最も確からしいパスを推論する」能力です。これこそが、大規模言語モデル(LLM)が得意とする領域なのです。
3. 解決策の比較検討:自動化のアプローチを選定する
AIを活用するにしても、いくつかのアプローチが考えられます。プロジェクトの規模と特性を考慮し、一般的に以下の3つの案が比較検討の対象となります。
案1:コードを構造化して書き換える専用プログラムの作成
コードをツリー構造(AST:抽象構文木)に変換し、プログラムで直接操作する方法です。
- メリット: 動作が高速で、変換のルールが確定していれば確実性が高いです。
- デメリット: プログラムを書く難易度が非常に高いです。特異な書き方が混在している場合、あらゆる例外パターンに対応するルールを作るのに膨大な時間がかかります。
案2:AIコーディングアシスタントの対話・エージェント利用
GitHub CopilotやChatGPTなどのAIアシスタントを活用する方法です。現在はエディタ内でプロジェクト全体を参照する機能や、タスクを自律的に実行する機能が強化されています。
- メリット: 環境構築が不要で、すぐに利用を開始できる点が魅力です。用途に合わせて特性の異なる最新のAIモデルを切り替えることも可能です。
- デメリット: 数千件規模のエラー修正を処理するには、依然として人間が確認・承認する手間がボトルネックになります。大量のファイルを一括で処理する際の制限もあり、完全な自動化には不向きです。
案3:自律型AIエージェントによる一括処理ワークフロー
エラーの記録(ログ)を読み込ませ、AIに修正案を作成させ、自動で適用する独自の仕組み(ワークフロー)を構築する方法です。
- メリット: 人間が寝ている間にも処理が進みます。大量のエラーを一括で処理でき、プロジェクト固有のルールを厳密に適用可能です。
- デメリット: システム構築の初期コストがかかります。AIが誤った修正をするリスク(ハルシネーション)があるため、検証の仕組みが必要です。
大規模なコード整理においては、「案3」のアプローチが推奨されます。
5万行という規模と、今後も同様の作業が発生する可能性を考えると、手作業を排除して仕組み化する価値が高いためです。また、曖昧な推論が必要な場面が多く、標準機能だけでは制御しきれない大量処理を独自の制御下で実行する場合、この選択が最適解となります。
4. 実装詳細:エラー検知から修正までを自律化するワークフロー
ここでは、プログラムを用いてコンパイラ(コードを変換・チェックするツール)と生成AIを連携させる、具体的な実装アプローチを解説します。エラーの検知から修正、検証までを自動化するこのプロセスは、近年注目される「エージェント型ワークフロー」の基礎となるものです。
Step 1:エラー情報の構造化と周辺情報の抽出
まず、コンパイラを実行し、その出力を受け取ります。通常のエラーログは人間用に見やすく整形されていますが、機械処理してAIに渡すには不向きです。
そこで、ログを解析して扱いやすいデータ形式(JSON形式)に変換します。
{
"filePath": "src/domains/inventory/StockList.vue",
"line": 24,
"errorCode": "TS2307",
"errorMessage": "Cannot find module '@/components/Button' or its corresponding type declarations.",
"context": "import Button from '@/components/Button';"
}
ここで重要なのが、エラーが発生している行だけでなく、そのファイルの読み込み部分全体も周辺情報(コンテキスト)として抽出することです。最新のAIは扱える情報量が大幅に拡大しているため、他の読み込み文や周辺コードを含めることで、「このファイルがどのフォルダ構造にある部品を参照しようとしているか」という傾向をより正確に掴めるようになります。
Step 2:AIへの的確な指示出しと推論
次に、構造化したエラー情報をAIに送信します。ここで工夫すべきは、プロジェクト固有の情報を補うアプローチと、最新モデルの特性を活かした指示(プロンプト)の設計です。
単純にエラーを投げるだけでは、AIはプロジェクト固有の「正しいパス」を知り得ません。そこで、プロジェクト内の全ファイルパスをリスト化したテキストを事前に作成し、前提条件として動的に注入します。
また、複雑な依存関係の解決には、推論能力に特化した最新モデルの活用が推奨されます。これらのモデルは回答を出力する前に内部で思考プロセスを経るため、論理的な整合性が求められるパス解決において高いパフォーマンスを発揮します。
プロンプト構成のベストプラクティス(概念図):
Role (役割):
あなたはコード整理の専門家であり、依存関係解決のエキスパートです。Context (前提情報):
- 対象ファイル:
src/domains/inventory/StockList.vue- エラー内容:
'@/components/Button'が見つかりません- 参照可能なファイル一覧(抜粋):
src/shared/ui/Button/index.vuesrc/shared/ui/Card.vue
...(以下略)Task (指示):
以下の手順で思考し、修正案を提示してください。
- エラーの原因となっているimportパスを特定する。
- 提供されたファイル一覧から、最も適切と思われる正しいパスを探索する。
- 相対パスではなく、エイリアス(@/)を用いた最適なパスを構築する。
Output (出力形式):
解説は不要です。修正後のimport文のみを含むJSON形式で出力してください。
このように「正解の候補リスト」を与えつつ、思考のステップを踏ませることで、AIの推論精度は劇的に向上します。
Step 3:修正の適用と自動検証のループ
AIから返ってきた修正案を、プログラムが該当ファイルに書き込みます。
しかし、ここで終わりではありません。自律型ワークフローの核心は、修正後に再度コンパイラを走らせ、エラーが消えたかどうかを検証するフィードバックループにあります。
- 修正適用: AIの提案通りにコードを書き換える。
- 再検証: コンパイラを実行し、該当のエラーが消滅したか確認する。
- 自己修正: もしエラーが消えていない、あるいは別のエラーが発生した場合、その結果を再びAIに伝えます。「前回の修正では解決しませんでした。次はこのエラーが出ました。どう修正しますか?」と問いかける反復的なループを回すのです。
これを最大3回程度繰り返しても直らない場合は、人間の確認が必要なリストに記録する設計にします。これにより、AIが解決できる部分は自動化し、難易度の高い問題だけをエンジニアが確認するという効率的な分担が可能になります。
5. 直面する困難とAIの「幻覚」対策
理論上は完璧に見えるこのフローですが、実際の運用環境ではいくつかの壁にぶつかることがよくあります。特に厄介なのが、AIの「ハルシネーション(幻覚)」です。
存在しない部品を捏造するハルシネーション
AIは時々、非常に自信満々に嘘をつきます。
例えば、ある部品を読み込むように修正案を出してきたものの、実際には指定されたファイルにその部品が存在しない、といったケースです。
これに対処するためには、AIが修正案を出した直後に、簡単なチェックを挟むアプローチが有効です。
「修正案で指定されたファイルが実在するか?」「そのファイルの中に指定された部品名が含まれているか?」を簡易的に確認し、怪しい場合は適用をブロックする仕組みを構築します。
循環参照を悪化させるケースと対策
もう一つの問題は、循環参照です。
AIは局所的なエラー解消には強いですが、全体構造を見通すのは苦手です。「Aのエラーを消すためにBを読み込み」、その結果「BでAが必要になる」というデッドロックを作ってしまうケースが見受けられます。
これについては、循環参照を検知する専用のツールをシステムに組み込むことが推奨されます。修正適用の前後にチェックを行い、もし新たな循環が発生したら元の状態に戻す(ロールバックする)安全装置を設けることで、システムを保護できます。
6. 成果と投資対効果:2週間の作業を3日に短縮
適切にこのシステムを稼働させた場合、実証データとして驚くべき成果が得られることがあります。
定量的成果:工数85%削減の内訳
ある導入事例のデータでは、以下のような結果が報告されています。
- 総エラー数: 2,450件
- AIによる自動修正: 2,180件(カバー率 約89%)
- 所要期間: 3日(スクリプト作成1日 + 実行・検証2日)
当初2週間(10営業日)と見込まれていた作業が、実質3日で完了し、工数にして約85%の削減を達成したケースです。
残りの10%強のエラーは、論理的な変更が必要な複雑なものでしたが、単純作業が自動化されたことで、エンジニアはこれら「本質的な修正」に集中できるようになります。
定性的成果:開発チームの心理的負担軽減
数値以上の成果として挙げられるのは、チームの士気に関わる部分です。
「朝起きたら、昨日までのエラーが1000件減っている」という体験は、開発チームに大きな安心感を与えます。
終わりが見えない単純作業はエンジニアの精神的な負担となりますが、AIがそれを肩代わりしてくれることで、「コードの整理は怖くない」というポジティブな空気が生まれます。これは長期的な開発文化にとって、非常に大きな資産となるでしょう。
7. 結論:AIによる「守りの開発」自動化のすすめ
これまでの解説から明らかなように、生成AIは「新しいコードを書く」だけでなく、「既存のコードを保守・修正する」場面でこそ、その真価を発揮する可能性があります。特に、人間が手作業で行うには精神的負荷が高すぎる「大量の単純修正」や「複雑な依存関係の解決」において、AIは強力な武器となります。
このワークフローが適しているプロジェクトの条件
すべてのプロジェクトで大規模な自動化が必要なわけではありません。以下の条件に当てはまる場合、導入を検討する価値があります。
- コード規模: 1万行以上で、手動修正に数日以上かかると見込まれる場合。
- 型定義: 静的型付け言語(TypeScriptなど)を使用している(コンパイラが正確なエラーを出してくれるため、AIの修正精度が向上します)。
- 課題: 大規模なフォルダ構成の変更や、ライブラリの破壊的変更への追従が必要な場合。
明日から始めるためのチェックリスト
もし同様の課題を抱えているなら、まずは小さな一歩から始めてみてください。最新のAIモデルは推論能力が向上しており、以前よりも複雑な文脈を理解できるようになっています。
- エラーログの構造化: 現在のエラーログをテキストファイルに出力し、パターンを分析する。
- プロンプトの検証: エラーの1つを最新のAIモデルに入力し、解決策の精度を確認する。特に「思考プロセス」を含めることで精度が上がることがあります。
- 前提情報の整備: プロジェクトのファイル構成図や関連コードをAIに効率的に渡す方法を整理する。
AIを活用した開発プロセスは、日々進化しています。最新のモデルやツールでは、単なるコード生成だけでなく、プロジェクト全体の文脈を理解した上での修正提案や、シームレスな統合も現実的になりつつあります。
確実に言えるのは、「面倒な作業」をAIに任せる技術を持ったチームは、より創造的な課題解決に時間を使えるようになり、競争力を増していくということです。ぜひ、「守りの開発」におけるAI活用を検討し、効率的な解決策を追求するための仕組み作りを始めてみてください。
コメント