Post
JA EN

AIペアプログラミングが生む密結合の罠:結合バランスで読み解く技術的負債

AIペアプログラミングが生む密結合の罠:結合バランスで読み解く技術的負債
  • 想定読者: ソフトウェアエンジニア、アーキテクト、AIツール(GitHub Copilot、Cursor、Claude等)を業務で使用する開発者
  • 前提知識: オブジェクト指向プログラミング、ソフトウェア設計の基礎概念
  • 所要時間: 20分

概要

GitHub Copilot、Cursor、ChatGPTなどのAIペアプログラミングツールは、開発速度を劇的に向上させる一方で、予期せぬ技術的負債を蓄積しています。GitClearが2025年に発表した研究では、211百万行のコード変更を分析した結果、AI導入後に重複コードが4倍に増加し、リファクタリングが60%減少したことが明らかになりました1

本記事では、Vlad Khononovの『ソフトウェア設計の結合バランス』2の理論的枠組みを用いて、AIが生成するコードの「密結合」問題を分析します。さらに、Cynefin理論を活用してAIに任せるべき領域と人間が判断すべき領域を明確化し、実践的なプロンプト設計技法を提案します。

本記事を読むことで、AI生成コードの品質問題を体系的に理解し、持続可能なAI協働開発の実践方法を習得できます。

注記:

本記事に掲載されているコード例およびプロンプトテンプレートは、説明のための例示であり、実際に実行して動作を検証したものではありません。実際のプロジェクトで使用する際は、適切なテストと検証を行い、プロジェクトの特性に応じてカスタマイズすることを推奨します。

データで見るAI生成コードの現実

GitClear 2025レポート:衝撃的な数値

GitClearは2025年1月、AIアシスタントのコード品質への影響を調査した大規模研究を発表しました1。この研究では、2020年から2024年の5年間、Google、Microsoft、Meta、および複数の大企業が所有するリポジトリから211百万行のコード変更を分析しました。

主要な発見:

  1. 重複コードの急増
    • コピー/ペーストされたコード行の割合が8.3%から12.3%に増加(約50%増)
    • 5行以上の重複コードブロックが4倍に増加
  2. リファクタリングの大幅な減少
    • “refactoring”に分類されるコード変更が2021年の25%から2024年には10%未満に低下(60%減)
    • DRY(Don’t Repeat Yourself)原則の違反が顕著に増加
  3. 開発速度の向上と品質のトレードオフ
    • AIアシスタントを使用する開発者の63%がAIを業務で使用1
    • 一方で、長期的な保守性と再利用性が低下

Google DORA 2024レポート:安定性への影響

Googleの2024年DevOps Research and Assessment(DORA)レポートでは、AI採用と配信安定性の相関関係が報告されています3

主要な発見:

  • AI採用率が25%増加するごとに、配信安定性(delivery stability)が7.2%低下
  • コードレビューの速度は向上するが、欠陥率(defect rate)が増加

この結果は、AIが「速く動くコード」の生成に優れている一方で、長期的な品質指標では課題があることを示唆しています。

学術研究:大規模生成時のパーティション分割の崩壊

arXivに投稿された研究論文4では、AI生成コードの結合度(coupling)と凝集度(cohesion)を分析しました。

主要な発見:

  • 小規模コードスニペット(関数レベル):人間が書いたコードと同等の保守性(高凝集、低結合)
  • 大規模コード生成(アプリケーション全体、大規模モジュール):パーティション分割の品質が著しく低下し、保守性が大幅に悪化
  • AIは大規模で複雑な問題に直面すると、不適切なソリューションを提案する傾向

この研究結果は、AIが「局所的な最適化」には優れているが、「グローバルなアーキテクチャ設計」では限界があることを示しています。

TiMi Studioのケーススタディ

ACM Digital Library に掲載された研究5では、TiMi Studioにおけるゲーム開発チームのAIペアプログラミング導入事例が報告されています。

ポジティブな側面:

  • 循環的複雑度(cyclomatic complexity)の低減
  • コードカバレッジの向上
  • コードスメルの減少

ネガティブな側面:

  • 信頼性に対する疑問(reliability-questioning)
  • 説明可能性の欠如(explainability-questioning)
  • 信頼の欠如(trust-lacking)
  • 自律性の喪失(autonomy-losing)

このケーススタディは、AIペアプログラミングが品質指標を改善する可能性がある一方で、開発者の認知プロセスや意思決定に新たな課題をもたらすことを示しています。

なぜAIは密結合コードを生むのか

1. 「動くコード」優先の設計思想

AI言語モデルは、訓練データに含まれる大量のコードから学習していますが、その学習目標は「構文的に正しく、実行可能なコード」の生成です6。長期的な保守性、拡張性、モジュール性といった抽象的な設計原則は、学習の直接的な目標ではありません。

