アンチパターン⑤「スパゲッティ大量生産」
AIの「お気に入り」
AIには、コード生成における「お気に入りのパターン」があります。
「AIはモノリシックな関数を書くのが大好きだ。1つの300行の関数で、APIからデータを取得し、3種類の変換を行い、エラーを処理し、データベースを更新し、メールを送信する。まさに『手続き型スパゲッティの傑作』だ」
このような「モンスター関数」が、AIによって大量生産されます。
【統計データ】AI開発の現実
CodeRabbitの2025年調査は、AI生成コードの品質を客観的に測定しました。
AI生成 vs 人間作成:
| 指標 | AI生成 | 人間作成 | 倍率 |
|---|---|---|---|
| PR当たりの問題数 | 10.83 | 6.45 | 1.68x |
| ロジックエラー | - | - | 1.75x |
| 品質・保守性問題 | - | - | 1.64x |
| 可読性問題 | - | - | 3.0x以上 |
| コード重複 | - | - | 4.0x |
特に、可読性の問題は3倍以上、コード重複は4倍 です。
これは、スパゲッティコードの大量生産を数字で示しています。
AI生成モンスター関数の例
AIが生成しがちなモンスター関数の例を見てみましょう。
async function processUserOrder(userId, orderData, paymentInfo, shippingDetails) {
// APIからデータをフェッチ(20行)
const user = await fetch(`/api/users/${userId}`);
const userData = await user.json();
// バリデーション(30行)
if (!userData) throw new Error('User not found');
if (!orderData.items) throw new Error('No items');
// ... 他のバリデーション
// データ変換1(50行)
const transformedOrder = {
...orderData,
userId: userData.id,
// ... 大量のマッピング
};
// データ変換2(50行)
const shippingData = {
// ... また大量のマッピング
};
// 決済処理(40行)
const paymentResult = await processPayment(paymentInfo);
if (!paymentResult.success) {
// エラーハンドリング
}
// データベース更新(30行)
await db.orders.create(transformedOrder);
await db.inventory.update(/* ... */);
await db.users.updateLastOrder(/* ... */);
// メール送信(20行)
await sendEmail(userData.email, /* ... */);
// ログ記録(20行)
await logActivity(/* ... */);
// Webhook通知(20行)
await notifyWebhooks(/* ... */);
return result;
}
この関数は 300行以上 で、10以上の責務 を持っています。
なぜモンスター関数が生まれるか
AIがモンスター関数を生成する理由があります。
理由1: トレーニングデータの影響
- Stack Overflowの回答は「動く」ことが優先
- チュートリアルは簡潔さを優先
- 分割より一括の方が「答え」として分かりやすい
理由2: プロンプトの影響
- 「〇〇機能を作って」と依頼すると、一括で作ろうとする
- 分割を明示的に指示しないと、分割しない
理由3: コンテキストの制約
- 複数ファイルにまたがる設計は困難
- 1つの関数で完結させる方がAIにとって「簡単」
AI生成スパゲッティコードの問題
モンスター関数には、多くの問題があります。
問題1: 単一責任の原則違反
1つの関数が10以上の責務を持つ。変更の理由が10以上ある。
問題2: テスト困難
各責務を個別にテストできない。モック地獄に陥る。
問題3: 再利用不可
「決済処理」だけを別の場所で使いたくても使えない。
問題4: デバッグ困難
300行のどこで問題が起きたか特定困難。
問題5: 変更波及
1箇所を変更すると、他の箇所に影響する可能性。
AI生成コードでDRY原則が崩壊する理由
GitClearの2025年分析は、衝撃的な統計を示しました。
2億1100万行のコード分析:
| 指標 | 傾向 |
|---|---|
| コードチャーン(短期変更) | 増加 |
| コードクローン(重複) | 約4倍増 |
| 移動/再利用コード | 過去最低 |
| DRY原則の遵守 | 崩壊中 |
AIは「再利用」が苦手です。似たような機能を依頼すると、既存のコードを使わず、新しく生成します。
結果として、同じようなコードがあちこちに散らばる ことになります。
スパゲッティを防ぐプロンプト
スパゲッティを防ぐためのプロンプト戦略があります。
戦略1: 責務を明示
以下の責務を持つ関数を、それぞれ別々に作成してください:
1. ユーザー情報の取得
2. 注文データのバリデーション
3. 決済処理
4. 注文の保存
5. 通知の送信
戦略2: 関数の行数を制限
各関数は30行以下にしてください。
30行を超える場合は、小さな関数に分割してください。
戦略3: 既存コードの再利用を指示
既存のvalidateUser関数を使用してください。
新しく作成しないでください。
リファクタリングの習慣
AIが生成したコードは、そのまま使わず、リファクタリング しましょう。
リファクタリングのチェックリスト:
-
関数の行数は30行以下か?
- 超えていれば分割
-
関数の責務は1つか?
- 複数あれば分割
-
重複したコードはないか?
- あれば共通化
-
適切な抽象化レベルか?
- 詳細と概要が混在していないか
-
命名は適切か?
- 関数名、変数名を確認
AI開発で意識すべき「良い」コードの特徴
AI生成コードを「良い」コードにするために、目指すべき特徴があります。
良いコードの特徴:
-
小さな関数
- 1つの関数は1つのことだけ
-
明確な命名
- 関数名を読めば何をするか分かる
-
低い結合度
- 関数間の依存を最小限に
-
高い凝集度
- 関連する処理をまとめる
-
テスト可能
- 各関数を独立してテストできる
この事例から学ぶべき教訓
「スパゲッティ大量生産」から学ぶべきことは以下の通りです。
-
AIはモンスター関数を生成しがち
- 300行、10責務の関数
-
可読性問題は3倍、重複は4倍
- CodeRabbit調査
-
DRY原則が崩壊している
- AIは再利用が苦手
-
プロンプトで分割を指示
- 責務を明示、行数を制限
-
リファクタリングを習慣化
- AIの出力をそのまま使わない
まとめ:重要ポイントの振り返り
- AIは モンスター関数 を生成しがち(300行、10責務)
- 可読性問題は 3倍以上 、コード重複は 4倍
- DRY原則が崩壊 、同じコードが散らばる
- プロンプトで 責務を明示 、行数を制限
- AI出力は リファクタリング してから使う
- 教訓:AIの出力をそのまま使うな、整形せよ