data codes through eyeglasses

Python Virtual Environments: A Practical Guide to venv

If you’ve been writing Python for a while and haven’t used virtual environments consistently, there’s a good chance you’ve hit the moment where two projects need different versions of the same library and suddenly nothing works as expected. Virtual environments solve this problem completely – they’re isolated Python environments where each project gets its own copy of Python packages, entirely separate from your system installation and from each other.

This guide covers the why, the how, and the practical workflow that makes virtual environments a natural part of Python development rather than an extra step you forget half the time.


Why Virtual Environments Exist

Python’s package manager, pip, installs packages globally by default – into your system Python installation, shared across every project on your machine. This creates problems quickly:

Project A uses Django 4.2. You start Project B which needs Django 5.0 for a new feature. Upgrading Django for Project B potentially breaks Project A. There’s no clean way to have both versions installed simultaneously in a global environment.

Virtual environments sidestep this entirely. Each project gets a venv directory containing its own Python interpreter and package library. Activating that environment makes it the active Python for your shell session. Packages you install with pip go into the project’s environment, not the global one. Switch to a different project, activate its environment, and you’re in a completely separate package space.


Creating a Virtual Environment

The venv module is built into Python 3 – no installation needed. From your project directory:

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

This creates a venv/ directory in your project. Inside it you’ll find a Python interpreter, a pip installation, and the standard library – all self-contained. The conventional name is venv or .venv (the dot makes it hidden on Unix systems). Either works; .venv is increasingly common because it keeps the directory out of file listings while still being clearly present.

# Alternative: use .venv (hidden directory)
python3 -m venv .venv

Activating and Deactivating

Creating the environment doesn’t activate it. You need to activate it explicitly, which changes your shell’s PATH to point at the environment’s Python and pip binaries:

# macOS and Linux
source venv/bin/activate

# Windows (Command Prompt)
venvScriptsactivate.bat

# Windows (PowerShell)
venvScriptsActivate.ps1

Once activated, your shell prompt changes to show the environment name – typically (venv) at the beginning. Any python or pip command now refers to the environment’s versions, not the global ones.

# Check which Python you're using (should show your venv path)
which python

# Install a package into the environment
pip install requests

# Deactivate when done
deactivate

Deactivating returns you to your system Python. The environment still exists and all installed packages are preserved – you just need to activate it again next time.


requirements.txt: Sharing Your Environment

The venv directory itself shouldn’t go into version control – it’s large, platform-specific, and easily regenerated. Instead, you record your project’s dependencies in a requirements.txt file that other developers (or other machines) use to recreate the same environment.

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

# Install from requirements.txt into a fresh environment
pip install -r requirements.txt

pip freeze outputs every installed package with its exact version – a complete snapshot of your environment. Commit requirements.txt to Git. Add venv/ or .venv/ to your .gitignore.

# .gitignore entry
venv/
.venv/

The Standard Workflow

Once this pattern is established, it becomes automatic. For any new Python project:

# 1. Create your project directory and navigate into it
mkdir my-project && cd my-project

# 2. Create the virtual environment
python3 -m venv .venv

# 3. Activate it
source .venv/bin/activate

# 4. Install your dependencies
pip install flask sqlalchemy pytest

# 5. Freeze them
pip freeze > requirements.txt

# 6. Add .venv to .gitignore and commit requirements.txt
echo ".venv/" >> .gitignore
git init && git add . && git commit -m "Initial commit"

When someone else clones the project:

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

They get an identical environment – same package versions, same dependencies, no conflicts with anything else on their machine.


Virtual Environments and Claude Code

If you’re using Claude Code for Python projects, virtual environments matter for the same reason they matter for any Python developer – but Claude Code also needs to know they’re there. In your CLAUDE.md, specify the virtual environment activation step as part of your project setup instructions:

## Setup
This project uses a Python virtual environment.
Activate with: source .venv/bin/activate
Install dependencies: pip install -r requirements.txt
Run tests: pytest

Claude Code reads this and handles environment setup correctly when running commands in your project, installing new packages into the right place rather than globally.


Beyond venv: When to Consider Alternatives

venv is the right starting point for most Python projects. As you get deeper into Python development you’ll encounter tools that build on or replace it: pipenv combines environment creation and dependency management into one tool; poetry adds sophisticated dependency resolution and packaging; uv (from Astral, the makers of Ruff) is a significantly faster drop-in replacement for pip and venv written in Rust. All of these work with the same fundamental concept – isolated environments – but add different workflows on top.

Start with venv and requirements.txt. The concepts transfer directly to any of the more sophisticated tools when you need them.


Virtual environments are one of those things that feel like overhead until you’ve been burned by dependency conflicts once. After that, activating a virtual environment before touching any Python project becomes muscle memory – the difference between a clean, reproducible development environment and the accumulated chaos of a global Python installation that’s been modified by a hundred different projects over time.


Leave a Reply