uvが減らすのは、速さより手順の分岐

Python projectでは、Python本体をpyenv、仮想環境をvenv、依存導入をpip、固定をpip-tools、CLI toolをpipx、配布をbuildやtwineで扱う構成がある。どれも役割は明確だが、新しいmemberは『このrepoではどれを、どの順で使うか』を覚えなければならない。

Astralのuvは、Python version管理、virtual environment、dependency resolution、lockfile、script実行、tool実行、package buildとpublishを一つのCLIへ寄せる。Rust製による速さが注目されるが、チームにとって大きいのは、uv syncuv runを共通の入口にできることだ。既存のpip-compatible interfaceもあり、新規導入と段階移行の両方を狙っている。

Poetryやpipを、全部すぐ置き換える道具ではない

uvのproject workflowはpyproject.tomluv.lockを中心に動く。Poetryと重なる部分は多いが、既存projectのbuild backendや公開方法まで自動的に同じ意味へ変換するわけではない。pip-compatible commandは、requirements中心の既存運用を維持したままresolverやinstallを置き換える場合に使える。

採用判断では機能数より、どのsource of truthを選ぶかを決める。新規applicationならpyprojectとuv.lockへ揃えやすい。既にPoetry lock、private index、editable package、platform markerを運用しているなら、同じdependency graphとbuild artifactが再現できるかを先に試す。

代表的な構成との役割比較
構成主なsource of truth向く状況移行時の注意
uv projectpyproject.toml + uv.lock新規開発、複数platform、統一CLIbuild backendとindex設定を確認
pip + requirementsrequirements*.txt単純な既存運用、広い互換性Python本体やlock生成は別管理
Poetrypyproject.toml + poetry.lock既存Poetry workflowlockの意味と公開設定を一括変換しない
pipx / uvxtoolごとの隔離環境CLIをproject依存へ入れず試す一時実行と永続installを分ける

隔離環境で、initからfrozen syncまで確認した

2026年9月17日、macOS x86_64の一時directoryへ公式installerでuv 0.12.15を配置し、repositoryやglobal Python設定を変更せず検証した。systemのCPython 3.13.2を使ってapplication projectを作り、httpx 0.28.1を追加した。uvは8 packageをresolveし、.venvuv.lockを作成した。

続けてuv runでhttpxをimportし、uv lock --checkuv sync --frozenが成功することを確認した。これは性能benchmarkではない。download状況やcacheで時間は変わる。ここで確認したかったのは、公式の基本commandが一つの隔離projectで、初期化、依存追加、実行、lock検証、再現までつながることだ。

確認項目環境・結果記事での扱い
uv0.12.15このversionでcommandを実行
PythonCPython 3.13.2既存system interpreterを利用
dependencyhttpx 0.28.1を含む8 packageuv.lockへ解決結果を記録
再現確認lock --check / sync --frozen 成功CI向け手順の動作を確認

新規projectは、四つのcommandで流れを覚える

uv initはproject metadataとsample sourceを作り、uv addはpyprojectへdependencyを追加してlockと環境を更新する。uv runはproject environmentでcommandを実行し、必要なら事前に同期する。uv syncはlockに沿ってenvironmentを明示的に揃える。activateは可能だが、日常commandをuv runへ寄せればshell状態への依存を減らせる。

Python versionはuv python pin.python-versionへ残せる。ただし、applicationが本当に3.13以上を要求するのか、開発者の好みで固定しただけかを分ける。libraryならsupport rangeをrequires-pythonへ書き、CIで複数versionを検証するほうが適切な場合がある。

最小projectを作る
uv init --app hello-uv --python 3.13
cd hello-uv
uv add httpx
uv add --dev pytest ruff
uv run python -c 'import httpx; print(httpx.__version__)'
uv run ruff check .
uv run pytest

uv.lockとpyproject.tomlの役割を分ける

pyproject.tomlは、人が管理するdirect dependencyとversion条件を表す。uv.lockは、transitive dependency、artifact、platform条件を含む解決結果を再現するためのmachine-managed fileだ。applicationでは通常lockfileをcommitする。libraryでも開発・test環境の再現には有用だが、利用者へ同じtransitive versionを強制するものではない。

uv sync --frozenはlockを更新せず同期し、pyprojectとの不整合があれば失敗する。CIで意図しないlock更新を防ぐのに向く。uv lock --checkはlockが最新かを確認する。dependency更新はapplication変更と別diffにし、対象を絞るならuv lock --upgrade-package httpxのように指定する。

clone後とCIの基本
uv lock --check
uv sync --frozen
uv run pytest
uv run ruff check .

# 対象を絞ったdependency更新
uv lock --upgrade-package httpx
uv run pytest

CIでは、uv自体とdependency cacheを分けて考える

CIは、uvを再現可能な方法で導入し、Pythonを揃え、uv sync --frozenを行い、uv runで検証する。公式GitHub Actionを使う場合もversionを固定し、Action自体の参照方法は組織のsupply-chain policyに合わせる。cacheは高速化であり、再現性の根拠ではない。cacheを消してもlockから構築できる状態を保つ。

private indexではcredentialをpyprojectやlockへ書かない。CI secretから必要なprocessへだけ渡し、logへURLやtokenを出さない。native extensionがあるprojectは、Python packageだけでなくOS library、compiler、architectureも再現条件へ含める。uvがinstallに成功してもapplicationが動くことは保証しないため、testとbuildまでを完了条件にする。

CI job内の実行部分
uv python install 3.13
uv lock --check
uv sync --frozen --all-groups
uv run ruff check .
uv run pytest
uv build

requirementsからは、変換と採用を分ける

公式guideにはuv add -r requirements.txtでdependencyをprojectへ取り込む方法がある。だが、変換commandが成功しても、environment marker、private index、editable install、constraints、hash policyが同じ意味で表現されたとは限らない。生成されたpyprojectとlockを読み、旧環境とtest結果を比較する。

安全な移行は、作業branchでuvを追加し、既存testをuv run経由で通し、次にCIを切り替え、最後に旧scriptを削除する。Dockerfile、release、publishまで同じPRへ入れると、失敗原因が増える。まずdevelopmentとCIの再現から始める。

既存requirementsをprojectへ取り込む例
uv init --bare
uv add -r requirements.txt
uv add --dev -r requirements-dev.txt
uv lock
uv run pytest
git diff -- pyproject.toml uv.lock

採用条件は、clean checkoutで決める

uvを入れたことではなく、clean checkoutから指定Python、dependency、test、buildを再現できたことを採用条件にする。既存memberのcacheが効いたmachineだけで成功しても不十分だ。macOSとLinux、必要ならWindowsでlockを検証し、native dependencyを含むartifactも確認する。

uvはPython ecosystemの複雑さを隠してくれるが、消してはくれない。build backend、wheel、index、platform markerを理解したうえで、日常操作を狭いinterfaceへまとめるtoolだ。小さなsampleで再現性を確認できたら、一つのserviceから段階的に広げるのがよい。

  • pyprojectを人の宣言、uv.lockを解決結果としてreviewする
  • CIではlock更新を許さず、dependency更新を別taskにする
  • private indexとnative dependencyを移行前に洗い出す
  • clean checkoutでtestとbuildが通るまで旧workflowを残す