モノレポ管理の課題とClaude Codeの活用
複数のプロジェクトを1つのリポジトリで管理するモノレポ構成。スケールするにつれて、ファイル間の依存関係を理解するのが難しくなったり、変更の影響範囲を把握するのに時間がかかったりします。個人のフリーランスプロジェクトでも、クライアントのマイクロサービス群を管理する場面でこの問題に直面しました。
Claude Codeはファイル操作とコンテキスト認識に優れたAIアシスタントです。ここでは、Claude Codeを活用してモノレポの複数プロジェクトを効率的に管理する方法を、実際に運用している構成とともに紹介します。この記事を読むことで、モノレポの複雑さを軽減し、プロジェクト間の一貫性を保ちながら開発速度を上げるテクニックが身につくでしょう。
モノレポ構造の基本設計
よくある問題:フラットな構成からの脱却
最初のプロジェクトでハマったのが、すべてのコードをトップレベルに配置する構成です。プロジェクトが増えると、どのファイルがどのプロジェクトに属しているのか、変更時にどこへの影響があるのかが曖昧になります。
❌ アンチパターン:フラットな構成
project-root/
├── main.py
├── utils.py
├── config.json
├── api_handler.py
├── database.py
├── auth.py
└── server.py
この構成では、3つ以上のプロジェクトが入り混じると、「このmain.pyはどのプロジェクトのものか」という質問が頻出します。
推奨される構成:プロジェクトごとのディレクトリ分離
実際のモノレポ運用では、プロジェクトごとにディレクトリを分けるのが基本です。
✅ 推奨構成
monorepo/
├── shared/
│ ├── utils/
│ ├── config/
│ └── types.py
├── projects/
│ ├── api-backend/
│ │ ├── src/
│ │ ├── tests/
│ │ ├── requirements.txt
│ │ └── pyproject.toml
│ ├── web-frontend/
│ │ ├── src/
│ │ ├── public/
│ │ ├── package.json
│ │ └── tsconfig.json
│ └── worker-service/
│ ├── src/
│ ├── Dockerfile
│ └── go.mod
├── .github/
│ └── workflows/
└── README.md
このような構成にすることで、プロジェクト間の境界が明確になります。Claude Codeに「projects/api-backend 配下で△△を実装して」と指示するだけで、スコープが限定されるため、無関係なファイルを変更するリスクが下がります。
Claude Codeとの効果的な連携パターン
モノレポ全体のコンテキスト共有
Claude Codeを使う際、最初にやることはモノレポ全体の構造を理解させることです。単にプロンプトで説明するより、以下のような情報をまとめたドキュメントを作成し、参照させるのが効果的です。
MONOREPO_STRUCTURE.md の例
# モノレポ構成
## プロジェクト一覧
- api-backend: FastAPI ベースの REST API(Python 3.12)
- web-frontend: React + TypeScript のフロントエンド
- worker-service: Go 1.22 で書かれた非同期ジョブ処理
## 共有ライブラリ
- shared/utils: 共通ユーティリティ関数
- shared/config: 環境設定管理
- shared/types: 全プロジェクトで使う型定義(JSON Schema形式)
## 依存関係
api-backend -> shared/utils, shared/config, shared/types
web-frontend -> shared/config, shared/types
worker-service -> shared/config
## デプロイメント
- api-backend: Docker コンテナ、AWS ECS
- web-frontend: S3 + CloudFront
- worker-service: Docker コンテナ、ECS + SQS
Claude Codeに「この MONOREPO_STRUCTURE.md を参考にして…」と指示することで、プロジェクト全体の文脈を理解した上で提案や実装を行ってくれます。
プロジェクト固有の指示テンプレート
各プロジェクトの開発ガイドラインをテンプレート化して、Claude Code に参照させるのが実務的です。
projects/api-backend/DEVELOPMENT.md の例
# API Backend 開発ガイド
## 技術スタック
- FastAPI 0.110
- Python 3.12
- PostgreSQL 16
- pytest + pytest-asyncio
## ディレクトリ構成
- src/main.py: アプリケーションエントリポイント
- src/routers/: エンドポイント定義
- src/models/: SQLAlchemy ORM モデル
- src/schemas/: リクエスト/レスポンス Pydantic スキーマ
- tests/: ユニットテスト + 統合テスト
## コーディング規約
- 型アノテーション必須
- async/await 推奨(ブロッキング処理は避ける)
- エンドポイントは routers/ に配置
- ビジネスロジックは models/ または別の service.py に
## テスト
すべての新機能には tests/ にテストを追加すること
pytest --cov=src
具体的な運用シナリオ別ガイド
シナリオ1:新しいエンドポイントをAPI Backendに追加する
「shared/types のユーザーモデルを使い、GET /users/{id} エンドポイントを実装してください。エラーハンドリングと型チェックも含めて」という指示を Claude Code に出すとします。
Claude Code が効果的に動くためには、以下の情報を提供するのが重要です:
- 現在の routers/ にある既存エンドポイントの例
- shared/types でのユーザーモデルの定義
- テストファイルのサンプル
実装例は以下のようになります:
from fastapi import APIRouter, HTTPException, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from src.models import User
from src.schemas import UserResponse
from shared.types import UserModel
router = APIRouter(prefix="/users", tags=["users"])
@router.get("/{user_id}")
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)) -> UserResponse:
"""ユーザー情報を ID で取得"""
user = await db.get(User, user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return UserResponse.model_validate(user)
ここで重要なのは、shared/types のモデルと src/schemas の Pydantic スキーマを明確に分離するという設計です。shared/types は複数プロジェクト間で使う共通定義、schemas はそのプロジェクト特有の API レスポンス形式という役割分担が決まっていると、Claude Code は一貫した提案ができます。
シナリオ2:複数プロジェクトに影響する共有ライブラリを変更する
shared/utils のヘルパー関数を修正する場面は危険です。API Backend、Worker Service の両方で使われている関数を変更した時、両プロジェクトの動作確認が必要になります。
実務では以下のアプローチを取ります:
- shared/utils の変更内容を明確にドキュメント化
- Claude Code に「この変更の影響を受けるプロジェクト/ファイルを列挙してください」と指示
- 影響を受けるプロジェクトの tests/ から該当テストを実行
- 必要に応じてテストコードを Claude Code に修正させる
例えば、shared/utils の日時処理関数を修正する場合:
from datetime import datetime, timezone
# 変更前
def parse_timestamp(ts: str) -> datetime:
return datetime.fromisoformat(ts)
# 変更後:UTCタイムゾーン情報を必ず含める
def parse_timestamp(ts: str) -> datetime:
dt = datetime.fromisoformat(ts)
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return dt
このような変更があると、既存の呼び出し元でテストが落ちる可能性があります。Claude Code に影響範囲を検索させ、テストを実行して確認するフロー を整備することで、リグレッションを防げます。
シナリオ3:新しいプロジェクトを追加する時の初期化
モノレポに新しいマイクロサービスを追加するとき、既存の慣例に従いつつ、プロジェクト固有の設定を行う必要があります。
Claude Code に以下のような指示が有効です:
現在のモノレポに新しい Go サービス(analytics-service)を追加します。既存の worker-service の構成を参考にしながら、以下を含めてください:
– go.mod と go.sum
– Dockerfile
– 共有設定への接続
– Makefile でのビルドターゲット
– DEVELOPMENT.md ファイル
実装結果例:
projects/analytics-service/
├── main.go
├── go.mod
├── go.sum
├── Dockerfile
├── Makefile
├── DEVELOPMENT.md
├── handlers/
│ └── handler.go
├── models/
│ └── analytics.go
└── tests/
└── handler_test.go
Claude Codeの活用時の実装パターン
コンテキストを限定した効果的な指示
モノレポではファイルが多いため、Claude Code に与えるコンテキストの質が重要です。無制限に全ファイルを参照させるより、関連するファイルだけを提示する方が正確な提案が得られます。
実務でよく使うパターン:
- 特定プロジェクトのディレクトリだけを参照させる – 「projects/api-backend/ 配下で…」と明示的に指定
- 依存ファイルの順序を決める – shared/types → shared/config → プロジェクト固有コード
- 参照する設定ファイルを指定 – 「pyproject.toml と DEVELOPMENT.md を参照にして…」
変更の影響範囲検証
モノレポで怖いのは、1つの変更が予想外の場所に影響することです。Claude Code の検索機能を活用して、事前に影響範囲を把握するプロセスが効果的です。
以下のようなチェックリストを Claude Code と協力して実行します:
影響範囲チェックリスト例
1. 変更対象: shared/types の UserModel.email フィールドを追加
2. 検索対象ファイル:
- projects/api-backend/src/schemas/*.py
- projects/web-frontend/src/types/*.ts
- projects/api-backend/tests/*.py
3. 確認すること:
- schema が UserModel.email に対応しているか
- API レスポンスに email を含める場合の DB マイグレーション
- フロントエンド側での型定義更新
- 既存テストの修正必要性
トラブルシューティングと実務ノウハウ
よくあるハマりポイント1:モジュール解決の失敗
Python のモノレポで、shared パッケージをインポートできないというエラーに遭遇しました。これは、Python が shared をモジュールとして認識できていないためです。
❌ 実装例(失敗):
from shared.types import UserModel # ModuleNotFoundError
✅ 修正方法:
pyproject.toml または setup.py で shared パッケージを明示的に指定します:
[tool.pytest.ini_options]
pythonpath = [".", "./shared", "./projects/api-backend"]
または PYTHONPATH を設定:
export PYTHONPATH="${PYTHONPATH}:$(pwd):$(pwd)/shared"
よくあるハマりポイント2:異なるバージョンの依存関係競合
複数のプロジェクトが異なるバージョンの同じライブラリを使うと、インストール時に競合します。poetry や pipenv といった依存関係管理ツール を各プロジェクトレベルで運用するのが推奨です。
projects/api-backend/pyproject.toml
[tool.poetry.dependencies]
python = "^3.12"
fastapi = "0.110.0"
projects/worker-service/pyproject.toml
[tool.poetry.dependencies]
python = "^3.12"
celery = "5.3.4"
各プロジェクトが独立した依存関係管理を持つことで、互いの影響を最小化できます。
よくあるハマりポイント3:Claude Code の過度な書き換え
Claude Code に「このプロジェクト全体をリファクタリングして」と丸投げするのは危険です。予期しない変更が加わり、本番環境で問題が発生するリスクがあります。
実務では以下のアプローチを取ります:
- 小粒度の変更に絞る – 1つのエンドポイント、1つのユーティリティなど
- 変更前後の比較を確認 – diff を見て想定外の変更がないか確認
- テストケースを必ず付ける – 変更内容に応じたテストを一緒に実装させる
- レビュープロセスを組み込む – 自動で merged する前に人間がチェック
パフォーマンスと運用コストの最適化
ビルド時間の削減
モノレポのビルド時間が増えると、開発効率が著しく低下します。変更があったプロジェクトだけをビルドする incremental build が重要です。
実装例(Makefile):
.PHONY: build-api build-frontend build-worker build-all
CHANGED_PROJECTS := $(shell git diff --name-only HEAD^ HEAD | cut -d/ -f2 | sort | uniq)
build-api:
@if echo "$(CHANGED_PROJECTS)" | grep -q "api-backend"; then \
echo "Building api-backend..."; \
cd projects/api-backend && poetry install && pytest; \
else \
echo "No changes in api-backend"; \
fi
build-all: build-api build-frontend build-worker
この Makefile により、変更があったプロジェクトだけが再ビルドされ、全体のビルド時間が大幅に削減されます。
テスト実行の効率化
モノレポ全体のテストを実行すると時間がかかります。影響範囲を絞ったテスト実行が現場では必須です。
テスト戦略例
1. ユニットテスト(各プロジェクト独立): 5〜10秒
pytest projects/api-backend/tests/unit/
go test ./projects/worker-service/...
2. 統合テスト(shared への変更時のみ): 20〜30秒
pytest projects/api-backend/tests/integration/
pytest projects/web-frontend/tests/integration/
3. エンドツーエンドテスト(本番前のみ): 2〜5分
docker-compose up -d && pytest e2e/
まとめ
- モノレポ構成はプロジェクトごとにディレクトリを分離し、shared ライブラリと明確に分ける。これがコンテキスト理解の第一歩
- Claude Code を活用する際は、MONOREPO_STRUCTURE.md や DEVELOPMENT.md といったドキュメントを参照させ、プロジェクト全体の文脈を理解させることが効果的
- 変更の影響範囲は事前に検証し、無関係なファイルへの予期しない変更を防ぐ。テストと一緒に実装させることが重要
- モジュール解決、依存関係競合、版管理といった技術的課題は、各プロジェクトでの独立した設定と、shared での統一ルールのバランスが鍵
- ビルドやテストの実行時間は、incremental build や影響範囲限定で最適化。開発効率に直結する
- Claude Code の提案は盲目的に採用せず、小粒度の変更に絞り、diff を確認し、テストを付けるプロセスを堅持
よくある質問(FAQ)
Q1. モノレポを運用している場合、CI/CD パイプラインはどう構成すべきですか?
変更があったプロジェクトだけをビルド・テスト・デプロイするのが基本です。git diff で変更ファイルを検出し、該当プロジェクトのジョブだけをトリガーする方法が一般的です。GitHub Actions の例なら、changed-files アクションを使い、ファイルパスでジョブを条件付けします。これにより、api-backend を変更した時に web-frontend の不要なビルドを避けられます。
Q2. shared ライブラリとプロジェクト個別のコードの責任分界はどう決めるべきですか?
shared に入れるのは、2つ以上のプロジェクトで使う、変更頻度が低い、かつ安定性が高いコードです。ユーティリティ関数、型定義、設定管理が代表例。一方、ビジネスロジックやプロジェクト特有のスキーマはプロジェクト個別に保つ。判断に迷ったら「1つのプロジェクトだけで使う可能性があるか」を問いかけることがコツです。
Q3. モノレポの規模が大きくなった場合、Claude Code だけでは対応しきれないことはありますか?
プロジェクト数が10個を超える場合、全体の依存関係グラフの可視化や、アーキテクチャ意思決定の記録(ADR)が重要になります。Claude Code は個別の実装タスクに強いですが、複数プロジェクト間の設計方針判断は人間の方が適切です。定期的にアーキテクチャレビューを開き、モノレポのルールを更新するプロセスが必要になります。
Q4. Python と Go、JavaScript が混在したモノレポでも Claude Code は対応できますか?
できます。ただし、各言語の慣例(Python なら PEP 8、Go なら Effective Go)を DEVELOPMENT.md に明記することが重要です。Claude Code に「この言語では~という慣例に従ってください」と明示すれば、生成されるコードの品質が大幅に向上します。また、言語別のディレクトリ分離や、各言語のテストフレームワークを統一するといった工夫も有効です。
Q5. モノレポのリポジトリサイズが大きくなった時、クローンやプッシュが遅くなる対策はありますか?
Git LFS(Large File Storage)の活用や、shallow clone(–depth オプション)での開発が考えられます。さらに根本的には、モノレポを分割することも視野に入ります。プロジェクト間の依存が薄い場合は、プロジェクトごとにリポジトリを分け、shared ライブラリだけ共有リポジトリにするという polyrepo 寄りの構成に変更するのも選択肢です。判断基準は「プロジェクト間の変更頻度と結合度」です。

