Dependency hell is real. I've lost entire weekends because some package broke everything when I upgraded it. You know the drill: working on a machine learning project, upgrading NumPy to 1.24.3, and watching your web app shit the bed because scikit-learn 0.24.1 couldn't handle the new version.
venv fixes this by giving each project its own isolated environment where it can install whatever versions it wants without fucking up everything else. It's been built into Python since 3.3 so you don't have to pip install yet another tool that might break.
What Actually Happens When You Use venv
When you run python -m venv myproject-env
, Python dumps a folder with:
- A copy of Python (or some symlink bullshit if you're on Linux/Mac - I never remember the difference)
- A
site-packages
folder where pip vomits all your project's dependencies - Activation scripts that lie to your shell about which Python to use
- A
pyvenv.cfg
file with metadata nobody reads
When you activate it, the shell thinks it's using venv Python instead of the system one. The script fucks with your PATH
variable so typing python
doesn't find /usr/bin/python3
anymore - it finds your project's Python instead. Packages get dumped in the venv folder instead of globally where they'd break everything else.
Why venv Doesn't Suck (Unlike Some Alternatives)
It's already there: No extra installation bullshit. If you have Python 3.3+, you have venv. The Python docs say use it, so that's what you should use.
It's fast enough: Takes under a second to create an environment unless you're on a potato computer. None of this "scanning the entire internet for packages" nonsense that some tools do.
It just works: No complex configuration files, no magic, no trying to solve world hunger. It creates a folder, you activate it, you install packages. Done.
Real-World Pain Points (Because Nothing's Perfect)
Forgetting to activate: You'll do this exactly once. Then you'll spend an hour figuring out why your system Python suddenly has Django 4.2 installed globally and your other project is throwing ModuleNotFoundError: No module named 'django.core.wsgi'
errors.
Windows symlink bullshit: Windows throws OSError: [WinError 1314] A required privilege is not held by the client
when trying to create symlinks. Python tries to be smart but Windows is Windows.
Fish shell users get fucked: Unless you remember source .venv/bin/activate.fish
instead of the regular activate script. Fish throws /bin/sh: 1: source: not found
if you use the wrong one. Stack Overflow is full of people who forgot this.
Environments aren't portable: You can't just copy the folder to another machine. The paths are hardcoded. When you need to move stuff, delete the environment and recreate it with requirements.txt
.
The Python 3.13 release added better .gitignore
creation and some other fixes, but the core concept hasn't changed since 2012. It's boring tech that works, which is exactly what you want for managing dependencies.
Framework adoption: Django tells you to use venv, Flask tells you to use venv, pretty much every Python tutorial assumes you're using venv. It's the default for a reason.
Real-world dependency management: James Bennett's "Boring Python" series has the best practical advice on managing Python dependencies without overengineering. The Python subreddit discussions show how complicated people make simple problems. For production environments, AWS has solid guidelines that actually work in the real world.
When things break: Stack Overflow's virtual environment problems catalog shows you're not alone in fighting with activation scripts. The Python discuss forum documents the latest ways Python packaging can break your day.
Now that you understand why venv exists and what problems it solves, you might wonder how it compares to the alternatives. Spoiler: there are many, and they all have opinions about being "better."