Without a virtual environment, you share dependencies between projects — leading to version conflicts, non-deterministic builds, and the classic “works on my machine” problem. Project A requires Django 4.2, project B needs Django 5.0, and without isolation they break each other. A virtual environment creates an isolated Python with its own set of packages for each project.
venv — Built-in¶
python -m venv .venv
source .venv/bin/activate # Linux/Mac
.venv\Scripts\activate # Windows
pip install -r requirements.txt
deactivate
The venv module has been part of the standard library since Python 3.3. It creates a .venv directory with a copy of the Python interpreter and an empty site-packages. Activation sets PATH so that python and pip point to the isolated environment. Each project has its own library versions without conflicts with the global installation.
uv — Modern Alternative¶
# Installation
curl -LsSf https://astral.sh/uv/install.sh | sh
# Usage
uv venv
uv pip install flask
uv pip compile requirements.in -o requirements.txt
uv from Astral (the Ruff creators) is written in Rust and offers 10-100x faster package installation than pip. It supports a pip-compatible interface, lock files, and pip-compatible resolution. For new projects, uv is the recommended choice — combining speed with compatibility. Poetry and PDM are alternatives for projects requiring advanced dependency management with their own lock format.
Best Practices¶
- .venv in .gitignore — never commit the virtual environment
- requirements.txt or pyproject.toml — declarative dependencies
- Lock file (uv.lock, poetry.lock) — reproducible builds with exact versions
- One env per project — never share environments between projects
- CI/CD — always create a clean environment from scratch in your pipeline
Key Takeaway¶
Always use a virtual environment for every Python project. uv is the fastest choice for new projects, Poetry or PDM for complex projects with many internal dependencies.