If you’ve been writing Python for any length of time, you’ve accumulated opinions about the toolchain. Probably strong ones. The pip-plus-virtualenv setup that most tutorials teach you is workable but clunky. Then there’s pyenv for managing Python versions, poetry or pip-tools if you want reproducible installs, pipx for tools you want globally available — and somewhere along the way you’ve ended up with four different tools doing vaguely related jobs and a README that starts with “first, make sure you have Python 3.11 installed via pyenv.”

uv changes this. It’s a single tool, built in Rust by the team at Astral (who also make Ruff, the Python linter), and it replaces the whole lot. It’s also significantly faster than any of the tools it’s replacing — 10–100x on typical operations, not marginal improvements.

What uv actually replaces

The scope is broader than it first sounds:

  • pip — package installation, uv pip install
  • pip-tools — dependency resolution and lockfile generation
  • virtualenv / venv — creating isolated Python environments
  • pyenv — managing multiple Python versions (uv python install 3.12)
  • pipx — running tools in isolated environments (uvx ruff check .)
  • poetry / PDM — project management with pyproject.toml, lockfiles, and publishing

You don’t have to use all of these at once. If you just want a faster pip, you can use uv pip install as a drop-in and stop there. But the project management features are where it gets genuinely useful.

Why it’s fast

Rust is part of the answer, but the bigger factor is the dependency resolver. uv uses a resolver derived from PubGrub (the same algorithm Dart’s pub uses), which is designed to be both correct and fast. Combined with a shared global package cache across projects, repeated installs — especially in CI — are dramatically faster because packages are often already cached.

In practice: creating a fresh virtual environment with a typical data science stack (numpy, pandas, matplotlib, scikit-learn) takes a few seconds with uv. The same operation with pip takes considerably longer because each package is downloaded and installed in sequence. uv downloads in parallel and caches aggressively.

The basics

Install uv (the recommended approach is their installer script, or via pip if you prefer):

curl -LsSf https://astral.sh/uv/install.sh | sh

Starting a new project:

uv init my-project
cd my-project
uv add requests httpx

That creates a pyproject.toml, a uv.lock file, and a .venv directory. The lockfile pins every dependency — direct and transitive — so your installs are reproducible. uv sync brings the environment back to exactly the locked state, which is what you want in CI.

Running scripts:

uv run python main.py
# or
uv run pytest

uv run automatically uses the project’s virtual environment without you having to activate it first. Less activation ceremony is one of those quality-of-life improvements that sounds small until you stop doing it.

Python version management

This was the part that surprised me most. You no longer need pyenv:

uv python install 3.12
uv python install 3.11 3.10  # install multiple at once
uv python list                # see what's installed and available

In a project, you can pin the Python version:

uv python pin 3.12

This writes a .python-version file. uv respects it. If you’re working across multiple projects with different Python requirements, uv handles the switching automatically without you needing to remember which version you’re on.

uvx for one-off tool execution

uvx is the equivalent of pipx run — it lets you run a Python tool in an isolated environment without installing it globally:

uvx ruff check .
uvx black --check .
uvx pytest --version

This is particularly useful in CI pipelines where you want a specific version of a tool without it polluting your project’s dependencies. It’s also handy for trying out tools without committing to installing them.

The lockfile situation

uv.lock is cross-platform by default — it pins packages for all supported platforms, not just the one you’re currently on. This matters if you’re developing on a Mac and deploying to Linux: the lockfile will work on both without modification. Poetry’s lockfiles can have platform-specific issues; uv handles this at the resolver level.

Should you switch?

If you’re starting a new project: yes, just use uv from the beginning. The setup is straightforward, the tooling is cohesive, and you’ll avoid the “which version of Python am I in right now?” confusion.

If you have an existing project using poetry or pip-tools: migration is reasonably painless. uv can import existing requirements.txt files and pyproject.toml configurations. The main thing to check is that your lockfile gets regenerated (uv.lock is a different format from poetry.lock), and that CI is updated to use uv sync rather than whatever you were using before.

If you’re a solo developer who hasn’t been using lockfiles at all and has been relying on pip install -r requirements.txt: this is actually the clearest win. Start using uv add instead of pip install, and you get proper lockfiles and reproducible environments without having to learn a complex new configuration format.

The one thing uv doesn’t fully replace yet is the publishing workflow — if you’re maintaining a public package on PyPI and need twine upload or equivalent, that’s still a separate step. The Astral team has indicated this is coming, but it’s not there yet.

For day-to-day Python development though, it’s already complete. One tool, faster than everything it replaces, with a lockfile format that actually works.