LeadDevに掲載された記事7では、AIが生成するコードの典型的な問題として以下が挙げられています:

  • 高度に結合されたコード(highly coupled code)
  • God Object(責任が過剰に集中したオブジェクト)
  • 過度に構造化されたソリューション(overly structured solutions)

これらのパターンは、AIが「今すぐ動くコード」を優先し、設計の意図や長期的な影響を考慮しないために発生します。

2. コンテキスト理解の限界

AIは提示されたコンテキスト(プロンプト、周辺コード)内で最適化を行いますが、プロジェクト全体のアーキテクチャ、既存のモジュール構造、チームのコーディング規約といった「暗黙の知識」を理解できません8

この限界により、以下の問題が発生します:

  1. 既存モジュールの無視
    • 類似機能が既に存在する場合でも、新規にコードを生成
    • 結果として、重複コードと不整合な実装が増加
  2. 依存関係の直接参照
    • 抽象化レイヤーや依存性注入(Dependency Injection)を経由せず、具体的な実装に直接依存
    • テスタビリティとモジュール独立性が低下
  3. 境界の不明瞭化
    • ドメイン境界、レイヤー境界を超えた結合を生成
    • Clean ArchitectureやHexagonal Architectureといった設計原則の違反

3. 抽象概念理解の困難さ

Mediumに掲載された27日間のAI実験記事8では、AIエージェントツールが「抽象的な概念、例えば設計原則、ユーザーエクスペリエンス、コード保守性といった概念に苦労する」ことが報告されています。

具体例:

AIに「RESTful APIを実装してください」と依頼すると、以下のような密結合コードが生成されがちです:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# AI生成の典型的なパターン(密結合)
class UserAPI:
    def __init__(self):
        self.db = MySQLDatabase("localhost", "user", "pass", "db")  # 直接依存
        self.logger = FileLogger("/var/log/app.log")  # 直接依存
        self.cache = RedisCache("localhost:6379")  # 直接依存

    def get_user(self, user_id):
        # ビジネスロジック、データアクセス、ログ、キャッシュが混在
        self.logger.log(f"Fetching user {user_id}")
        cached = self.cache.get(f"user:{user_id}")
        if cached:
            return cached
        user = self.db.query(f"SELECT * FROM users WHERE id = {user_id}")
        self.cache.set(f"user:{user_id}", user)
        return user

このコードの問題点:

  • データベース実装(MySQL)に直接依存
  • ロギング実装(FileLogger)に直接依存
  • キャッシュ実装(Redis)に直接依存
  • テスト時に実際のデータベース、ファイルシステム、Redisが必要
  • データベースやキャッシュの切り替えが困難

一方、人間の設計者は抽象化を導入します:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 人間が設計する疎結合バージョン
class UserAPI:
    def __init__(
        self,
        repository: UserRepository,  # 抽象化
        logger: Logger,  # 抽象化
        cache: Cache  # 抽象化
    ):
        self.repository = repository
        self.logger = logger
        self.cache = cache

    def get_user(self, user_id):
        self.logger.log(f"Fetching user {user_id}")
        cached = self.cache.get(f"user:{user_id}")
        if cached:
            return cached
        user = self.repository.find_by_id(user_id)
        self.cache.set(f"user:{user_id}", user)
        return user

4. 局所最適化バイアス

学術研究4が示すように、AIは小規模なコードスニペット(関数レベル)では良好な品質を達成しますが、大規模なコード生成では品質が低下します。

これは、AIが以下のような「局所最適化」を行うためです:

  1. 現在のスコープ内での完結を優先
    • 関数内で必要な処理をすべて実装
    • 他のモジュールや関数への委譲を検討しない
  2. 即座に利用可能な依存関係を選択
    • より適切だが少し遠い抽象化よりも、直接アクセス可能な具体実装を選択
    • 例:インターフェースを定義するより、既存のクラスに直接依存
  3. 短期的な実装容易性を優先
    • 長期的な保守コストよりも、今すぐ動くコードを生成

結合バランスの3次元で診断する

Vlad Khononovは、著書『Balancing Coupling in Software Design』2において、従来の「疎結合至上主義」を超えた新しいアプローチ、結合のバランス化(Balancing Coupling)を提案しています。

結合の3次元

Khononovは、結合(coupling)を3つの次元で評価する枠組みを提示しています29

1. 強度(Strength / Integration Strength)

結合の密度や依存性の程度を表します。

強度のレベル(弱い→強い):

  1. データ結合(Data Coupling)
    • 単純なデータ型(int、string等)を引数として渡す
    • 例:calculate_total(price: float, quantity: int)
  2. スタンプ結合(Stamp Coupling)
    • 構造体やオブジェクトを渡すが、一部のフィールドのみ使用
    • 例:process_order(order: Order)(Orderオブジェクトのidフィールドのみ使用)
  3. 制御結合(Control Coupling)
    • フラグや制御情報を渡して、呼び出し先の振る舞いを制御
    • 例:send_notification(user: User, notification_type: str)
  4. 共通結合(Common Coupling)
    • グローバル変数や共有データ構造への依存
    • 例:複数モジュールが同じグローバル設定オブジェクトを参照
  5. 内容結合(Content Coupling)
    • 他モジュールの内部実装に直接依存
    • 例:user._internal_cache.clear()(プライベートフィールドへの直接アクセス)

AI生成コードの典型的な問題:

  • データ結合やスタンプ結合を使うべき箇所で、共通結合や内容結合を生成
  • グローバル変数への依存が多い
  • プライベートフィールドへの直接アクセス

2. 距離(Distance / Locality)

モジュール間の物理的・論理的な離隔を表します。

距離のレベル(近い→遠い):

  1. 同一クラス内(Within a class)
  2. 同一パッケージ/モジュール内(Within a package/module)
  3. 同一アプリケーション内(Within an application)
  4. 異なるサービス間(Across services)

Khononovの原則9

「強度が高い結合は距離を縮める」

AI生成コードの典型的な問題:

  • 遠い距離にあるモジュール間に強い結合を作成
  • 例:フロントエンドがバックエンドの具体的なデータベーススキーマに依存

3. 変動性(Volatility)

変更可能性と影響範囲を表します。

変動性のレベル(安定→不安定):

  1. 標準ライブラリ(Standard library):変更頻度が極めて低い
  2. サードパーティライブラリ(Third-party library):メジャーバージョン間で安定
  3. 社内共通ライブラリ(Internal shared library):プロジェクト間で共有、定期的に更新
  4. アプリケーション固有のコード(Application-specific code):頻繁に変更

Khononovの原則9

「変動性が低いなら強度の高さを許容し得る」

AI生成コードの典型的な問題:

  • 変動性の高いビジネスロジックに多くのモジュールが強く依存
  • ビジネスルールの変更時に広範囲の修正が必要

Connascence(コナーセンス):結合度のより精緻な尺度

Meilir Page-Jonesが1992年に提唱したConnascence(コナーセンス)10は、結合度を測るより精緻な指標です。Khononovの『結合バランス』でも第6章で詳しく解説されています11

Connascenceの定義:

2つのソフトウェア要素AとBの間にコナーセンスが存在するとは、Aに対する変更がBの変更(または慎重なチェック)を要求する場合、またはAとBの両方を同時に変更する必要がある場合を指す。

Connascenceの3次元11

  1. 強度(Strength):変更の難しさとコスト
  2. 度合い(Degree):結合の数
  3. 距離(Locality):関連要素間の近さ

Connascenceの種類(弱い→強い):

  1. 名前のコナーセンス(Connascence of Name, CoN)
    • 同じ名前を参照する必要がある
    • 例:関数名、変数名
  2. 型のコナーセンス(Connascence of Type, CoT)
    • 同じ型を使用する必要がある
    • 例:関数の引数と戻り値の型
  3. 意味のコナーセンス(Connascence of Meaning, CoM)
    • 特定の値が特定の意味を持つ
    • 例:マジックナンバー(if status == 11が”active”を意味)
  4. 位置のコナーセンス(Connascence of Position, CoP)
    • 要素の順序が重要
    • 例:位置引数 create_user("John", "Doe", 30)
  5. アルゴリズムのコナーセンス(Connascence of Algorithm, CoA)
    • 同じアルゴリズムを使用する必要がある
    • 例:暗号化と復号化

AI生成コードのConnascence診断:

AIが生成するコードは、しばしば強いコナーセンスを含みます:

  • 意味のコナーセンス:マジックナンバー、マジックストリングが多い
  • 位置のコナーセンス:位置引数を多用し、名前付き引数を使わない
  • アルゴリズムのコナーセンス:複数箇所で同じロジックを重複実装

結合バランスモデルの実践

Khononovは、3次元の結合評価に基づいて、以下のような実践的な判断基準を提示しています9

許容される結合:

  • 強度が低く、変動性が低い(例:標準ライブラリへの依存)
  • 強度が高いが、距離が近い(例:同一クラス内のメソッド間依存)

問題のある結合:

  • 強度が高く、距離が遠く、変動性が高い
  • 例:マイクロサービス間での具体的なデータベーススキーマへの依存

