世界を動かす技術を、日本語で。

UVは素晴らしいが、そのパッケージ管理のユーザーエクスペリエンスは混乱している

概要

uv はPython界で急速に普及中だが、保守作業でいくつかのUX課題が浮上。 依存パッケージのアップデート管理 やバージョン制約の扱いは、他ツールと比べて難あり。 --boundsオプション や設定で改善可能だが、初期設定のままだとリスクが残る。 uv pip list --outdated などのコマンドで一部不満は解消可能。 アプリケーション運用 とライブラリ公開で推奨設定が異なる点に注意。

uvの導入と初期設定の利便性

  • uv は高速動作・多機能・シングルバイナリでPythonプロジェクト管理を簡素化。
  • 新規プロジェクトのセットアップや依存追加が極めて容易。
  • Pythonバージョン管理や各種ツールの置き換えにも対応。

保守フェーズでの課題

  • 依存パッケージの更新確認 が直感的でない。

    • JavaScriptの pnpm outdated のようなシンプルなコマンドが存在しない。
    • uvでは uv tree --outdated --depth 1 を使用するが、全依存ツリーが表示されてノイズが多い。
    • Poetryの poetry show --outdated はまだ実用的だが、uvは情報過多。
  • バージョン制約のデフォルトが危険

    • pnpmやPoetryはデフォルトで上限バージョンを設定し、 SemVer による安全なアップデートが可能。
    • uvは 上限なし(例: pydantic>=2.13.4) で記載され、メジャーバージョンの破壊的変更も自動で受け入れてしまう。
    • uv lock --upgrade は全依存を最新化し、深い依存まで一気に更新されるため、破壊的変更のリスクが高い。
  • 個別アップデートコマンドの煩雑さ

    • pnpmでは pnpm update pydantic httpx uvicorn のように一度で複数指定可能。
    • uvは uv lock --upgrade-package を依存ごとに繰り返し指定する必要があり、非効率。

改善策と現状の希望

  • --boundsオプション の導入

    • uv add pydantic --bounds major で上限付き制約(例: pydantic>=2.13.4,<3.0.0)を追加可能。
    • ただし現時点では プレビュー機能 であり、毎回明示的に指定が必要。
  • 設定ファイルによるデフォルト化

    • pyproject.toml[tool.uv] add-bounds = "major" を記載すれば、毎回--bounds指定不要。
    • これにより、上限付き制約をデフォルト化でき、運用リスクを大幅に軽減。
  • uv pip list --outdated の活用

    • uv pip list --outdated は実際に更新が必要なパッケージのみを表示。
    • ただし、pip互換コマンド配下であり、発見性がやや低い点が課題。

アプリケーションとライブラリでの運用指針の違い

  • ライブラリ の場合
    • 上限をピン止めすると依存解決が困難になり、PyPI公開ライブラリでは非推奨。
  • アプリケーション の場合
    • 上限設定で破壊的変更のリスクを回避できるため、 add-bounds = "major" 運用が推奨。

今後の改善要望

  • uv outdated のような専用コマンドの実装。
  • updateコマンドのUX改善 (複数依存の一括指定など)。
  • デフォルトで SemVer準拠のバージョン制約 を採用すること。

まとめ

  • uv は速度・多機能性で画期的だが、保守性やUX面で既存ツールに及ばない部分も多い。
  • 設定やコマンドの工夫である程度解決可能だが、デフォルト挙動や発見性の向上が今後の課題。
  • アプリケーション運用時はadd-boundsの設定が必須、ライブラリ開発時は上限設定を避けるのが鉄則。

Hackerたちの意見

「めちゃくちゃだ」と言って、余計な引数をいくつか書かなきゃいけない例を二つ挙げてるね。もっといいタイトルは「UVにあったらいいQOLの変更」かな。

あのフレーズや「このコマンドラインインターフェースを設計したのは誰だ?」っていうのは、注目を集めるために書かれたんだろうね。フィードバックの内容は役に立つし、ほとんどに同意するけど、そんなフレーズを使うとフィードバックの価値が下がっちゃうし、防御的な反応を招くよね。私もuvのコマンドラインインターフェースは使いづらいと思うけど、なぜこう書かれたのかは理解してるよ。

