I have destroyed more Python environments than I care to admit. Random pip install commands, global site-packages contamination, that one project that absolutely needs Django 3.2 while everything else is on 4.x. Every Python developer has been there.

Virtual environments are the answer. But which one? venv? virtualenv? pipenv? poetry? uv? I have tried all of them and I have opinions.

Developer workspace with code on screen
This is what a clean Python setup looks like. ( Most of us don't have one. )

The Problem

Without a virtual environment, every pip install goes into your system Python. That means:

  • Project A needs requests 2.28, Project B needs requests 2.31. You can't have both.
  • One bad install breaks your entire system and you're reinstalling Python from scratch.
  • "Works on my machine" becomes "works on my machine, once, by accident".

This is not a theoretical problem. It will happen to you.

venv ( The Default )

Python ships with venv. No install needed. Here's how it works:

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

That's it. Three commands and you're isolated from the system.

The good: it's built-in, no dependencies, works everywhere. The bad: no dependency locking ( unless you manually generate requirements.txt ), no dependency resolution ( pip just installs the latest compatible version ), and the activate script is easy to forget. I can't count the times I installed packages outside the venv because I forgot to activate it.

Python code on screen
The moment you realize you pip-installed into the system Python. ( Happens to everyone. )

pipenv ( The One That Promised Too Much )

Pipenv was supposed to be the answer. Pipfile + Pipfile.lock, automatic venv creation, dependency resolution. It felt like the Python world was finally catching up to npm and bundler.

The reality: slow dependency resolution, random lock file conflicts, and the project has been in maintenance mode for years. I used it for a year and then gave up. The lock files would break in ways that made no sense, and pipenv install would sometimes take 10 minutes to resolve a simple dependency tree.

Skip it. Seriously.

Poetry ( The Heavyweight )

Poetry does everything. Dependency resolution, lock files, build system, publish to PyPI, virtual environment management, group dependencies. The pyproject.toml format is clean:

[tool.poetry.dependencies]
python = "^3.11"
requests = "^2.31"
flask = {version = "^3.0", optional = true }

[tool.poetry.group.dev.dependencies]
pytest = "^8.0"
ruff = "^0.4"

Poetry is solid for library development and publishing. For most of my projects, it's overkill. I don't need a build system, I don't need to publish anything to PyPI, I just need isolated dependencies. But if you're building a library or a complex project with optional dependency groups, Poetry is worth the setup.

Terminal code screen
Dependencies resolving. ( Please hold. Or don't. We'll be here a while. )

uv ( What I Use Now )

Astral ( the people behind Ruff ) built uv. It's written in Rust. It's fast. Embarrassingly fast. Like, pip install takes 30 seconds, uv pip install takes 2 seconds. No exaggeration.

Here are the commands I actually use:

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

# Create a venv ( yeah, it still uses venv under the hood )
uv venv

# Activate it
source .venv/bin/activate

# Install packages
uv pip install requests flask

# Install from requirements.txt
uv pip install -r requirements.txt

# Export locked dependencies
uv pip freeze > requirements.txt

uv also has project management mode ( uv init, uv add, uv run ) with its own pyproject.toml and lock file. It's basically Poetry but 100x faster and with a much simpler mental model. I've been using it for 6 months and I'm not going back.

What I Actually Do

For quick scripts and one-offs: plain venv. No point installing extra tooling for a 50-line script.

For projects I care about: uv. Fast installs, proper lock files, no nonsense.

For libraries I publish to PyPI: Poetry. The build and publish workflow is still the best for that use case.

Coding setup
Pick one tool and stick with it. ( Or spend three years switching like I did. )

5 Rules That Saved Me

1. Never install globally. Ever. If you catch yourself running pip install without activating a venv first, stop and create one.

2. Add .venv/ to your .gitignore on day one. Not day three after you've already pushed 200MB of compiled C extensions.

3. Pin your dependencies. A requirements.txt with requests without a version number is a time bomb.

4. Use uv pip compile to generate lock files. Reproducible builds aren't optional, they're how you sleep at night.

5. Delete old venvs. If a project hasn't been touched in 6 months, nuke the .venv folder. You can recreate it in seconds from the lock file.

Pick one tool. Learn it. Use it everywhere. Stop switching every 3 months because a blog post told you to. ( Unless that blog post is this one, in which case: switch to uv. )

Thank you :)