AI生成コードのリファクタリング指針:

  1. 意味のコナーセンス → 名前のコナーセンスへ
    1
    2
    3
    4
    5
    6
    7
    
    # Before(意味のコナーセンス)
    if user.status == 1:
        send_email(user)
    
    # After(名前のコナーセンス)
    if user.status == UserStatus.ACTIVE:
        send_email(user)
    
  2. 共通結合 → データ結合へ
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    # Before(共通結合)
    global_config = {...}
    
    def process_data():
        timeout = global_config['timeout']  # グローバル変数への依存
    
    # After(データ結合)
    def process_data(timeout: int):
        # 引数として明示的に渡す
    
  3. 強い結合 × 遠い距離 → 抽象化の導入
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    # Before(サービスAがサービスBの具体実装に依存)
    from service_b.mysql_repository import MySQLUserRepository
    
    class ServiceA:
        def __init__(self):
            self.user_repo = MySQLUserRepository()  # 具体実装への依存
    
    # After(インターフェースへの依存)
    from service_b.interfaces import UserRepository
    
    class ServiceA:
        def __init__(self, user_repo: UserRepository):
            self.user_repo = user_repo  # 抽象化への依存
    

実践:密結合を防ぐプロンプト設計

AI生成コードの密結合問題を軽減するには、プロンプトエンジニアリングが重要です。近年の研究では、モジュラープロンプティング技法の有効性が示されています。

MoT(Modularization of Thought):モジュラープロンプティング

arXivに投稿された研究12では、MoT(Modularization of Thought)という新しいプロンプティング技法が提案されています。

MoTの原則:

  • 複雑なプログラミング問題を小さく、独立した推論ステップに分解
  • より構造化され、解釈可能な問題解決プロセス
  • GPT-4o-miniとDeepSeek-R1を用いた6つのデータセットでの実験で、Pass@1スコアが58.1%〜95.1%を達成12

MoTのメリット:

  • 柔軟性と汎化性の向上
  • エラーの分離
  • 情報検索、算術演算、外部APIの統合が容易

境界とモジュールを明示するプロンプト

研究13では、「境界(role/tone)プロンプト」と「適応制御スキーマ」を分離することの重要性が指摘されています。

推奨されるプロンプト構造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## コンテキスト
[プロジェクトの概要、技術スタック、既存のアーキテクチャ]

## 制約条件
- 依存性注入を使用すること
- 具体的な実装ではなく、インターフェースに依存すること
- 各クラスは単一責任原則(SRP)に従うこと
- テスト可能な設計にすること

## モジュール境界
- データアクセス層:repositoriesパッケージ
- ビジネスロジック層:servicesパッケージ
- API層:controllersパッケージ
- 各層は上位層にのみ依存すること(依存関係逆転の原則)

## タスク
[具体的な実装タスク]

実践例:RESTful API実装

❌ 悪いプロンプト(密結合コードを生成しやすい):

1
Pythonで、ユーザー情報を取得するREST APIを実装してください。

このプロンプトでは、AIは以下のような密結合コードを生成する可能性が高いです:

  • データベース接続をAPIクラスに直接記述
  • ビジネスロジックとデータアクセスが混在
  • テストが困難

✅ 良いプロンプト(疎結合コードを促す):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
## コンテキスト
FastAPIを使用したPythonのREST APIプロジェクトです。
Clean Architectureに従い、レイヤー分離を重視しています。

## アーキテクチャ構造
- domain/: ビジネスロジックとエンティティ(フレームワーク非依存)
- application/: ユースケース(インターフェース定義)
- infrastructure/: 外部依存(DB、API等)
- presentation/: API層(FastAPI)

## 制約条件
- 依存性注入を使用(FastAPIのDependsを活用)
- repositoryパターンを使用し、データアクセスを抽象化
- 各レイヤーは下位層の抽象(インターフェース)にのみ依存
- ビジネスロジックはドメイン層に集約
- すべてのクラスは単一責任原則に従う
- テスト可能な設計(モックやスタブが容易)

## タスク
ユーザー情報を取得するREST APIエンドポイント(GET /users/{user_id})を実装してください。
以下のファイル構成で実装すること:

1. domain/entities/user.py: Userエンティティ
2. application/interfaces/user_repository.py: UserRepository抽象クラス
3. infrastructure/repositories/user_repository_impl.py: UserRepository実装
4. application/services/user_service.py: ユーザー取得ロジック
5. presentation/api/user_controller.py: FastAPIエンドポイント

依存関係図:
Controller → Service → Repository(interface) ← RepositoryImpl

各ファイルの役割を明確にし、レイヤー間の責任分離を徹底してください。

このプロンプトを使用すると、AIは以下のような疎結合コードを生成する可能性が高まります:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# domain/entities/user.py
from dataclasses import dataclass

@dataclass
class User:
    id: int
    name: str
    email: str

# application/interfaces/user_repository.py
from abc import ABC, abstractmethod
from typing import Optional
from domain.entities.user import User