上限がないことに注意。UVは特異な解決が必要だから、これは意図的なものだよ。npmではツリーの異なる部分に対して分岐する解決をインストールできるけど、Pythonではそれができない。Ryeでも同じ決断をしなきゃいけなかったけど、ここにもっと良い解決策はないんだ。もし上限が設定されたら、実際に解決できないツリーになっちゃうよ。Pythonのいくつかのパッケージエコシステムでは、誤った上限を持つ古いパッケージのオーバーライドを公開するところまで行ったこともある。今日、自分のパッケージがまだリリースされていないパッケージと互換性があるかどうかはわからないってことを忘れないで。

セマンティックバージョニングの目的は、その難題を解決する方法を提供することだよ。新しいメジャーバージョン=互換性がないと仮定する。実際に動くかどうかは別として、そういうことなんだ。

それに、実際にはuvとnpmの両方を使う正しい方法は、すべてを「=」に切り替えて、手動で更新することだよ。非メジャーアップデートを信頼して何も壊れないって思うよりもね。

uvが状況を改善してくれたとはいえ、根本的にツールで解決できない問題がたくさんあるんじゃないかな。前と比べて状況がかなり良くなったのは信じられないけど、エコシステム全体が何かしらの大きな変更をしない限り、完全に良くなることはない気がする。2から3への移行のことを考えると、そんなことをする気はしばらくないだろうね。

個人的には、updateを実行したときにuvから「パッケージが互換性がない」ってエラーが出る方がいいかな。必要ならそれをオーバーライドできる方法があれば、実行時に互換性のないバージョンで追跡が難しいエラーが出るよりはマシだと思う。

pyproject.tomlに上限がないのが本当の問題じゃない。実際の問題は、uv lock --upgradeが上限なしで全てを一括でアップグレードしちゃうことだよ。メジャーバージョンを更新せずにパッケージをアップグレードできる方法があれば、このコマンドはもっと安全に実行できると思う。

あなたの言ってることはライブラリの作者には理解できるけど、ウェブサイトを作るときにたくさんのパッケージに依存してると、アップグレードのときに安全でいたいし、上限を設定したいんだよね。—-boundフラグはすごく役立つけど、もう一つ打たなきゃいけないことが増えるのが面倒。プロジェクトがライブラリじゃないって分かったら、uvがデフォルトで上限を設定してくれるといいな。

その部分の記事はほとんどクリックベイトみたいに感じたよ。最後に上限引数があるって認めてるからね。> uv add pydantic --bounds major だから、何を文句言ってるのかよく分からない。

uvは~=をサポートしてないの?それとも、コマンドラインでパッケージを追加するときにpyprojectファイルを編集するのではなく、uvがそれを追加しないって話なの?~=は私にとっては標準的なやり方で、コマンドを覚えるよりファイルを編集する方が好きなんだよね。

上限バージョンが指定されると、実際には解決できないツリーになってしまうだろう。そうなると、上限バージョンなしでuvを再実行しなきゃいけなくなる。ああ、人間性よ!互換性のないパッケージで依存関係が壊れるよりはマシだけどね。安全じゃないオプションがデフォルトになるのはおかしい気がする。

(注:私はuvの開発に関わっています。)これらの多くは、クリックベイトスタイルで表現されていても、役に立つフィードバックだと思う。いくつかの考えを挙げると: - pnpm outdatedについて:これはあまり話題に上がらないけど、私には合理的に思える。PythonとJavaScriptの文化的な違いが影響してると思う。Pythonの依存関係が古くなってるかどうか気にしたことはないから、脆弱性や壊れていなければいいんだ。対照的に、JavaScriptエコシステムでは、機会を見てアップグレードするのが一般的なようだね。これは悪いことではないと思うけど、非常に大きなプログラミングコミュニティ間でCLIで何が価値があるかの直感が異なることを示す良い例だと思う。 - Arminが指摘しているように[1]、uvの上限の挙動は意図的なもので、Pythonの解決方法の機能的な必要性なんだ。これはPythonが他の言語と比べて選んだトレードオフだけど、正直言って私はこれが良いと思う。依存関係のコピーが一つだけあって、すべての相互依存する要件がそれに解決するのが好きなんだ。 - uv lock --upgradeはロックファイルをアップグレードするためにそう書かれているんだ。ユーザー自身の要件をアップグレードするわけじゃない。対照的に、pnpm updateはユーザー自身の要件(package.json内)を更新するようだね。これが混乱を招くのは理解できるけど、uv lockの下に置く方が厳密に正確だと思う。そうしないと、ユーザーが「uv upgrade」が自分の考えるアップグレードをしない理由で混乱することになるからね。それでも、もっとクリーンに提示できることは確かだし、要件を直接アップグレードするuvのサブコマンドに対するユーザーの需要は明確にあるよ。

