はじめに:そのコード、本当に「時短」になっていますか?
「GitHub CopilotやChatGPTを導入して、開発スピードが倍になった!」
そんな歓喜の声が聞こえてきたのも束の間、最近では開発現場から少し違った種類の溜息が漏れ聞こえるようになってきました。
「AIが書いたコード、動くんだけど…レビューに倍の時間がかかるんだよね」
「修正しようとしたら、どこがどう繋がっているのか複雑すぎて手が出せない」
AIは確かに魔法のようなツールです。しかし、魔法には対価が必要です。何も考えずに生成されたコードは、一見正しく動作していても、裏側では「技術的負債」という名の利子を静かに、しかし確実に積み上げています。
今回は、なぜAIが書くコードは「スパゲッティ化」しやすいのか、そしてAIという強力なパートナーを真に使いこなし、保守性の高い堅牢なコードを生み出すためにはどのような「設計思考」が必要なのか。デザインパターンの適用とリファクタリングの観点から、論理的かつ平易に解説していきます。
なぜAI生成コードは「スパゲッティ化」しやすいのか
まず、根本的な原因から目を背けずに分析してみましょう。AIツールは膨大なコードデータを学習しており、文法的な間違いや一般的なアルゴリズムの実装においては、人間よりも遥かに正確で高速です。それなのになぜ、全体として見ると「読みづらい」「直しにくい」コードになってしまうのでしょうか。
「動くコード」と「保守できるコード」の決定的な違い
AIにとってのゴールは、プロンプトで指示された要件を満たす「動くコード」を出力することです。一方で、長期的に必要とされているのは、将来の変更に耐えうる「保守できるコード」です。
この二つの間には、大きな隔たりがあります。
例えば、ある機能を追加する際、AIは既存のコードの末尾に新しい処理を継ぎ足すことを好みます。なぜなら、それが最も変更範囲が少なく、かつ論理的に破綻しにくい(エラーが出にくい)「局所最適解」だからです。しかし、これを繰り返すとどうなるでしょうか。一つの関数が数百行に膨れ上がり、条件分岐が幾重にもネストされた、立派なスパゲッティコードの完成です。
AIコーディングにおける「コンテキスト欠如」の課題
AIが構造的な脆さを生む最大の理由は、「コンテキスト(文脈)の理解不足」にあります。
ここでの「コンテキスト」とは、単に前後のコード行のことではありません。プロジェクト全体のアーキテクチャ思想や、そのコードが将来どう拡張される可能性があるかといった「未来の文脈」のことです。
技術的に言えば、LLM(大規模言語モデル)は確率論的に「次に来るもっともらしいトークン(単語)」を予測してコードを生成しています。「このクラス設計が、ドメイン駆動設計(DDD)の原則に反していないか?」と自問自答しながら書いているわけではありません。
人間であれば、「ここは将来的に決済手段が増えるから、今のうちにインターフェースを切っておこう」と判断できます。しかし、AIにその指示を与えなければ、AIは「現在求められている決済処理」を直接書き込むだけです。
つまり、AIは「全体最適」を考慮せず、「局所最適」を積み重ねる傾向があるのです。これが、AI生成コードが技術的負債を加速させるメカニズムの正体です。
では、AIに「良い設計」をさせるにはどうすれば良いのでしょうか? ここにありがちな誤解がいくつかあります。
誤解①:「ChatGPTは最適なデザインパターンを自動で選んでくれる」
「でも、ChatGPTの最新モデルに『いい感じに設計して』って頼めば、最適なデザインパターンを使ってくれるんじゃないの?」
そう期待する方も多いかもしれません。確かにAIモデルの推論能力は飛躍的に向上していますが、ここには依然として大きな誤解があります。AIはデザインパターンの「知識」は膨大に持っていますが、それを適用すべき「ビジネス上の文脈」や「将来の拡張計画」までは、人間が伝えない限り理解できません。
AIは「パターン」を知っているが「適用すべき場面」を知らない
GoF(Gang of Four)のデザインパターンなどは、特定の課題に対する解決策の型です。しかし、どの型を使うべきかは、解決したい課題の本質やプロジェクトの制約に依存します。
具体的な例を見てみましょう。ECサイトの商品割引計算ロジックを実装するとします。「会員ランク」や「季節セール」など条件が複雑です。
AIに単に「割引計算のコードを書いて」と頼むと、多くの場合、巨大な if-else 文や switch 文の塊を出力します。
// AIが生成しがちなコード例(概念)
function calculateDiscount(user, item) {
if (user.isPremium) {
if (item.category === 'electronics') {
return item.price * 0.9;
} else if (item.isSeasonal) {
// ...さらにネストが続く
}
}
// ...延々と続く条件分岐
}
これは「動く」コードですが、将来新しい割引ルールが増えたら修正が大変です。ここで人間が「条件分岐が増えるから、Strategyパターンを適用して、割引ルールごとにクラスを分けて」と指示して初めて、AIはその強力な実装能力を発揮し、拡張性の高いコードを生成します。
指示なき生成から「共創」へ:最新機能の活用アプローチ
プロンプトで明示的な設計指示がない場合、AIは最も単純な実装、つまり「手続き型」のアプローチを選びがちです。しかし、この課題を解決するための新しいアプローチが登場しています。単なる「指示出し」から、AIをパートナーとした「共創」へのシフトです。
1. Canvas(共同編集UI)での対話的リファクタリング
最新のChatGPTには、ドキュメントやコードを並べて編集できるCanvas機能が搭載されています。これを利用すると、生成されたコードの特定部分をハイライトして、「この条件分岐部分は可読性が低いので、デザインパターンを用いてリファクタリングして」と具体的に指示を出すことが可能です。チャットの流れに埋もれず、コードを見ながら設計を練り上げるプロセスは、まさにペアプログラミングに近い体験と言えます。
2. 推論強化モデル(Thinkingモデル)の活用
また、推論に特化した最新モデル(Thinkingモデルなど)を活用することも有効です。実装を依頼する前に、「この要件に対して、保守性を高めるための設計案を3つ提示して」と問いかけてみてください。AIは論理的な推論を行い、「Strategyパターンの適用」や「Factoryパターンの導入」といった選択肢を提示してくれるでしょう。
AIは依然として「指示待ち」の側面を持ちますが、適切なツールとモデルを選び、こちらから設計の意図(コンテキスト)を明確に伝えることで、スパゲッティコードを回避し、堅牢なアーキテクチャを導き出すことができます。
誤解②:「GoFパターンを適用させればコード品質は無条件に上がる」
では、逆に「とにかくデザインパターンを使え」と指示すれば良いのでしょうか? これもまた、危険な落とし穴です。
AIによる「過剰設計(Over Engineering)」の罠
実務の現場において、「将来の拡張性を持たせるためにFactoryパターンを使ってログ出力機能を実装して」とAIに指示するケースを想定してみましょう。シンプルな関数が1つか2つあれば十分な場面であっても、AIは以下のような構成を出力することがあります。
ILoggerインターフェースFileLoggerクラスDatabaseLoggerクラスLoggerFactoryクラスLoggerConfigurationクラス
単純なテキストログを出したいだけなのに、5つものファイルが生成され、コード量が3倍に膨れ上がってしまうケースです。AIは指示に忠実すぎるあまり、必要以上の複雑さを持ち込むことがあります。いわゆる「過剰設計(Over Engineering)」です。
YAGNI原則とAIの提案力のバランス
ソフトウェア開発には「YAGNI(You Ain't Gonna Need It:それが必要になるまで実装するな)」という重要な原則があります。
AIはこの原則を文脈に合わせて判断できません。求められれば、宇宙船の制御システムのような複雑なアーキテクチャでも喜んで提案してきます。ここで重要になるのが、人間の「分析的な視点」です。
「そのパターンは本当に必要か?」「もっとシンプルに書けないか?」
AIの提案を鵜呑みにせず、プロジェクトの規模やフェーズに適した設計を選択する判断力こそが、求められているスキルなのです。
誤解③:「コードを貼り付ければリファクタリングは完了する」
「汚いコードをChatGPTに貼り付けて『リファクタリングして』と言えば、綺麗になる」
これは半分正解で、半分間違いです。変数名の変更やインデントの修正といった「整形」は行われますが、根本的な構造改善までは至らないことがほとんどです。
なぜAI任せでは「スパゲッティコード」になるのか
AI生成コードが構造的に破綻しやすい主な原因は、プロンプトにおける「構造的欠落」とAIの「文脈理解の限界」にあります。
AIは、そのコードの背後にある「暗黙知(過去の経緯や将来の拡張計画)」や「チーム固有の判断基準」を知りません。そのため、単に「修正して」と投げかけるだけでは、既存のアーキテクチャを無視した継ぎ接ぎのようなコードが生成されやすくなります。
特に、修正を繰り返すことで初期の設計思想から徐々に逸脱していく「ドリフト現象」には注意が必要です。コードが複雑化し、いわゆる「スパゲッティコード」になってしまうのは、AIの能力不足というよりは、指示の出し方に構造的な欠陥があるケースが珍しくありません。
正しいアプローチ:事前設計とContext Engineering
AIを単なる「コード生成機」ではなく、「設計を実装に落とし込むパートナー」として扱う必要があります。現在の開発現場における一般的なベストプラクティスでは、以下のステップで対話を進めることが推奨されています。
事前構造化(設計の明示):
いきなりコードを書かせるのではなく、まずはクラス図やシーケンス図、あるいは適用したいデザインパターン(FactoryやObserverなど)を定義します。「このパターンで実装して」と構造を指定することで、AIの出力ブレを防ぎます。Context Engineering(文脈の注入):
プロンプトには、コードだけでなくアーキテクチャの思想や制約事項を体系的に記述します。以前から有効なSOLID原則に基づいた具体的な指示出しは、Context Engineeringの一環として非常に重要です。- 単一責任の原則(SRP)の視点:
「この関数はデータの取得と表示が混在しています。取得ロジックを別関数に切り出してください」 - オープン・クローズドの原則(OCP)の視点:
「条件分岐が増えるのを防ぐため、決済方法ごとにクラスを分けるStrategyパターンを適用してください」 - 依存性逆転の原則(DIP)の視点:
「直接インスタンス化せず、コンストラクタでインターフェースを受け取るDependency Injectionに変更してください」
- 単一責任の原則(SRP)の視点:
人間主導のサイクル:
AIが生成したコードは「80点の完成度」と捉えるのが安全です。そこから人間がアーキテクチャとの整合性を確認し、プロトタイピングとテストを繰り返すようにリファクタリングを行うことで100点に近づけます。
このように、人間が「設計と視点」を提供し、AIが「実装」を行う。この役割分担こそが、持続可能な高品質コードを生み出す鍵となります。
AI時代に求められる「新たなアーキテクト像」
AIツールの進化により、人間が「手でコードを書く」時間は確実に減っています。しかし、それはエンジニアの仕事がなくなることを意味しません。むしろ、役割はより高度な領域へとシフトしています。
「コードを書く」から「構造を定義する」へ
これからのエンジニアは、現場監督やアーキテクトに近い存在になります。詳細なレンガ積み(コーディング)はAIという優秀な職人に任せ、人間は「どのような建物を建てるか(構造設計)」、「どこに窓を配置すれば快適か(インターフェース設計)」に注力するのです。
UI/UXデザインの世界でも、見た目の装飾よりもユーザーの目的を達成するための「情報設計」が重要であるのと同様に、プログラミングにおいても「コード設計」の重要性が相対的に高まっています。
AIを優秀なジュニアエンジニアとして扱う設計指示力
AIを「何でも知っている先生」ではなく、「指示待ちだが手は早くて正確な優秀なジュニアエンジニア」だと思って接してみてください。
「この機能を作って」と丸投げするのではなく、「このデザインパターンを使って、こういう責務分割で、この機能を作って」と具体的に指示できるか。この「設計意図の言語化能力」が、AI時代のエンジニアの価値を決定づける要因の一つになるでしょう。
まとめ:AIを使いこなす「設計力」を磨くために
AIによる自動コーディングは、開発の風景を一変させました。しかし、スピードの裏側で「読みづらいコード」が増殖している事実も見逃せません。
- AIは局所最適に走りがちで、全体構造を見通せない。
- デザインパターンの選択と適用の判断は、人間が行う必要がある。
- 一発変換ではなく、原則に基づいた対話型リファクタリングが有効。
結局のところ、ツールの性能を引き出すのは使い手の力量です。AI時代だからこそ、問題の本質を見抜くための「設計スキル」や「デザインパターン」の知識が、これまで以上に強力な武器となります。
コメント