class UserRepository(ABC):
    @abstractmethod
    async def find_by_id(self, user_id: int) -> Optional[User]:
        pass

# application/services/user_service.py
from typing import Optional
from application.interfaces.user_repository import UserRepository
from domain.entities.user import User

class UserService:
    def __init__(self, user_repository: UserRepository):
        self.user_repository = user_repository

    async def get_user(self, user_id: int) -> Optional[User]:
        return await self.user_repository.find_by_id(user_id)

# infrastructure/repositories/user_repository_impl.py
from typing import Optional
from application.interfaces.user_repository import UserRepository
from domain.entities.user import User
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from infrastructure.models.user_model import UserModel

class UserRepositoryImpl(UserRepository):
    def __init__(self, db_session: AsyncSession):
        self.db_session = db_session

    async def find_by_id(self, user_id: int) -> Optional[User]:
        result = await self.db_session.execute(
            select(UserModel).filter(UserModel.id == user_id)
        )
        user_model = result.scalar_one_or_none()
        if user_model is None:
            return None
        return User(
            id=user_model.id,
            name=user_model.name,
            email=user_model.email
        )

# presentation/api/user_controller.py
from fastapi import APIRouter, Depends, HTTPException
from application.services.user_service import UserService
from presentation.dependencies import get_user_service

router = APIRouter()

@router.get("/users/{user_id}")
async def get_user(
    user_id: int,
    user_service: UserService = Depends(get_user_service)
):
    user = await user_service.get_user(user_id)
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return user

プロンプトテンプレートの組織的管理

Thoughtworksの記事14では、AIペアプログラミングにおけるテスト駆動開発(TDD)とペアプログラミングの重要性が指摘されています。同様に、プロンプトテンプレートも組織的に管理することが推奨されます。

推奨プラクティス:

  1. CLAUDE.mdやCursor Rulesの活用
    • プロジェクトルートに設計原則を明記
    • AIが自動的に参照するよう設定
  2. カスタムインストラクションの設定
    • ChatGPTのCustom Instructions
    • GitHub Copilotのworkspace設定
  3. プロンプトライブラリの構築
    • 頻繁に使用するプロンプトをドキュメント化
    • チーム内で共有

CLAUDE.mdの例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# プロジェクト設計原則

## アーキテクチャ
本プロジェクトはClean Architectureに従います。

## 結合度に関する原則
1. 具体実装ではなく、抽象(インターフェース、抽象クラス)に依存すること
2. 依存性注入を使用すること
3. 各クラスは単一責任原則(SRP)に従うこと
4. 変動性の高いビジネスロジックへの依存を最小化すること

## コード生成時の注意事項
- データアクセスとビジネスロジックを分離
- グローバル変数の使用禁止
- マジックナンバー・マジックストリングの使用禁止(定数や列挙型を使用)
- テスタビリティを常に考慮

レビュー観点:生成後のリファクタリングポイント

AI生成コードのレビュー時には、以下のチェックリストを使用します:

結合度チェックリスト:

  1. 強度(Strength)
    • グローバル変数への依存がないか
    • プライベートフィールドへの直接アクセスがないか
    • データ結合が適切に使用されているか
  2. 距離(Distance)
    • 遠い距離にあるモジュール間に強い結合がないか
    • 抽象化レイヤーが適切に配置されているか
  3. 変動性(Volatility)
    • 変動性の高いビジネスロジックへの依存が最小化されているか
    • 安定した抽象(インターフェース)への依存になっているか

Connascenceチェックリスト:

  1. マジックナンバー・マジックストリングがないか → 定数・列挙型に置き換え
  2. 位置引数が多用されていないか → 名前付き引数やデータクラスに置き換え
  3. 重複したアルゴリズムがないか → 共通化を検討

アーキテクチャチェックリスト:

  1. 単一責任原則(SRP)に違反していないか
  2. 依存関係逆転の原則(DIP)に違反していないか
  3. レイヤー境界が適切に守られているか

Cynefin理論で判断する:AIに任せる領域、人間が判断する領域

Vlad Khononovの『結合バランス』第2章15では、Cynefin(クネビン)フレームワークを用いて複雑性を理解し、適切な設計判断を行う方法が解説されています。

Cynefin(クネビン)フレームワークとは

Cynefin(クネビン)フレームワーク16は、経営コンサルタントであり複雑系科学の研究者であるDavid J. Snowdenが1990年代終わりに開発した意思決定フレームワークです。問題を4つのドメインに分類します:

  1. Simple(単純):因果関係が明確、ベストプラクティスが存在
  2. Complicated(煩雑):分析すれば因果関係が判明、専門知識が必要
  3. Complex(複雑):因果関係が複雑、探索と実験が必要
  4. Chaotic(混沌):因果関係が不明確、即座の行動が必要