pipからuvに移行したんだけど、情報が必要なときはuv pip list --outdatedに戻っちゃう。

この記事の著者です。「クリックベイトスタイル」に見えるのは申し訳ないけど、実際はオランダの率直さと正直さから来てる詩的な更新なんだ。uvのCLIの整理の仕方が、正直言って使いづらいと思う。ユーザーフレンドリーじゃなくて、機械向けに設計されてる感じ。

pnpm outdated: これはあまり話題になってないけど、私には合理的に思える。これを使う一つの目的は、「uv sync --update」や「uv lock --update」を実行したときに何が更新されるかを見ることだね。ただ、そのコマンドには確認プロンプトがあった方がいいかも。

古いバージョンや上限については同意だね。ただ、ユーザーがインターフェースが難しいって文句を言ってるなら、何か問題があるんじゃないかな。uv lockコマンドがロックファイルにしか効かないのは理解できるけど、ユーザーには直接依存関係や間接依存関係をアップグレードする必要があるんだよね。間接依存関係にはuv lock --upgrade-packageが使えるけど、ちょっと長いよね。直接依存関係にもuv lock --upgrade-packageは使えるけど、pyproject.tomlには触れないから、開発者には見えにくい。uv.lockのパッケージバージョンがpyproject.tomlを超えてくると、pyproject.tomlは依存関係の信頼できるガイドじゃなくなってくる。フレンドリーなuv upgradeコマンドがあったらいいな。今まで見た中で一番のuv UXの落とし穴はuv pipだね。多くのプロジェクトが開発にpyproject.tomluv.lockを正しく使ってるのに、デプロイのDockerfileやCIツールではuv pip install -r pyproject.tomlを使って、uv.lockをバイパスしてるのを見たことがある。確かに、コーディングエージェントが悪いuv pipパターンを推奨してるのは、彼らのトレーニングセットにpipが多すぎるからだけど、uvはユーザーを守るための工夫をすべきだと思う。愚痴っちゃってごめん、uvは素晴らしいツールで、もっと使われるべきだと思うよ!エコシステムへの貢献に感謝!

ちなみに、uv upgradeはロードマップに入ってるよ。まだ実装してないのは、素晴らしい体験を作るのが難しいからなんだ(予想以上に細かいニュアンスがあるし)、私たちは小さなチームで優先事項がたくさんあるからね。

UVはPythonにとってすごく貢献してるけど、今日はちょっと苦戦したよ。いくつかの異なるリポジトリに出てくるスクリプトの管理を集中させようとしてたんだけど、実装が時間とともにいろいろずれてしまってたんだ。私のアイデアは「uv run --with $package main --help」で、自動的に 1. 存在しなければインストールして実行 2. 最新版ならインストールしない 3. 最新版でなければ更新 という簡単な方法を探してたんだ。この3つは意外と難しかった。デフォルトでは、uv runは毎回再インストールしちゃうからね。6秒のvenvとuvxやuvツールもあまり良くなくて、ユーザーがアップグレードを受け取れない新たな問題を引き起こすことになった。結局、スクリプトを使ってcodeartifactでページネートされたGETを実行し、新しい非開発版があれば更新(その後再実行)することにした。それがうまくいくみたい。200msの遅延は6秒よりはマシだけど、私が求めてた体験とはちょっと違ったかな。

uvxやuvツールもあまり良くなかったから、ユーザーがアップグレードを受け取れない新たな問題を引き起こした。ユーザーはただuv tool upgradeを実行すればよくない?

それにはuv tool installuv tool upgradeを使うといいと思うよ。でも、ぜひ問題を報告してほしい。簡単に対処できそうな小さな問題っぽいから!

https://copier.readthedocs.io/en/stable/をチェックしてみて!あなたの使い方にぴったりかは分からないけど、ポリレポのマイクロサービスエコシステムを同期させるのにすごく役立ってるよ。

ちょっと混乱してるんだけど、uv run --with $package main --helpは言ってることをほとんどオーバーヘッドなしで実行できるはずだよ。毎回再インストールするわけじゃないし、--with環境はキャッシュに保存されるから。環境がキャッシュされてても、依存関係もキャッシュされてるから、キャッシュからのインストールはすごく早いよ(確実に200ms未満)。詳細を添えて再現を開いてくれてもいいよ。私、uvの開発に関わってるから。

