uv has won the Python packaging speed war. With installs running 10-100x faster than pip, OpenAI’s backing after the Astral acquisition in March 2026, and 126 million downloads per month, the momentum is real. For most new Python projects in 2026, uv is the right default. However, a Hacker News discussion this week put into words what many developers quietly hit once they move past project setup and into actual maintenance: uv’s package manager UX feels like it was built for resolvers, not for humans.
This isn’t a hit piece. Speed matters enormously, and uv delivers. But ergonomics matter too, and three specific gaps in uv’s day-to-day workflow deserve honest attention from every Python developer relying on it in production.
The Missing uv outdated Command
The most basic daily question in package maintenance is simple: what needs updating? In Poetry, one command answers it cleanly:
# Poetry — filtered, clean output:
poetry show --outdated
In uv, there is no uv outdated. The closest equivalent is uv tree --outdated --depth 1 — a command that does not show you outdated packages. It shows your entire dependency tree, with small annotations next to the packages that have updates available. If your project has 50 direct dependencies and two are outdated, you scan 50 lines to find two annotations. As Kevin Renskers noted on Loopwerk.io, “the output doesn’t just show what is outdated; it shows your entire top-level dependency tree.”
Moreover, this is a friction point that compounds with project size. A 10-dependency project is manageable. A 60-dependency production application is not. A dedicated uv outdated with filtered output is the obvious fix, and the community has been asking for it consistently.
Unsafe Default Version Constraints
Here is where the stakes get meaningfully higher. When you add a package with uv add pydantic, the resulting constraint written to pyproject.toml looks like this:
# uv default — no upper bound:
[project]
dependencies = [
"pydantic>=2.13.4",
]
Compare that to Poetry’s default behavior:
# Poetry default — caret constraint, safe:
[tool.poetry.dependencies]
pydantic = "^2.13.4" # equivalent to >=2.13.4,<3.0.0
Poetry’s caret constraint means poetry update will never pull in pydantic v3 without an explicit opt-in. uv’s default means that if you run uv lock --upgrade and pydantic v3 has shipped, your lockfile just got a potentially breaking major version — silently. According to the uv official documentation, the default is a lower-bound-only constraint by design.
uv maintainer Armin Mitsuhiko addressed this directly in the HN thread: “you cannot know today if your package is going to be compatible with a not-yet-released package.” That’s a principled position about resolution semantics. It doesn’t, however, make the default safe for teams running routine upgrades on production codebases. The risk is not theoretical — every major package in the Python ecosystem ships breaking changes in major releases.
A workaround exists: uv add pydantic --bounds major produces pydantic>=2.13.4,<3.0.0, which provides the upper-bound safety you’d expect. But the flag is experimental, opt-in, and must be specified for every package you add. Most developers will never know it exists.
Verbose Upgrade Command Syntax
Updating specific packages in uv requires a flag that must be repeated for every package you want to upgrade:
# Poetry — intuitive, readable:
poetry update pydantic httpx
# uv — verbose, repetitive:
uv lock --upgrade-package pydantic --upgrade-package httpx
Furthermore, uv lock --upgrade without specifying packages is a nuclear option — it upgrades every single package in your lockfile to absolute latest, bypassing whatever constraint ranges you have. There is no equivalent of a “safe upgrade” that respects caret constraints as a first step. The distinction between “update lockfile” and “update requirements” is also not obvious to new users, requiring a documentation dive to confirm behavior. According to the Scopir Python Package Manager comparison for 2026, this ergonomic gap is the most common friction point cited by teams migrating from Poetry to uv.
What to Do Right Now
These problems are solvable and uv’s pace of development suggests they will be fixed. The --bounds major flag is evidence the team is listening. In the meantime, here are the practical steps for teams using uv in production today.
- Use
--bounds majorfor everyuv addif you want safe major-version constraints. Until this becomes the default, treat it as mandatory. - Never run
uv lock --upgradeblindly. Checkuv tree --outdated --depth 1first, identify which packages need updating, then upgrade them individually withuv lock --upgrade-package <name>. - Keep uv for what it genuinely excels at: Python version management, virtual environment creation, CI/CD pipeline sync, and raw installation speed. The speed gains in pipelines alone justify adoption.
- Consider staying on Poetry for projects where maintenance-phase ergonomics are a daily concern and your team isn’t prepared to carry the
--bounds majoroverhead until uv makes it the default.
uv is the future of Python packaging — the Rust-based resolver, the unified toolchain story, and now OpenAI’s resources behind it all point in one direction. However, the maintenance UX is still catching up to the install UX. Go in with eyes open, apply the workarounds, and watch the changelog. These gaps likely won’t be gaps for long.
Key Takeaways
- uv has no
uv outdatedcommand —uv tree --outdated --depth 1is noisy and shows the full dependency tree - Default constraints from
uv addhave no upper bound — runninguv lock --upgradecan silently pull in breaking major versions - Use
uv add <package> --bounds majorfor safer, Poetry-style version constraints - Upgrade individual packages with
uv lock --upgrade-package <name>instead of bulk upgrades - uv remains the right default for new projects in 2026 — these are fixable gaps, not dealbreakers