Cynefin理論は、ソフトウェア開発においてアジャイル開発やスクラムを採用すべき理由の説明によく用いられます16。複雑性の高い問題領域では、ウォーターフォール型ではなく、アジャイル開発のようなアプローチが適しています。

AIの得意領域と不得意領域

Cynefinフレームワークを用いて、AIペアプログラミングの適用領域を分類できます:

1. Simple(単純)領域:AIに任せるべき

特徴:

  • 明確な入出力仕様
  • 確立されたパターン
  • 変動性が低い

例:

  • 標準的なCRUD操作の実装
  • データバリデーション
  • よく知られたアルゴリズムの実装(ソート、検索等)
  • 定型的なテストコードの生成

推奨アプローチ:

  • AIに全面的に任せる
  • レビューは機械的チェック(リンター、型チェッカー)で十分

2. Complicated(煩雑)領域:AIと人間の協働

特徴:

  • 分析すれば因果関係が判明
  • 専門知識が必要
  • 複数の正解が存在し得る

例:

  • パフォーマンス最適化
  • セキュリティ実装(認証、認可)
  • データベーススキーマ設計
  • 既存システムへの機能追加

推奨アプローチ:

  • AIに初期実装を生成させる
  • 人間が専門知識に基づいてレビュー・改善
  • 結合度、セキュリティ、パフォーマンスの観点から精査

3. Complex(複雑)領域:人間主導、AIは補助

特徴:

  • 因果関係が複雑
  • 探索と実験が必要
  • 創発的なソリューションが求められる

例:

  • システムアーキテクチャの設計
  • ドメインモデリング(Domain-Driven Design)
  • マイクロサービスの境界決定
  • 結合のバランス化判断

推奨アプローチ:

  • 人間が設計判断を主導
  • AIは部分的な実装やプロトタイプ生成を補助
  • 結合の3次元(強度、距離、変動性)を人間が評価

なぜAIは複雑領域で限界があるのか:

研究4が示すように、AIは大規模で複雑な問題に直面すると不適切なソリューションを提案します。これは、以下の理由によります:

  • 全体像の欠如:AIは提示されたコンテキストのみで判断
  • トレードオフの評価困難:短期的な実装容易性と長期的な保守性のトレードオフを適切に評価できない
  • ビジネスコンテキストの理解不足:ビジネス要件の変化、組織の制約、チームの能力といった要因を考慮できない

4. Chaotic(混沌)領域:人間のみ

特徴:

  • 因果関係が不明確
  • 即座の行動と判断が必要
  • 状況が急速に変化

例:

  • 本番障害対応
  • セキュリティインシデント対応
  • 緊急のホットフィックス

推奨アプローチ:

  • 人間が直接対応
  • AIは参考情報の検索程度に留める

実践的な判断基準

以下の判断基準を用いて、AIに任せる範囲を決定します:

要素AIに任せる(Simple)協働(Complicated)人間主導(Complex)
スコープ単一関数単一モジュールシステム全体
変動性低(標準ライブラリ)中(共通ライブラリ)高(ビジネスロジック)
結合の影響範囲局所的中程度グローバル
要求の明確さ明確分析により明確化可能曖昧、探索が必要
テスタビリティ容易可能設計に依存

例:判断フローチャート

graph TD
    A[タスク開始] --> B{要件は明確か?}
    B -->|Yes| C{スコープは単一関数か?}
    B -->|No| D[人間がドメイン分析]
    C -->|Yes| E{変動性は低いか?}
    C -->|No| F{既存アーキテクチャへの影響は?}
    E -->|Yes| G[AIに全面的に任せる]
    E -->|No| F
    F -->|局所的| H[AIと人間の協働]
    F -->|広範囲| I[人間主導、AIは補助]
    D --> I

ケーススタディ:マイクロサービスの境界決定

シナリオ: ECサイトのモノリシックアプリケーションをマイクロサービス化する際、どのようにサービス境界を決定すべきか。

Cynefin分類:

  • Complex(複雑)領域:因果関係が複雑で、探索と実験が必要

アプローチ:

  1. 人間の役割:
    • ドメイン駆動設計(DDD)に基づく境界文脈(Bounded Context)の特定
    • ビジネスケイパビリティの分析
    • 結合の3次元(強度、距離、変動性)を評価
    • トレードオフの判断(例:ネットワークレイテンシ vs. 独立性)
  2. AIの役割:
    • 既存コードベースの依存関係分析(静的解析)
    • 潜在的なサービス境界の可視化
    • 各候補境界のメリット・デメリットの整理
    • マイグレーション計画のドラフト作成

