Why virtualenvs exist
Without isolation, every pip install goes into the system Python. Two projects that need different versions of the same library can't coexist. Virtual environments give each project its own isolated site-packages directory — install whatever you want, no conflicts.
venv — the stdlib answer
python -m venv .venv creates a virtualenv in .venv/. Activate with source .venv/bin/activate (Unix) or .venv\Scripts\activate (Windows). After activation, python and pip point at the venv's binaries. Deactivate with deactivate. This is the standard, ships with Python, and works everywhere.
uv — the modern fast alternative
uv (from Astral) is a Rust-written drop-in for pip + venv that's 10-100x faster. uv venv creates an env. uv pip install foo installs into it. Pippa's stack uses uv. The tradeoff: it's still maturing relative to pip, and pip is the universally available baseline. Use uv when you can; fall back to pip when you can't.
conda — separate ecosystem
conda manages both Python interpreters and packages, and supports non-Python dependencies (CUDA, BLAS, system libraries). Useful for data science, ML, anywhere you need pre-built binaries with system-level deps. Pippa runs in a conda env (cwk-pippa) for exactly this reason. Use conda when you need it; use venv/uv when you don't.
pip install into system Python. The cost of activation is ~1 second; the cost of dependency hell from skipping it is hours.
Environment declarations are part of the contract. Commit the project and lock files needed for reproduction, but not the installed environment directory itself. Share the declaration; rebuild the artifact on each machine.