The Isolation Bubble

Virtual environments: how to prevent Python projects from tearing each other apart.

Fundamentals 13 min Beginner April 26, 2026

You know how to install packages with pip. Everything works — until one day you update NumPy for a new project and your old TensorFlow project refuses to start. You didn't change a single line of code, but something invisible broke.

Welcome to Dependency Hell: the moment when shared package versions clash across projects. This article introduces two tools that prevent this nightmare — virtual environments for Python-level isolation and Docker containers for full system-level isolation.

Dependency Hell — When Packages Clash

Dependency Conflict

AnalogyDefinition
Imagine a large workshop with multiple workbenches. Workbench A is for building a delicate architectural model — precision glue and clean surfaces. Workbench B is for repairing a motor — oil, grease, and heavy tools. If all tools go into one shared shelf, the motor oil eventually contaminates your model parts. That is what happens in global site-packages: one project updates NumPy "for itself," but it actually affects every project.

In a real workshop, you see the chaos immediately. In Python, conflicts hide until a runtime import crashes with a cryptic error message. Also, in a workshop you could theoretically have two versions of the same tool side by side — in one Python environment, only one version of numpy can be active.

Example: TensorFlow vs. NumPy

You try to install TensorFlow 2.12 and a newer NumPy version:

pip install tensorflow==2.12.0 numpy==1.24.2

ERROR: tensorflow 2.12.0 requires numpy<1.24,>=1.22
but you have numpy 1.24.2 which is incompatible.

Even worse: Project A uses TensorFlow with numpy==1.23. Then you install PyTorch for Project B with numpy==2.1. The global installation overwrites Project A's version. Next time Project A starts: crash — "module compiled using NumPy 1.x cannot run in NumPy 2.x."

Three Levels of Isolation

Docker Container Isolated OS + Python + packages — full reproducibility
Virtual Environment Isolated Python packages per project — standard solution for development
Global (Chaos) All projects share one site-packages — version conflicts inevitable

Virtual Environments — One Bubble Per Project

Virtual Environment (venv)

AnalogyDefinition
Each workbench gets an invisible plastic wrap. Everything you put on your desk (packages installed via pip) stays inside your wrap. Your neighbor's desk has its own wrap — no oil, no glue, no stray screws. When a project is done or misconfigured, you throw away the wrap and everything on it and start fresh.

The plastic-wrap analogy works well: isolation and easy cleanup. But a venv only isolates Python packages — not the operating system, system libraries, or GPU drivers. And requirements.txt is more precise than a wrap — it is an exact inventory with version numbers.

The venv Workflow in 5 Steps

1
python -m venv ai_project — Creates the environment directory
2
source ai_project/bin/activate (Linux/macOS) or ai_project\Scripts\activate (Windows) — Activates it (prompt changes to (ai_project))
3
pip install transformers — Installed only in this environment
4
pip freeze > requirements.txt — Saves exact versions
5
deactivate — Returns to global Python

Reproducibility with requirements.txt

pip freeze outputs an exact list of all installed packages with version numbers:

pip freeze > requirements.txt

# Contents of requirements.txt:
transformers==4.37.0
tokenizers==0.15.0
numpy==1.26.3
...

Another developer can use pip install -r requirements.txt to build exactly the same environment on their machine. This is how reproducibility works in Python projects.

Warning: Never pip install Without an Active venv

Without an active virtual environment, pip installs globally — every package goes into the shared site-packages and affects all projects. Before every pip install, check that your prompt shows (ai_project). When in doubt: which python (Unix) or where python (Windows) — the path should point to your venv directory.

Interactive: Before vs. After

Toggle between the two scenarios: on the left, see how a global installation leads to version conflicts. On the right, the same workflow with virtual environments — each project gets its own bubble, no package interferes with any other. Pay attention to the error message on the left versus the clean training start on the right.

Global Installation (Chaos)
Terminal
$ pip install tensorflow==2.12.0
Successfully installed numpy-1.23.5 tensorflow-2.12.0
 
# 3 months later: new project
$ pip install pytorch numpy==2.1.0
Successfully installed numpy-2.1.0 pytorch-2.3.0
 
# Back to the old project...
$ python train.py
ModuleNotFoundError: module compiled using NumPy 1.x
cannot run in NumPy 2.x
ERROR: tensorflow 2.12.0 requires numpy<1.24
Both projects share a global site-packages. The update for Project B breaks Project A.
Without venv: Shared Chaos
site-packages (global)
numpy 2.1.0tensorflow 2.12.0pytorch 2.3.0
Project A
needs numpy<1.24
Project B
needs numpy>=2.0

Docker — The Shipping Container for Code

Docker

AnalogyDefinition
Before standardized shipping containers existed, dock workers had to load sacks, crates, and barrels individually — each shaped differently. Today, everything goes into a standard steel container. The ship doesn't care whether there are cars, grain, or machines inside — it just transports containers. Docker works the same way: the developer packs the entire setup (OS, Python, libraries, code) into a container, and the server just starts and stops containers.

A shipping container has its own physical walls; Docker containers share the host's kernel — they are lighter but also more dependent. Cloning a physical container requires time; Docker images are copied and pushed to registries in seconds. And shipping containers don't start in seconds — Docker containers do because there is no boot process.

Virtual Environment (venv)

Isolates Python packages only. Lightweight, built into Python since 3.3, no extra tool needed. Ideal for everyday development. Does not isolate the operating system or system libraries.

Docker Container

Isolates the entire system: OS, libraries, GPU drivers, Python, and packages. Heavier, requires Docker installation. Ideal for deployment, collaboration, and specific GPU/CUDA setups.

Example: Minimal Dockerfile for an AI Project

FROM python:3.11
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "train.py"]

Two commands to build and run — works identically on any machine with Docker:

docker build -t my_ai_project .
docker run --rm my_ai_project

Misconception: Docker Is a Virtual Machine

Docker containers share the host system's kernel — they do not boot their own operating system. That is why they start in seconds instead of minutes. They are much lighter than VMs. And Docker does not replace venv: in practice, both are often combined — Docker for the OS level, venv for the Python level inside the container.

There is an important difference between pinned and unpinned versions:

# Pinned (reproducible):
numpy==1.24.4
transformers==4.37.0

# Unpinned (NOT reproducible!):
numpy
transformers

Unpinned means: "install the latest version." What is latest today is different tomorrow. pip freeze captures all versions exactly, including transitive dependencies. For cleaner files, you can alternatively maintain only top-level packages by hand — but always with versions.

In AI production, containers are the standard. Why? Because CUDA/cuDNN versions must match exactly — containers solve this.

Hugging Face offers Docker Spaces for deploying any framework. AWS SageMaker provides pre-built containers for TensorFlow and PyTorch, and supports custom ones. GPU-accelerated containers with NVIDIA CUDA are the only reliable way to make deep learning setups reproducible.

When you build and deploy AI models in later articles, Docker will be your constant companion.

Key Takeaways

  1. Dependency Hell happens when two projects need different versions of the same package but share one global Python installation. The solution: give each project its own environment.
  2. python -m venv project_name creates an isolated Python environment. source project_name/bin/activate (Linux/macOS) or project_name\Scripts\activate (Windows) switches into it.
  3. pip freeze > requirements.txt saves exact package versions. pip install -r requirements.txt reproduces the environment on another machine. Always pin versions.
  4. A virtual environment isolates Python packages only. Docker isolates the entire system: OS, libraries, GPU drivers, Python, and packages — all in one portable container.
  5. Use venv for everyday development. Use Docker when you need full reproducibility across machines, deploy to servers, or require specific GPU/CUDA setups.

Quiz: Virtual Environments & Docker

Question 1 / 4
Not completed

What is "Dependency Hell"?

Select one answer
Answer Key: 1) B · 2) B · 3) C · 4) B

Checkpoint

  • What happens when two projects need different versions of the same library and both are installed globally, without a virtual environment?
  • What does the command python -m venv my_project do to your Python setup and package management?
  • What is the main difference between a Python virtual environment and a Docker container in terms of what they isolate?