research••4 min read

TIL: Why uv Completely Replaced pip and venv in My Daily Workflow

A practical look at Astral's uv, how it speeds up Python dependency management, and why I stopped managing virtual environments manually

For years, setting up a Python project followed the exact same muscle-memory ritual:

bash
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt

It worked, but it had obvious friction points:

  1. pip install over slow or flaky networks feels sluggish, especially when resolving dependencies for complex scientific or ML libraries like PyTorch or Pandas.
  2. Managing multiple Python runtimes across Windows and WSL often meant juggling pyenv, system packages, or global installers.
  3. Lockfiles with vanilla pip are non-existent unless you bring in heavier tools like poetry or pip-tools, which introduce their own performance overhead and idiosyncrasies.

A few months ago, I switched to uv (built by Astral in Rust). At this point, I haven't run python -m venv or pip install directly in months. Here is what made the difference in practical daily engineering.


1. Instant Virtual Environments

Creating a virtual environment with uv is practically instantaneous:

bash
# Creates a .venv folder in milliseconds
uv venv

Under the hood, uv doesn't just invoke standard library scripts; it uses copy-on-write / hardlinks where supported and initializes virtual environments virtually instantaneously.

If you need a specific Python version that isn't installed on your machine yet, uv handles the download and isolation automatically:

bash
uv venv --python 3.11

You don't need to manually hunt down deadsnakes PPAs in Ubuntu/WSL or download separate Windows installers.


2. Resolving and Installing Packages at Rust Speed

When building local ML experiments or FastAPI microservices, dependency trees can get dense quickly. uv pip replaces pip drop-in:

bash
uv pip install fastapi uvicorn

Because uv features a global caching layer, re-installing dependencies across different local scratchpads or test environments takes seconds instead of minutes. If a wheel was cached previously on disk, uv links it rather than re-downloading or unpacking from scratch.


3. Project Management without the Poetry Bloat

While uv pip is great as a fast drop-in for pip, the real power comes from uv's native project management commands (uv init, uv add, uv run):

Starting a project

bash
uv init my-project
cd my-project

This generates a clean pyproject.toml and .python-version.

Adding dependencies and locking them

bash
uv add fastapi pydantic
uv add --dev pytest

This generates a deterministic uv.lock file. Unlike raw requirements.txt pinning, uv.lock guarantees that every developer and CI runner resolves the exact same sub-dependencies across platforms.

Running scripts without manual activation

Instead of remembering to run source .venv/bin/activate (or .venv\Scripts\activate on Windows PowerShell), you can use:

bash
uv run python main.py
# or run installed CLIs directly:
uv run uvicorn main:app --reload

uv run ensures the virtual environment exists, dependencies are up-to-date, and executes within the isolated context.


4. Ephemeral Tools with uvx

Just like npx in the Node.js ecosystem, uvx lets you run command-line tools in temporary, isolated environments without polluting your global environment or project:

bash
# Run Ruff linting on the current directory without installing Ruff globally
uvx ruff check .

# Spin up a temporary HTTP server or tool
uvx httpie GET https://api.github.com

Practical Trade-offs & Gotchas

Nothing is without trade-offs:

  • Enterprise Proxies / Self-signed Certs: If you are working behind strict corporate proxies with custom SSL roots, you need to configure SSL_CERT_FILE or pass --native-tls so uv correctly trusts internal CA certificates.
  • Legacy setup.py packages: Highly legacy packages that rely on non-standard, custom C-extension build scripts without wheel support can still hit standard compilation hurdles. uv will build them via an isolated build environment, but you still need local build tools (GCC/Clang or MSVC) installed.

Summary

uv is not just a faster pip; it consolidates:

  • Python version management
  • Virtual environment creation
  • Fast, cached dependency resolution
  • Lockfile generation (uv.lock)
  • Single-command execution (uv run)

If you are starting any new Python service, MLOps pipeline, or local AI script, skip the standard venv boilerplate and give uv a spin.

Technical Discussion (0)

Ask a question or reply to a thread below. Your email is never displayed publicly.

No comments yet. Start the technical discussion above.