推奨プロンプト:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
## コンテキスト
ECサイトのモノリシックアプリケーション(Python/Django)をマイクロサービス化します。
以下の主要なドメイン領域があります:
- 商品管理
- 在庫管理
- 注文管理
- 顧客管理
- 決済処理

## タスク
既存コードベースの依存関係を分析し、潜在的なサービス境界候補を3つ提案してください。
各候補について、以下を評価すること:

1. 結合の強度(どれだけ密に他のドメインと結合しているか)
2. データの一貫性要件(トランザクション境界)
3. 変更頻度(変動性)
4. チーム構成への適合性

候補は表形式で整理し、メリット・デメリットを明記してください。
ただし、最終的な意思決定は人間が行うため、「推奨」ではなく「選択肢の提示」として出力してください。

AIの出力を参考にしつつ、人間が以下の追加要素を考慮して最終判断を行います:

  • ビジネス戦略(どの領域に投資するか)
  • チームの専門性と規模
  • インフラストラクチャのコスト
  • 段階的移行の現実性

まとめ

AIペアプログラミングツールは、開発速度を大幅に向上させる一方で、密結合なコードを生成しやすく、技術的負債を蓄積するリスクがあります。GitClear 2025レポート1が示すように、重複コードの4倍増加とリファクタリングの60%減少は、長期的な保守コストの増大を示唆しています。

本記事では、以下の点を論じました:

1. AI生成コードの密結合問題

実証研究134により、AIは以下の傾向があることが明らかになりました:

  • 小規模なコードスニペットでは良好な品質
  • 大規模コード生成時にパーティション分割の品質が著しく低下
  • 「動くコード」を優先し、長期的な保守性を後回し
  • コンテキスト理解の限界により、既存アーキテクチャを無視

2. 結合バランスの3次元評価

Vlad Khononovの『結合バランス』2の枠組みを用いて、結合を体系的に評価する方法を示しました:

  • 強度(Strength):依存性の程度
  • 距離(Distance):モジュール間の離隔
  • 変動性(Volatility):変更可能性

さらに、Connascence(コナーセンス)1011の概念を導入し、より精緻な結合度評価を可能にしました。

3. 実践的なプロンプト設計

密結合を防ぐための具体的なプロンプト設計技法を提案しました:

  • MoT(Modularization of Thought)12:モジュラープロンプティング
  • 境界とモジュールを明示するプロンプト
  • CLAUDE.mdやCursor Rulesを活用した組織的管理
  • 生成後のレビュー観点とチェックリスト

4. Cynefin理論に基づく適用領域の判断

Cynefin(クネビン)フレームワーク1516を用いて、AIに任せるべき領域と人間が判断すべき領域を明確化しました:

  • Simple(単純):AIに全面的に任せる
  • Complicated(煩雑):AIと人間の協働
  • Complex(複雑):人間主導、AIは補助
  • Chaotic(混沌):人間のみ

持続可能なAI協働開発に向けて

AI生成コードの品質問題に対処するには、以下のアプローチが有効です:

  1. 設計原則の明示化
    • プロジェクトルートにCLAUDE.mdやCursor Rulesを配置
    • 結合度、凝集度、レイヤー分離の原則を明記
  2. プロンプトエンジニアリングの組織的取り組み
    • プロンプトテンプレートライブラリの構築
    • チーム内での共有と改善
  3. レビュープロセスの強化
    • 結合度チェックリストの活用
    • Connascence評価の導入
    • 静的解析ツールの活用(例:依存関係分析、循環依存検出)
  4. 継続的なリファクタリング
    • AI生成コードを「初期ドラフト」として扱う
    • 定期的なアーキテクチャレビュー
    • 技術的負債の可視化と管理
  5. 適切な責任分担
    • Cynefin理論に基づく判断基準の確立
    • 人間が設計判断を主導し、AIを補助として活用

AIペアプログラミングは、適切に活用すれば開発生産性を大幅に向上させる強力なツールです。しかし、その潜在的なリスクを理解し、結合バランスの観点から体系的に管理することで、持続可能なソフトウェア開発が可能になります。

Vlad Khononovが述べるように2、重要なのは「疎結合至上主義」ではなく、状況に応じた適切なバランスです。AI時代においても、この原則は変わりません。

関連記事

参考資料

その他参考資料(本文中で番号引用なし)

本文中では直接引用していないが、記事作成時に参照した資料を記載。

引用の正確性について:

本記事で引用した研究は、以下の方法で検証しています:

  • 学術データベース(Google Scholar、arXiv、ACM Digital Library、IEEE Xplore等)での確認
  • 公式ジャーナルウェブサイトでの論文情報の確認
  • 複数の独立した情報源(学術メディア、研究機関の公式発表等)による相互検証

