Python's UV Package Manager: Why I Switched From pip
Python’s packaging story has been a pain point for decades. pip, virtualenv, pipenv, poetry, conda — the ecosystem has produced more dependency management tools than most languages have web frameworks. When I first heard about UV, my reaction was “not another one.” After six months of daily use, my reaction is “finally.”
What Is UV?
UV is a Python package installer and resolver written in Rust by the team at Astral (the same people behind the Ruff linter). It’s designed as a drop-in replacement for pip and pip-tools, with dramatic speed improvements and better dependency resolution.
The speed difference is not subtle. Installing Django with pip takes about 8 seconds on my machine. UV does it in under 1 second. For projects with hundreds of dependencies, UV completes in seconds where pip takes minutes.
Installation and Basic Usage
Install UV with a single command:
curl -LsSf https://astral.sh/uv/install.sh | sh
Or via pip (ironic, I know):
pip install uv
The basic commands mirror pip’s interface:
uv pip install flask
uv pip install -r requirements.txt
uv pip freeze > requirements.txt
If you’ve used pip, you already know how to use UV for basic tasks. The commands are intentionally familiar.
Virtual Environment Management
UV includes built-in virtual environment management that replaces both venv and virtualenv:
uv venv # Create .venv in current directory
uv venv myenv # Create named environment
uv pip install flask # Install into active venv
This isn’t revolutionary on its own, but having the venv tool and package installer in a single fast binary reduces friction. No more remembering whether you need python -m venv or virtualenv or python3 -m venv.
The Project Management Mode
Where UV really shines is its project management capabilities. Running uv init creates a pyproject.toml and manages your entire project lifecycle:
uv init myproject
cd myproject
uv add flask sqlalchemy
uv add --dev pytest black
uv run python app.py
uv run pytest
The uv add command modifies pyproject.toml and updates the lockfile. The uv run command ensures the virtual environment is created and up-to-date before executing your command. If you’ve used npm or cargo, this workflow feels natural.
The lockfile (uv.lock) captures the exact resolved dependency tree, making builds reproducible across machines. This is what Poetry tried to do, but UV does it faster and with fewer edge cases in my experience.
Dependency Resolution
UV’s resolver is significantly better than pip’s. It handles version conflicts more intelligently and fails with clear error messages when resolution is impossible. Pip’s backtracking resolver can hang for minutes on complex dependency trees before giving up; UV resolves the same trees in seconds.
The resolver also supports override constraints, which let you force specific versions when you know better than the metadata:
uv pip install --override overrides.txt -r requirements.txt
This is useful when a package declares an overly restrictive upper bound on a dependency that you know works fine with a newer version.
Python Version Management
UV can also manage Python installations, replacing tools like pyenv:
uv python install 3.12
uv python install 3.11
uv python list
Having Python version management, virtual environment management, and package installation in a single tool eliminates an entire category of “which tool does what” confusion that plagues Python newcomers.
Compatibility and Limitations
UV is compatible with the standard Python packaging ecosystem. It reads requirements.txt, pyproject.toml, and setup.py files. It publishes to PyPI. It respects the same environment variables and configuration files that pip uses.
The main limitation I’ve encountered is with packages that have complex build requirements. Some scientific computing packages with C extensions or unusual build systems occasionally have issues. These are becoming rarer as UV matures, but if you’re working heavily with numpy, scipy, or similar packages, test your specific workflow before fully committing.
Migration Path
If you’re using pip with requirements.txt, the migration is simple: replace pip with uv pip in your commands and CI scripts. Everything else stays the same.
If you’re using Poetry, the migration requires converting your pyproject.toml from Poetry’s format to standard PEP 621 format. UV provides a migration guide, and the changes are mostly mechanical.
If you’re using conda for scientific computing, UV isn’t a direct replacement. Conda manages non-Python dependencies (C libraries, CUDA toolkits) that UV doesn’t handle. You can use UV within a conda environment, but conda remains necessary for the system-level dependencies.
My Recommendation
For new Python projects, use UV from the start. The uv init workflow is the most streamlined project setup Python has ever had.
For existing projects, try replacing pip with uv pip in your local development and CI. It’s a low-risk change that gives you immediate speed improvements. If that goes well, consider adopting uv add and uv.lock for full project management.
Python’s packaging ecosystem has been fragmented for too long. UV doesn’t solve every problem, but it consolidates the most common workflows into a single, fast, well-designed tool. That’s progress worth adopting.