「混乱してる」と言われると、アプリを書くときは、Claudeが登場する前から依存関係のインストールに脳みその2つの細胞しか使ってなかったけど、それでnpmやuv、cargoがちゃんと動いてたから十分だった。これらのフラグは一切使ったことないし、その間にpipとかはもっと注意を払わないといけないから面倒だよね。

(私、uvの開発に関わってる)ちなみに、uv addのデフォルトの上限を永続的な設定で設定できるから、毎回指定する必要はないよ。詳しくは https://docs.astral.sh/uv/reference/settings/#add-bounds を見てね。デフォルトで上限を追加しない方がいいのは、エコシステム内で不必要な競合が多くなるからなんだ。以前、Poetryを使っていたときにこの件についてのリソースを集めたことがあるよ :) 参考は https://github.com/zanieb/poetry-relax#references だよ。

わー、ありがとう!「add-bounds」設定について初めて知ったよ!特に、正確な依存関係に固定することが重要で、経験の浅い開発者が見逃しがちなプロジェクトにはめっちゃ役立つね(エンドプロダクト、ライブラリじゃなくて)。

それに、Pythonプロジェクトはセマンティックバージョニングを使わないことが多いよね。 > 「uvの目から見ると、pydanticのバージョン2、3、100はすべて完全に受け入れられる。」セマンティックバージョニングがないと、そうなるよね。

「ライブラリを公開する際に上限バージョンを削除するのは重要だ。」って、すごく納得!でも、この記事はライブラリじゃなくてウェブサイトを作る人向けに書かれてたよね。ウェブプロジェクトで依存関係を使うときは、上限バージョンがあった方が壊れる変更を防げるから、やっぱり必要だよ(依存関係がセマンティックバージョニングを守ってる前提だけど)。その設定を指摘してくれてありがとう、記事を更新したよ。

「uv tree --outdated --depth 1」を使って古い依存関係をリストアップするっていうおすすめには本当に驚いたよ。個人的には「uv pip list --outdated」を使ってるけど、これは導入されてからずっとそう。確かに、このコマンドはすごく重要だから、独立したサブコマンドがあってもいいと思う。

「uv tree -od1」は多分動くね。でも、pacmanや他のパッケージマネージャーの批判は、aptみたいに頻繁に使うコマンドに人間が理解しやすいコマンドを提供する必要があるってことだったからね。

著者です。これはおすすめじゃなくて、ただ私が知っている唯一の方法だったんです。「uv pip list --outdated」は確かにもっと良い出力が出ますね、ありがとう!でも、なんで古いパッケージを表示する方法が2つあって、出力がこんなに違うのか気になります。UXがめちゃくちゃだな…

Pixiはuvをバックエンドに使ってるけど、UIが好きだな。古いパッケージのきれいにフォーマットされたリストを作るためのタスクエイリアスを簡単に追加できるから。(このプロジェクトとは関係ないけど。)特に「pixi-diff-to-markdown」を使うと、自動CIのパッケージ更新をスキャンするのが楽になるよ。だから、更新される古いパッケージを確認するために、プロジェクト用にタスクエイリアスを作る感じかな:pixi task add outdated "pixi update --dry-run --json | pixi exec pixi-diff-to-markdown" それからプロジェクト内でタスクを実行するには:pixi run outdated 出力は、更新されるパッケージの読みやすいMarkdownテーブルで、古いバージョンと新しいバージョンが表示されるよ。もちろん、好みや使い方は人それぞれだけどね。

UXの問題は一番重要じゃないよ。AIが改善するのは数分の問題だから、GitHubで報告する必要があるね。「安全でないバージョン」っていうのは不当な非難だと思う。人々は本当にセマンティックバージョニングを信じてるのかな?メジャーバージョンを上げないことが安全だって?それは解決が難しい互換性の問題を生むだけで、解決よりも多くの問題を引き起こすよ。依存関係のバージョンを制限しないことが基本的にコンセンサスになってるね。

現在のuvのデフォルト設定が正しいと思う。もしそのライブラリがセマンティックバージョニングに従っていることを確認していないなら、次のメジャーリリースでコードが壊れることが分かっているなら、上限を設定するべきじゃない。ロックファイルの更新管理にはCIを使うべきだよ(例えば、dependabotやrenovateみたいに)。ロックファイルを盲目的に更新するのはダメ。依存関係のツリーも大事にしないとね。直接の依存関係だけじゃなくて。著者はPythonがnpmエコシステムと同じように振る舞うと思っているみたいで、同じ教訓が当てはまると思ってるんじゃないかな。