一部の論文については、全文PDFへの直接アクセスが制限されている場合がありますが、論文の要約(abstract)、DOI、著者情報、および主要な発見については、公式の学術データベースおよび信頼できる二次情報源を通じて確認しています。

  1. Coding on Copilot: AI Code Quality Research 2025 - GitClear (2025). 【信頼性: 高】211百万行のコード変更を分析した大規模研究。重複コードの4倍増加、リファクタリングの60%減少を報告。 ↩︎ ↩︎2 ↩︎3 ↩︎4 ↩︎5

  2. Balancing Coupling in Software Design: Universal Design Principles for Architecting Modular Software Systems - Vlad Khononov (2023). Addison-Wesley. 【信頼性: 高】結合の3次元(強度、距離、変動性)を提示し、結合のバランス化アプローチを提案。英語版は2023年10月出版、日本語版『ソフトウェア設計の結合バランス 持続可能な成長を支えるモジュール化の原則』は2025年10月17日にインプレスから出版。 ↩︎ ↩︎2 ↩︎3 ↩︎4 ↩︎5

  3. DORA Report 2024 - Google DevOps Research and Assessment (2024). 【信頼性: 高】AI採用25%増加ごとに配信安定性が7.2%低下することを報告。 ↩︎ ↩︎2

  4. The Impact of AI-Generated Solutions on Software Architecture and Productivity: Results from a Survey Study - arXiv (2024). 【信頼性: 中〜高】AI生成コードの結合度と凝集度を分析。大規模生成時にパーティション分割の品質が著しく低下すると報告。 ↩︎ ↩︎2 ↩︎3 ↩︎4

  5. The Impact of AI-Pair Programmers on Code Quality and Developer Satisfaction: Evidence from TiMi studio - ACM Digital Library (2024). 【信頼性: 高】TiMi Studioのケーススタディ。AIペアプログラミングのポジティブな側面とネガティブな側面を両方報告。 ↩︎

  6. Evaluating the Code Quality of AI-Assisted Code Generation Tools: An Empirical Study on GitHub Copilot, Amazon CodeWhisperer, and ChatGPT - arXiv (2023). 【信頼性: 中〜高】GitHub Copilot、Amazon CodeWhisperer、ChatGPTのコード品質を比較分析。 ↩︎

  7. How AI generated code compounds technical debt - LeadDev (2025). 【信頼性: 中〜高】AIが生成する高度に結合されたコード、God Object、過度に構造化されたソリューションの問題を指摘。 ↩︎

  8. Zero Human Code - What I learned from forcing AI to build (and fix) its own code for 27 straight days - Daniel Bentes, Medium (2024). 【信頼性: 中】27日間のAI実験レポート。AIが抽象概念(設計原則、コード保守性等)に苦労することを報告。 ↩︎ ↩︎2

  9. Balancing Coupling in Software Design: Core Concepts - Vlad Khononov (2024). 【信頼性: 高】結合の3次元(強度、距離、変動性)の詳細解説。公式サイト。 ↩︎ ↩︎2 ↩︎3 ↩︎4

  10. Connascence - Wikipedia. 【信頼性: 中〜高】Meilir Page-Jonesが1992年に提唱したコナーセンスの概念を解説。 ↩︎ ↩︎2

  11. 『ソフトウェア設計の結合バランス 持続可能な成長を支えるモジュール化の原則』 - snoozer05’s blog (2025). 【信頼性: 中】書籍レビュー。第6章がコナーセンスに特化していることを報告。 ↩︎ ↩︎2 ↩︎3

  12. Modularization is Better: Effective Code Generation with Modular Prompting - arXiv (2025). 【信頼性: 中〜高】MoT(Modularization of Thought)プロンプティング技法を提案。Pass@1スコア58.1%〜95.1%を達成。 ↩︎ ↩︎2 ↩︎3

  13. Prompting Robotic Modalities (PRM): A structured architecture for centralizing language models in complex systems - ScienceDirect (2025). 【信頼性: 高】境界プロンプトと適応制御スキーマの分離の重要性を指摘。 ↩︎

  14. Why test-driven development and pair programming are perfect companions for GitHub Copilot - Thoughtworks (2024). 【信頼性: 中〜高】AIペアプログラミングにおけるTDDとペアプログラミングの重要性を解説。 ↩︎

  15. Balancing Coupling in Software Design - Chapter 2: Coupling and Complexity: Cynefin - Vlad Khononov (2024). 【信頼性: 高】書籍第2章。Cynefin理論と複雑性を解説。 ↩︎ ↩︎2

  16. クネビンフレームワークとソフトウェアエンジニアリング - mtx2s’s blog (2021). 【信頼性: 中】Cynefin理論のソフトウェア開発への応用を解説。 ↩︎ ↩︎2 ↩︎3

This post is licensed under CC BY 4.0 by the author.