Virtual Environments, pip and Dependency Management

Python’s virtual environment system solves a fundamental problem: different projects may require different versions of the same package, and installing everything globally leads to version conflicts. A virtual environment is an isolated Python installation โ€” it has its own copy of pip and its own site-packages directory. Every Python project you work on should have its own virtual environment. For FastAPI, this means a clean, reproducible environment where fastapi, sqlalchemy, pydantic, and all other dependencies are version-pinned and isolated from other projects on your machine.

Creating and Activating Virtual Environments

# Create a virtual environment in the 'venv' directory
python3 -m venv venv

# Activate โ€” changes your shell to use venv's Python and pip
# macOS / Linux:
source venv/bin/activate
# Windows:
venv\Scripts\activate

# Verify you're in the venv โ€” prompt changes to (venv)
which python   # /path/to/project/venv/bin/python
pip --version  # pip X.Y.Z from /path/to/project/venv/...

# Deactivate โ€” return to global Python
deactivate

# Tip: name the directory 'venv' or '.venv' โ€” both are conventional
# .venv is preferred for modern projects (hidden directory)
python3 -m venv .venv
source .venv/bin/activate   # same process
Note: Always add venv/ and .venv/ to your .gitignore โ€” virtual environment directories are large (hundreds of MB) and machine-specific. They should never be committed to version control. Instead, commit your requirements.txt (or pyproject.toml) which lists the packages needed to recreate the environment. Any developer can run pip install -r requirements.txt to get an identical environment.
Tip: For FastAPI projects, use two requirements files: requirements.txt for production dependencies (fastapi, sqlalchemy, pydantic, etc.) and requirements-dev.txt that starts with -r requirements.txt and adds development-only packages (pytest, httpx, black, ruff, mypy). This separation keeps production images lean while development environments have all tooling available.
Warning: Never install packages into your global Python installation for project work. Global installs pollute all projects on your machine and create version conflicts that are extremely difficult to debug. The symptom: code that works on your machine fails on a colleague’s or in production because they have different versions of a package globally installed. Always use virtual environments, always.

pip โ€” Installing and Managing Packages

# Install a package
pip install fastapi
pip install "fastapi[all]"         # with optional extras (uvicorn, etc.)
pip install "sqlalchemy>=2.0,<3.0" # version constraint

# Install from requirements file
pip install -r requirements.txt
pip install -r requirements-dev.txt

# Upgrade a package
pip install --upgrade fastapi

# Uninstall
pip uninstall fastapi

# Show installed packages
pip list                 # all installed packages
pip freeze               # name==version format (for requirements.txt)
pip show fastapi         # info about a specific package

# Generate requirements.txt from current environment
pip freeze > requirements.txt

# Check for security vulnerabilities
pip audit   # pip 23.0+ has built-in audit

requirements.txt for FastAPI Projects

# requirements.txt โ€” production dependencies
fastapi==0.111.0
uvicorn[standard]==0.29.0
sqlalchemy==2.0.30
alembic==1.13.1
pydantic==2.7.1
pydantic-settings==2.2.1
psycopg2-binary==2.9.9       # PostgreSQL driver
python-jose[cryptography]==3.3.0   # JWT
passlib[bcrypt]==1.7.4        # password hashing
python-multipart==0.0.9       # file uploads
httpx==0.27.0                 # async HTTP client (for tests too)
python-dotenv==1.0.1          # .env file support

# requirements-dev.txt โ€” development + test dependencies
-r requirements.txt           # include all production deps first
pytest==8.2.0
pytest-asyncio==0.23.7
black==24.4.2                 # code formatter
ruff==0.4.4                   # fast linter
mypy==1.10.0                  # static type checker

Modern: pyproject.toml

# pyproject.toml โ€” modern all-in-one project configuration
# Replaces setup.py, setup.cfg, and partly requirements.txt

[project]
name = "blog-api"
version = "1.0.0"
description = "FastAPI blog application"
requires-python = ">=3.11"
dependencies = [
    "fastapi>=0.111.0",
    "uvicorn[standard]>=0.29.0",
    "sqlalchemy>=2.0.0",
    "alembic>=1.13.0",
    "pydantic>=2.7.0",
    "pydantic-settings>=2.2.0",
    "psycopg2-binary>=2.9.9",
    "python-jose[cryptography]>=3.3.0",
    "passlib[bcrypt]>=1.7.4",
    "python-dotenv>=1.0.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0.0",
    "pytest-asyncio>=0.23.0",
    "black>=24.0.0",
    "ruff>=0.4.0",
    "mypy>=1.10.0",
]

[tool.black]
line-length = 88

[tool.ruff]
line-length = 88
select = ["E", "F", "I"]

[tool.mypy]
python_version = "3.11"
strict = true

Common Mistakes

Mistake 1 โ€” Installing packages globally instead of in venv

โŒ Wrong โ€” forgetting to activate the venv:

pip install fastapi   # installs globally if venv not activated

โœ… Correct โ€” always activate first:

source venv/bin/activate
pip install fastapi   # installs into venv โœ“

Mistake 2 โ€” Pinning to patch version when minor version constraint is enough

โŒ Wrong โ€” overly strict pinning causes update friction:

fastapi==0.111.0   # exact pin โ€” will miss security patches

โœ… Better โ€” pin to compatible version:

fastapi>=0.111.0,<0.112.0   # or use ~=0.111.0 (compatible release)

Mistake 3 โ€” Committing venv to git

โŒ Wrong โ€” venv directory in the repository:

git add venv/   # adds hundreds of MB of binary files to git

โœ… Correct โ€” add to .gitignore:

# .gitignore
venv/
.venv/
__pycache__/
*.pyc
.env

Quick Reference

Task Command
Create venv python3 -m venv .venv
Activate (Mac/Linux) source .venv/bin/activate
Activate (Windows) .venv\Scripts\activate
Install package pip install package-name
Install from file pip install -r requirements.txt
Save requirements pip freeze > requirements.txt
Deactivate deactivate

🧠 Test Yourself

A new developer joins your FastAPI project. What is the correct sequence of commands to set up their development environment from the repository?