はじめに
ども!最近またですね、新しい検証を進めるために環境構築をつらつらとやっている龍ちゃんです。AI開発をスムーズに進めるための環境構築を検証しているんですが、今回はuvのワークスペース機能を使ったモノレポ環境について共有します。
前回の記事「uv + Ruff + mypyで構築する超軽量Python開発環境」では、単一プロジェクトでの開発環境最適化を紹介しました。今回は、その延長として複数プロジェクトを1つのリポジトリで管理するモノレポ環境を構築していきます。
この記事でわかること
Pythonでモノレポを管理するのは大変ですよね。requirements.txtの手動管理、複数venvの環境切り替え、AI開発ツールとの相性…。
この記事では、uvワークスペースを使って、これらの課題を「まるっと解決」する方法を紹介します。Node.jsのpackage.jsonのような自動管理が、Pythonでも実現できます。
この記事の流れ
- 従来のアプローチの課題
- uvワークスペースによる解決
- 実装ガイド(10分で構築)
- 実際のプロジェクト例(GA4分析プロジェクト)
**注**: 本記事ではuvワークスペースを紹介しますが、Poetry、PDM、 Hatchなどでも同様のモノレポ環境を構築できます。プロジェクトの 状況(既存ツール、チームの慣れ、安定性要件など)に応じて 適切なツールを選択してください。uvは2024年登場の新しいツールで、 特に速度を重視する新規プロジェクトに適しています。
pip + requirements.txt の根本的な問題
Pythonの依存関係管理で、こんな経験はありませんか?
# パッケージをインストールしても...
pip install flask
# → requirements.txt は更新されない!
# → 手動で追加するか、pip freeze を使う必要があるこれが、PythonとNode.jsの最大の違いなんですよね。
Node.jsとの比較
| 項目 | pip + requirements.txt | npm + package.json |
|---|---|---|
| パッケージ追加 | pip install X→ 手動でファイル編集が必要 | npm install X→ 自動でファイル更新 |
| パッケージ削除 | pip uninstall X→ 手動でファイル編集が必要 | npm uninstall X→ 自動でファイル更新 |
| 直接依存 vs 間接依存 | 区別困難(pip freeze で混在) | 明確に分離 |
pip freeze の問題
pip freeze を使うと、直接依存と間接依存が混在して出力されます。
# pip freeze の出力
flask==3.0.0
click==8.1.0 # ← これは直接依存?間接依存?
Jinja2==3.1.0 # ← これは直接依存?間接依存?
Werkzeug==3.0.0 # ← これは直接依存?間接依存?
MarkupSafe==2.1.0 # ← Jinja2が依存(間接依存の間接依存!)問題点:
- どれが直接依存で、どれが間接依存か判別できない
- パッケージを削除する時に「これは本当に削除して大丈夫?」と悩む
- requirements.txt が肥大化する
モノレポでの2つのパターンとその課題
Pythonでモノレポを作るとき、これまで主に2つのパターンを試してきました。
パターン1: 各プロジェクトに個別venv
monorepo/
├── project-a/
│ ├── .venv/ # project-a専用の仮想環境
│ ├── requirements.txt
│ └── src/
├── project-b/
│ ├── .venv/ # project-b専用の仮想環境
│ ├── requirements.txt
│ └── src/
└── project-c/
├── .venv/ # project-c専用の仮想環境
├── requirements.txt
└── src/メリット:
- ✅ 各プロジェクトの依存関係が完全に分離
- ✅ プロジェクト間でバージョン競合が発生しない
デメリット:
- ❌ IDE設定が煩雑(環境切り替えが必要)
- ❌ 共通依存関係(pandas など)が各
.venvに重複インストール - ❌ VSCodeのインタープリター設定を頻繁に変更する必要がある
VSCodeワークスペース(.code-workspace)でも解決しない問題
VSCodeには複数のフォルダを1つのワークスペースとして管理する機能があります。
// monorepo.code-workspace
{
"folders": [
{ "path": "project-a" },
{ "path": "project-b" },
{ "path": "project-c" }
],
"settings": {
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python"
}
}これで各プロジェクトが独立した設定を持てるんですが、AI開発の観点では問題があります。
AI開発ツール(Claude Code など)の視点で不利な理由:
- AIは1つのVSCodeウィンドウ全体をコンテキストとして理解する
- フォルダ間の関係性を把握しにくい
- 「project-aのコードを参考にproject-bを修正して」といった指示が通りにくい
私の場合、Claude Codeを使って開発することが多いので、この点は結構重要でした。
monorepo/
├── .venv/ # 全プロジェクトの依存関係を含む
├── requirements.txt # 全プロジェクトの依存関係を手動管理
├── project-a/
│ └── src/
├── project-b/
│ └── src/
└── project-c/
└── src/メリット:
- ✅ IDE設定が簡単(1つのvenvを指定するだけ)
- ✅ 共通依存関係の重複インストールがない
デメリット:
- ❌ requirements.txtの手動管理が確実に必要
- ❌ どのパッケージがどこで使われているか不明瞭
- ❌ バージョン競合が発生しやすい
- ❌ パッケージ削除時の影響範囲が不明
例(ルートrequirements.txtの肥大化):
# monorepo/requirements.txt
# project-a の依存関係
flask==3.0.0
pandas==2.2.0
# project-b の依存関係
django==5.0.0
pandas==2.2.0 # ← project-aと重複
# project-c の依存関係
fastapi==0.115.0
pandas==2.1.0 # ← バージョン競合!どちらを選ぶ?
# これらは直接依存?間接依存?誰が使っている?
click==8.1.0
jinja2==3.1.0
sqlalchemy==2.0.0
...(100行以上続く)問題点:
- どのパッケージがどのプロジェクトで使われているか追跡困難
- パッケージを削除する時に「本当に削除して大丈夫か」判断できない
- バージョン競合を手動で解決する必要がある
私も最初はこのパターンで試していたんですが、requirements.txtの管理が煩雑で、結構な時間のロスをしました。
package.jsonライクな自動管理
uvを使うと、Node.jsのpackage.jsonのような自動管理が実現できます。
uvの動作
# npm の場合
npm install express
# → package.json が自動更新される!
# uv の場合
uv add flask
# → pyproject.toml が自動更新される!これは想像以上に効果的でした。特に複数プロジェクトを管理する場合、手動でrequirements.txtを編集する手間がなくなるだけで、開発体験が大きく変わります。
重要な3つの特徴
- 自動更新される依存関係ファイル
uv addするとpyproject.tomlが自動で更新される- pip のように手動で
requirements.txtを編集する必要がない
- 直接依存と間接依存の分離
pyproject.toml: 直接依存のみ(読みやすい、package.json と同じ)uv.lock: 全依存関係をロック(再現性、package-lock.json と同じ)
- ロックファイルによる再現性
uv.lockで全環境で同じバージョンを保証- バージョン競合を自動解決
単一venv + ワークスペース管理
uvワークスペースを使うと、パターン2の利点(単一venv)とパターン1の利点(明確な依存関係)を両立できます。
uvワークスペースの構成
sios-tech-lab-analytics-ga4/
├── .venv/ ← 単一の仮想環境(全ワークスペース共有)
├── uv.lock ← 統合ロックファイル
├── pyproject.toml ← ルート設定
└── application/
├── batch/
│ ├── pyproject.toml ← バッチの依存関係(自動管理)
│ └── src/
├── frontend/
│ ├── pyproject.toml ← フロントエンドの依存関係(自動管理)
│ └── app.py
└── scraper/
├── pyproject.toml ← スクレイパーの依存関係(自動管理)
└── main.pyポイント:
- ✅ IDE設定が簡単(1つのvenvを指定するだけ)
- ✅ 各ワークスペースの依存関係は
pyproject.tomlで明確に管理 - ✅ バージョン競合は
uv.lockで自動解決 - ✅ 手動での
requirements.txt編集が不要
モノレポ構成によるAI開発体験の向上
私が実際にClaude Codeを使って開発していて感じたのは、モノレポ構成そのものがAI開発ツールとの相性が良いということです。
なぜモノレポがAI開発に向いているのか?
1. AIが全プロジェクトのコンテキストを一度に把握できる
従来のアプローチ(複数リポジトリ or 個別venv):
- AIはリポジトリごとにコンテキストが分断される
- 「project-aのコードを参考にproject-bを修正して」という指示が難しい
- 複数のVSCodeウィンドウを開く必要がある
モノレポ構成:
- 単一のVSCodeウィンドウで全体を見渡せる
- プロジェクト間の依存関係や共通コードをAIが理解しやすい
- 「batchのコードを参考にfrontendを修正して」という自然な指示が通る
これはPoetry、PDM、uvなど、どのツールでも共通するモノレポの利点です。
2. 環境管理のシンプルさ
複数venvの環境では、AIがパッケージをインストールする際に「どのvenvにインストールすべきか」の判断が必要でした。
モノレポで単一venv(または統一された依存管理)を使うと:
- VSCodeのインタープリター設定は1つだけ
- 「今どの環境にいるのか」を意識する必要がない
- AIが想定外の環境にパッケージをインストールするリスクが低い
uvワークスペースの追加メリット
その上で、uvワークスペースには以下の利点があります:
自動管理による認知負荷の軽減
# uv の場合
uv add --package my-monorepo-batch pandas
# → pyproject.toml が自動更新される
# Poetry の場合(同様に自動更新)
cd application/batch
poetry add pandas
# pip の場合(手動編集が必要)
pip install pandas
# → requirements.txt を手動で編集...依存関係の追加・削除時に、AIに「pyproject.tomlを更新して」と指示する必要がありません。
高速なインストール
開発中に頻繁にパッケージを試すとき、uvの高速さ(pip比10-100倍)は体感できます。
しかし: これらは「AI開発に必須」ではなく、「開発体験を向上させる要素」です。Poetry、PDMでも同等の開発体験を得られます。
AI開発における依存管理ツールの選択
| 要素 | 影響度 |
|---|---|
| プロジェクト構造の明確さ | ⭐⭐⭐⭐⭐ 最重要 |
| ドキュメントの充実 | ⭐⭐⭐⭐⭐ 最重要 |
| モノレポ構成 | ⭐⭐⭐⭐ 重要 |
| 一貫性のある命名規則 | ⭐⭐⭐⭐ 重要 |
| 依存管理ツールの種類 | ⭐⭐ 影響は限定的 |
AIツールは依存管理ツールの種類を気にしません。重要なのは、論理的なプロジェクト構造と良いドキュメントです。
実際の選択基準
AI開発においても、依存管理ツールの選択は従来の基準で問題ありません:
- 新規プロジェクト + 速度重視 → uv
- 既存Poetryプロジェクト → そのまま継続で問題なし
- チームがPEP準拠を重視 → PDM
- PyPA公式ツール希望 → Hatch
さらに深くAI協業開発を学びたい方へ: モノレポ環境を整えた後、AI協業開発環境の構築術|モノレポでビルド時間を大幅短縮するCLAUDE.md活用法も参考にしてみてください。CLAUDE.md階層構造を使って、AIにプロジェクト全体像を理解させる方法を解説しています。
実装ガイド(10分で構築)
それでは、実際にuvワークスペースを構築してみましょう。
ステップ1: ルート pyproject.toml の作成
[project]
name = "my-monorepo"
version = "0.1.0"
requires-python = ">=3.12"
[dependency-groups]
dev = [
"ruff>=0.7.0",
"pytest>=8.0.0",
"mypy>=1.18.2",
]
[tool.uv.workspace]
members = ["application/frontend", "application/batch", "application/scraper"]
[tool.ruff]
line-length = 88
target-version = "py312"
[tool.ruff.lint]
select = ["E", "W", "F", "I", "N", "UP", "B"]
[tool.mypy]
python_version = "3.12"
warn_return_any = true
check_untyped_defs = true
ignore_missing_imports = trueポイント:
tool.uv.workspace.membersで各ワークスペースを定義- ルートには開発ツール(ruff, pytest, mypy)を配置
- コード品質設定(Ruff、mypy)は全ワークスペースで共有
ステップ2: 各ワークスペースの pyproject.toml
例: application/batch/pyproject.toml
[project]
name = "my-monorepo-batch"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"pandas>=2.2.0",
"python-dotenv>=1.0.0",
]例: application/frontend/pyproject.toml
[project]
name = "my-monorepo-frontend"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"streamlit>=1.40.0",
"pandas>=2.2.0",
"plotly>=5.24.0",
]ポイント:
- 各ワークスペースは独立した
pyproject.tomlを持つ - 共通依存関係(pandas など)は
uv.lockで自動的に1つのバージョンに統一される
ステップ3: 依存関係のインストール
# 全ワークスペースの依存関係を同期
uv sync
# 特定のワークスペースにパッケージを追加
uv add --package my-monorepo-batch pandas
# ルートワークスペースに開発ツールを追加
uv add --dev ruff pytest mypy重要: uv sync 1回で全ワークスペースの依存関係がインストールされます。これは、npmの npm install と同じ感覚ですね。
ステップ4: コマンド実行
# 特定のワークスペースのコマンドを実行
uv run --package my-monorepo-batch python application/batch/script.py
uv run --package my-monorepo-frontend streamlit run application/frontend/app.py
# 開発ツールの実行(ルートワークスペース)
uv run ruff format .
uv run mypy .
uv run pytest実際のプロジェクト例: GA4分析
私が実際に構築したGA4(Google Analytics 4)分析プロジェクトでの実装例を紹介します。
ワークスペース構成
sios-tech-lab-analytics-ga4/
├── .venv/ # 単一の仮想環境
├── uv.lock # 統合ロックファイル(206KB、1306行)
├── pyproject.toml # ルート設定
└── application/
├── batch/
│ ├── pyproject.toml # GA4データ取得・変換
│ └── script.py
├── frontend/
│ ├── pyproject.toml # Streamlitダッシュボード
│ └── app.py
└── scraper/
├── pyproject.toml # ブログデータ収集
└── main.py各ワークスペースの役割
1. batch: GA4からのデータ取得・変換
# application/batch/pyproject.toml
[project]
name = "sios-tech-lab-analytics-ga4-batch"
dependencies = [
"google-analytics-data>=0.18.0",
"pandas>=2.2.0",
"python-dotenv>=1.0.0",
]2. frontend: Streamlitダッシュボード
# application/frontend/pyproject.toml
[project]
name = "sios-tech-lab-analytics-ga4-frontend"
dependencies = [
"streamlit>=1.40.0",
"pandas>=2.2.0",
"plotly>=5.24.0",
]3. scraper: ブログデータ収集
# application/scraper/pyproject.toml
[project]
name = "sios-tech-lab-analytics-ga4-scraper"
dependencies = [
"requests>=2.32.0",
"beautifulsoup4>=4.12.0",
"lxml>=5.0.0",
"click>=8.1.0",
]
[project.scripts]
blog-scraper = "sios_tech_lab_analytics_ga4_scraper.main:main"ポイント:
- エントリポイント(
[project.scripts])を定義することで、uv run blog-scraperでコマンド実行可能
共通依存関係の扱い
pandas>=2.2.0 は batch と frontend で共有されますが、uv.lock で自動的に1つのバージョン(2.2.1)に統一されます。
これにより、バージョン競合を気にせず開発できます。手動で調整する必要がないので、すごく楽ですね。
DevContainerとの統合
{
"name": "Python Dev (uv + Ruff + mypy)",
"build": {
"dockerfile": "Dockerfile"
},
"customizations": {
"vscode": {
"extensions": ["charliermarsh.ruff", "ms-python.python"],
"settings": {
"": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true
}
}
}
},
"postCreateCommand": "uv sync"
}ポイント:
postCreateCommand: "uv sync"でコンテナ作成時に全依存関係を自動インストール- チーム全員が同じ環境を共有できる
まとめ
今回は、uvワークスペースを使ったPythonモノレポ管理の方法を紹介しました。
この環境で得られる4つのメリット
✅ package.jsonライクな自動管理 – pip + requirements.txtからの脱却
✅ 単一venv + ワークスペース管理 – IDE設定が簡単
✅ AI開発ツールとの相性が抜群 – 全プロジェクトを一度に把握
✅ バージョン競合の自動解決 – 手動調整が不要
Pythonのモノレポ管理が、Node.jsのように快適になります。
次のステップ
- 今回の記事: 開発環境での構築(uvワークスペースの基本)
- 次回の記事: デプロイ・本番環境への展開
- GitHub ActionsでのUV対応
- uvからrequirements.txtへの変換
- コンテナビルドと本番環境での実行
ぜひ、あなたのプロジェクトでも試してみてください!
質問や感想は、コメント欄でお待ちしております。
