Skip to content

Why Use uv Instead of venv for Python Projects?

Problem

I was starting a new Python project and went through the usual ritual:

Terminal window
python -m venv .venv
source .venv/bin/activate
pip install requests

Then I forgot to add .venv to .gitignore. I committed the entire virtual environment by mistake. My repository size exploded.

I asked on Reddit about best practices for virtual environment setup, and the top comment surprised me:

Don’t waste time managing virtual environments. Learn how to use uv. It takes care of everything for you with virtual environments.

Multiple people echoed the same advice: “Use UV.” I was skeptical. I’d been using venv and pip for years. Why change?

What Happened?

I decided to give uv a try. The first thing I noticed was the difference in workflow.

Traditional venv approach:

Traditional workflow
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ python -m venv │───>│ source activate │───>│ pip install │
│ .venv │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Update gitignore│ │ pip freeze > │
│ add .venv │ │ requirements.txt│
└─────────────────┘ └─────────────────┘

5 separate steps. Different commands on Windows vs Unix for activation. Manual gitignore management.

uv approach:

uv workflow
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ uv init │───>│ uv add │───>│ uv run │
│ │ │ <package> │ │ <script.py> │
└─────────────────┘ └─────────────────┘ └─────────────────┘

3 steps. No activation. No gitignore updates. uv handles everything behind the scenes.

Why This Matters

The problem isn’t just the number of steps. It’s the cognitive overhead:

  1. Manual Setup Overhead: Creating venv, activating it, remembering to deactivate
  2. Platform Inconsistencies: source .venv/bin/activate on Unix, .venv\Scripts\activate on Windows
  3. Gitignore Management: Forgetting to exclude .venv directories happens constantly
  4. Dependency Hell: pip’s slow resolution, lack of lock files by default
  5. Tool Fragmentation: Separate tools for venv, pip, pipx, pyenv, poetry

I was wasting mental cycles on environment setup instead of writing code. The Reddit thread confirmed this is a universal frustration.

The Solution

uv (from Astral, creators of ruff) is an extremely fast Python package installer and resolver written in Rust. It replaces pip, pip-tools, pipx, poetry, pyenv, virtualenv - all in one tool.

Installation

Terminal window
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Or with pip
pip install uv

Basic Workflow

Let me create a new project with uv:

Terminal
# Initialize a new project
uv init myproject
cd myproject
# Add dependencies
uv add requests flask
# Run scripts without activation
uv run python main.py

That’s it. No source .venv/bin/activate. No .gitignore updates. uv created the virtual environment automatically.

What uv Actually Created

Project structure
myproject/
├── .venv/ # Auto-created, auto-managed
├── .python-version # Python version pin
├── pyproject.toml # Project configuration
├── uv.lock # Lock file for reproducibility
└── hello.py

The pyproject.toml file contains my dependencies:

pyproject.toml
[project]
name = "myproject"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"flask>=3.0.0",
"requests>=2.31.0",
]

And uv.lock ensures reproducible builds across machines.

Speed Comparison

The speed difference is dramatic. I tested installing the same requirements:

Benchmark test
# With pip
time pip install -r requirements.txt
# real 0m12.345s
# With uv
time uv pip install -r requirements.txt
# real 0m0.892s

uv was 10-15x faster in my tests. For larger dependency trees, the difference grows to 100x.

Here’s why:

Speed comparison
┌─────────────────────────────────────────────────────────────┐
│ pip install time │
├─────────────────────────────────────────────────────────────┤
│████████████████████████████████████████████████████████████│ 12.3s
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ uv install time │
├─────────────────────────────────────────────────────────────┤
│████ │ 0.9s
└─────────────────────────────────────────────────────────────┘

uv’s Rust implementation and parallel downloading make the difference.

Key Features

Automatic Virtual Environment Management

I don’t need to think about venv anymore:

Terminal window
# uv run automatically uses the project's venv
uv run python script.py
# No need for:
# source .venv/bin/activate
# python script.py

Lock Files by Default

Terminal window
# uv creates uv.lock automatically
uv add pandas
# Lock file ensures consistent installs
uv sync # Reproduces exact environment

Tool Installation (pipx replacement)

Terminal window
# Run tools without installing them
uvx ruff check .
# Install tools globally
uv tool install black

Python Version Management (pyenv replacement)

Terminal window
# Install and use specific Python versions
uv python install 3.11
uv python pin 3.11

Common Mistakes

When I first started using uv, I made these mistakes:

Mistake 1: Still Manually Activating venv

Terminal window
# WRONG - unnecessary with uv
source .venv/bin/activate
python script.py
# CORRECT - just use uv run
uv run python script.py

Mistake 2: Mixing pip and uv

Terminal window
# WRONG - can cause conflicts
uv add requests
pip install flask # Don't do this!
# CORRECT - stick with uv
uv add requests flask

Mistake 3: Ignoring pyproject.toml

Terminal window
# WRONG - fighting the tool
uv add requests
# Then manually editing requirements.txt
# CORRECT - embrace pyproject.toml
uv add requests
# Let uv manage pyproject.toml and uv.lock

Mistake 4: Not Reading the Docs

uv has additional features I missed:

Terminal window
# Tool running without installation
uvx black .
# Sync exact versions
uv sync --frozen
# Export to requirements.txt if needed
uv pip compile pyproject.toml -o requirements.txt

Migration from venv

I migrated my existing projects gradually:

Terminal window
# In existing project directory
uv init # Creates pyproject.toml if missing
uv add $(cat requirements.txt) # Add existing dependencies
# Or for requirements.txt projects
uv pip sync requirements.txt

The uv.lock file ensures my team gets the exact same versions.

When to Stick with venv

uv isn’t always the right choice. Stick with traditional venv if:

  • Your organization has strict tool approval processes
  • You need maximum compatibility with existing CI/CD pipelines
  • Your team lacks Rust runtime support (rare)
  • You’re working on a system where uv isn’t available

But for most Python projects in 2026, uv is the better choice.

Summary

In this post, I explained why uv replaces traditional venv for Python virtual environment management. The key benefits are:

  • No manual venv management: uv handles creation and activation automatically
  • 10-100x faster: Rust implementation makes dependency resolution instant
  • Lock files by default: Reproducible builds without extra configuration
  • Unified tooling: Replaces pip, pip-tools, pipx, poetry, pyenv, virtualenv

The Reddit community was right. I wasted years on manual venv management. uv eliminates that overhead entirely. Three commands - uv init, uv add, uv run - replace the entire traditional workflow.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments