Chapter 04 · Section I · 20 min read
Virtual environments and package management
Every project deserves its own clean room. Virtual environments keep each project's dependencies separated so installs do not collide across projects. Five minutes of habit saves countless hours of "but it worked yesterday".
You have installed NumPy, pandas, and matplotlib for your building-ai-notes folder. Now imagine you start a second project — a Telegram bot, say — that needs a library called python-telegram-bot v20. And imagine it pulls in an old version of requests that breaks an experiment you ran last week in building-ai-notes. Suddenly nothing works, and you cannot tell which install broke which thing. This is the problem virtual environments solve. They are not optional gear for serious builders. They are part of the basic kit, and once you learn the routine you stop thinking about it.
The picture: every project, its own room
Without a virtual environment, every pip install you run drops the package into one big shared folder that every Python project on your laptop sees. With a virtual environment, each project gets its own folder of installed packages. Activating the environment is like walking into a clean room with only this project’s tools laid out.
The benefit is enormous: two projects can use different versions of the same library, you can wipe a project’s dependencies without touching anything else, and you can hand someone a requirements.txt file and they can recreate your setup exactly.
Creating one with venv
Python comes with a built-in tool called venv. From your project folder:
python3 -m venv .venv
The -m venv tells Python to run its venv module. The argument .venv is the folder name — by convention, it starts with a dot (which keeps it hidden on Unix) and lives inside the project. After this command, you will see a .venv/ folder appear inside your project.
Now activate it.
On macOS / Linux:
source .venv/bin/activate
On Windows (PowerShell):
.venv\Scripts\Activate.ps1
You will see your terminal prompt change — usually a (.venv) prefix appears. That tells you the environment is active. From now on, pip install puts packages inside .venv/, and python refers to the Python inside .venv/, not the system one.
To leave the environment when you are done for the day:
deactivate
You do not need to deactivate before closing the terminal. Closing the terminal does it for you.
Installing packages into the environment
Once the environment is active, install your dependencies the usual way:
pip install numpy pandas matplotlib jupyter
Everything goes into .venv/. Nothing escapes. To see what is installed:
pip list
To capture the exact versions for sharing:
pip freeze > requirements.txt
That writes a file like:
numpy==2.1.3
pandas==2.2.3
matplotlib==3.9.2
jupyter==1.1.1
Anyone you give this file to (or future-you on a new laptop) can recreate the environment with one command:
pip install -r requirements.txt
Treat requirements.txt as a contract with your future self. Keep it in version control (next section) and update it when you add a dependency.
Telling VS Code about it
VS Code needs to know which Python to use when it runs a notebook cell. After creating the environment:
- Open the project folder in VS Code.
- Open the command palette (
Cmd+Shift+P/Ctrl+Shift+P). - Search for “Python: Select Interpreter” and pick it.
- From the list, choose the one labelled with your project’s
.venvpath — it usually shows up at the top.
From then on, when you run a notebook cell, VS Code uses the Python inside .venv/. Run a cell containing !pip list to verify — it should show only what you installed.
A modern alternative: uv
A relatively new tool called uv has been quietly taking over modern Python tooling. It does the same things as venv and pip but is roughly ten to a hundred times faster, and the commands feel a little cleaner:
# install uv once
pip install uv
# in a project folder
uv venv
uv pip install numpy pandas matplotlib
# install from requirements.txt
uv pip install -r requirements.txt
We mention it because you will see it in the wild — many open-source projects in 2026 use it by default — but for this track, plain venv plus pip is enough. They produce the same result; uv just does it faster.
A habit that saves real time
After the first week, the routine becomes invisible:
- Start a new project: make a folder,
python3 -m venv .venv, activate, install dependencies,pip freeze > requirements.txt, commit (next section). - Resume an existing project: open the folder, activate the environment, start work.
- Add a new dependency:
pip install <name>, thenpip freeze > requirements.txt, then commit.
Three habits, all under five seconds each. The reason to take them seriously is not theoretical purity — it is that, when something breaks at 11 pm before a deadline, the diagnosis is “did I install X?” instead of “what did I install across thirty projects in 2026?”.
Check your understanding
Quick check
—You activated a virtual environment, ran `pip install pandas`, closed your laptop for the night, and reopened a new terminal window the next morning. You type `python` and try `import pandas` — you get ModuleNotFoundError. Why?
Quick check
—A teammate sends you a Python project they built last month. You clone it, open the folder, and want to run it. What is the right sequence of commands?
What comes next
You have a per-project clean room. The next habit is keeping the contents of that room safe and shareable: version control with Git. This is what turns a folder of files into a project — something with a history, something you can hand to someone, something you can undo when you break it.