2026 年 5 月 22 日:這篇文章登上了 Hacker News 首頁。讀者指出了我之前忽略的幾個細節,以及我應該更清楚說明的一些觀點。請參見文末的更正與澄清部分。

Astral 的 uv 工具在 Python 世界掀起了風潮,這是有原因的。它速度極快,輕鬆管理 Python 版本,並用一個二進位檔取代了多達半打的工具。我之前也寫過多篇相關文章。

使用 uv 開始一個新的 Python 專案並加入第一批依賴套件非常簡單。但一旦超過初期設定,進入專案維護階段,也就是檢查過時套件和執行例行升級時,uv 的命令列介面相比 pnpm 或 Poetry 等同類工具,顯得相當笨重。

在我的 JavaScript 專案中,如果我想查看需要更新的套件,我會執行:

這會清楚簡潔地列出過時套件、當前版本、最新版本以及符合限制條件的版本。

而在 uv 中,沒有 uv outdated 這個指令。你必須記住以下冗長的指令:

輸出結果也是問題所在。它不僅顯示過時套件,還會列出整個頂層依賴樹,並在有更新的套件旁標註。如果你有 50 個依賴套件,只有兩個過時,你仍然得掃描 50 行的清單。

Poetry 的 poetry show --outdated 也不怎麼好,但至少只顯示真正過時的套件。

這是 uv 與 pnpm 和 Poetry 最大的哲學差異,也是對生產穩定性來說相當危險的差異。

當你用 pnpm add 新增套件時,它會用插入符號(^1.23.4)寫入 package.json。插入符號表示允許任何 1.x.x 版本,但不會升級到 2.0.0。

Poetry 預設也類似,使用格式如 >=1.23.4,<2.0.0 。我覺得這不如 ^1.23.4 易讀,但效果相同。

這兩者的更新預設都是安全的。你每天早上執行 pnpm update 或 poetry update,都能有高度信心不會因重大 API 變更而破壞建置(前提是你依賴的套件遵守 SemVer)。

但當你執行 uv add pydantic 時,它會在 pyproject.toml 中插入:

注意缺少上限。在 uv 看來,pydantic 版本 2、3 或 100 都是完全可接受的。

這表示 uv 的更新預設是不安全的。如果你執行批次更新,不只是拿到錯誤修正,而是接受了依賴圖中每個維護者發佈的所有破壞性變更。

實際執行更新的 uv 指令感覺像是為機器設計,而非人類。

在 pnpm 或 Poetry 中,更新全部套件只需簡單的 pnpm update 或 poetry update 指令。而在 uv,你得使用:

為什麼不直接是 uv update 或 uv upgrade?這命令列介面是誰設計的?它也不是 uv lock --add 或 uv lock --remove!

由於前述「無上限」問題,uv lock --upgrade 是核武級選項。它會將鎖定檔中所有套件升級到最新版本,無視 SemVer 安全性。這還包括你從未聽過的深層巢狀依賴!祝你好運,希望沒有任何破壞性變更。

一旦你意識到這太冒險,你會想只升級特定套件。經過搜尋 uv tree --outdated --depth 1 的不佳輸出找到它們後,語法變得重複且繁瑣。

每個套件都要重複使用 --upgrade-package 標誌,當你想更新一堆套件時,這是個大麻煩。我不明白為什麼 uv 的指令使用體驗會這麼差。

幸運的是,uv 最近引入了 uv add 的 --bounds 選項:

這會產生我們期望的較安全約束 pydantic>=2.13.4,<3.0.0 。不過這目前是選用功能,你必須每次記得輸入,且目前仍屬預覽階段。

在 --bounds major(或類似設定)成為預設行為之前,uv 使用者基本上只能在兩個壞選項中抉擇:

我喜歡 uv。它的速度具有變革性,管理 Python 工具鏈的方式無與倫比。但作為套件管理器,專案維護的開發者體驗目前比之前的工具退步。

我們需要一個專門的 uv outdated 指令來過濾雜訊,一個更符合人體工學的更新指令,不需重複標誌,還有尊重語義版本控制的預設版本約束。

在此之前,我會對鎖定檔的每一行變更保持高度懷疑並仔細檢查。

這篇文章登上 Hacker News 後,讀者指出我忽略了兩點,以及一個我應該更清楚說明的觀點。

使用 uv pip list --outdated 取代 uv tree --outdated --depth 1 。uv pip 指令實際上只會過濾出過時套件,這使得「尋找過時套件」的批評比我說的弱很多。剩下的抱怨是這指令屬於 pip 相容命名空間,而非一級頂層指令,這是可發現性問題,不是輸出雜訊問題。

你可以在 pyproject.toml 中設定 --bounds 預設值。你不必每次 uv add 都記得輸入 --bounds major 。你可以設定一次:

這推翻了「兩個壞選項」的說法。實際情況是:設定一次後,從此有合理的預設。它仍是預覽功能,對應用程式來說最好成為預設,但使用體驗並不像我之前描述的那麼糟。

範疇:應用程式與函式庫。Python 標準包裝建議是,發佈到 PyPI 的函式庫不應該設定上限,這建議是正確的。如果每個函式庫都設定上限,下游使用者會遇到無法解析的依賴樹。但對於應用程式,你是依賴圖的終端節點,沒有人會依你的限制解析,情況就反過來:上限不會帶來成本,卻能保護你免於意外的重大版本跳躍。本文談的是維護應用程式(網站、服務、內部工具),不是發佈函式庫。我應該一開始就明確說明,因為「無上限」預設對函式庫來說確實合理。