Anyone who has been programming in Python for a few months sooner or later runs into the same problem: a project works, then you install a library for another project and the first one stops working. Or a program that runs perfectly on your computer fails on a colleague's. The cause is almost always the same: all dependencies are installed in one place, mixed together. The solution is called a virtual environment.
The problem with global dependencies
When you run pip install without precautions, the library ends up in the Python installation shared by the whole system (or by your user). Imagine two projects:
- project A uses a library at version 1.x;
- project B requires the same library at version 2.x, with incompatible changes.
A single global installation cannot hold both versions. Upgrading for B breaks A. On some operating systems, moreover, the system Python is used by internal tools: installing random packages into it can cause problems that are hard to diagnose. This is why recent Linux distributions tend to block global pip install, suggesting virtual environments instead.
What a virtual environment is
A virtual environment is a folder containing a Python interpreter linked to the base one and an isolated space where libraries are installed. Each project gets its own. When the environment is active, python and pip point to that folder, and everything you install stays there.
It is not a virtual machine or a container: it is a lightweight mechanism, created in a few seconds, that can be deleted and recreated with no consequences.
Creating and activating an environment with venv
Python includes the venv module in its standard library. From the project folder:
python3 -m venv .venv
source .venv/bin/activate # Linux and macOS
.venv\Scripts\activate # Windows (command prompt)
After activation, the terminal prompt usually shows the environment name. From then on:
pip install requests
python script.py
deactivate # to leave the environment
The name .venv is a widespread convention: many editors recognise it automatically and offer it as the project interpreter.
Pinning dependencies
A virtual environment on its own is not enough: you also need to record which libraries are required, so anyone can recreate the same environment. The simplest method is the requirements.txt file:
pip freeze > requirements.txt
pip install -r requirements.txt
The first command lists every installed package with its exact version; the second reinstalls them elsewhere. It is important to distinguish two levels:
| Level | Contents | Purpose |
|---|---|---|
| Direct dependencies | The libraries you actually use in the code | Describe what the project needs |
| Locked dependencies | All libraries, including indirect ones, with exact versions | Reproduce an identical environment |
For an application going into production, locked versions are worth having, so the test environment and the real one match. For a library meant for other projects, on the other hand, you specify ranges of compatible versions, so as not to impose overly rigid constraints on its users.
pyproject.toml and modern tools
The pyproject.toml file is today the standard way to describe a Python project: name, version, dependencies, tool configuration. Tools such as Poetry, PDM or uv use it to manage the virtual environment, direct dependencies and the lock file together. They are not essential to get started, but on projects with several people they simplify the work a lot, because they clearly separate what you declared from what was actually resolved.
Practical advice: learn venv and pip first, because they are always available and help you understand the mechanism. Then adopt a more advanced tool if the project calls for it.
Good practices
- One environment per project, always, even for small scripts you think you will use only once.
- Do not put the environment folder under Git. Add
.venv/to your.gitignore: the environment is recreated from the dependency files, not copied. - Record the Python version. An environment created with one version is not guaranteed to work with another; state it in the README or in
pyproject.toml. - Recreate the environment when something is odd. Deleting the folder and reinstalling from
requirements.txtsolves many mysterious problems and checks that the file is really complete. - Upgrade deliberately. Update dependencies at a dedicated moment, run the tests and then record the new versions, rather than ending up with random upgrades.
Frequent mistakes
- Forgetting to activate the environment and installing everything globally. A quick check:
which python(orwhere pythonon Windows) should point to the.venvfolder. - Using the
pipof one Python and thepythonof another. Writingpython -m pip install ...removes the ambiguity, because it uses the pip of the interpreter you are running. - Moving the environment folder. Virtual environments contain absolute paths: if you move the project, it is simpler to recreate the environment.
In short
- Virtual environments isolate each project's dependencies and prevent version conflicts.
python3 -m venv .venvand activation are all you need to get started.- Record dependencies in
requirements.txtorpyproject.toml, never the environment folder in Git. - Use
python -m pipto be sure you are installing into the right interpreter. - When something does not add up, recreate the environment from scratch: it is quick and revealing.