Virtual Environments, requirements.txt, and Reproducibility

Make your project run the same on any machine

Intermediate 11 min

In this lesson

Reproducibility means anyone — including future you — can set up your project and get the same result, instead of it only running on your computer. The tools are a virtual environment, a pinned requirements.txt that lists exact package versions, and a short README. This lesson ties them together.

Explain it like I’m 5

A reproducible project is one someone else can set up without guessing what your computer secretly had installed. You write down exactly which packages and versions it needs.

Why “works on my machine” happens

Two computers rarely have the same packages at the same versions, so code that runs for you can break elsewhere. A virtual environment gives each project its own isolated set of packages, and freezing those packages records exactly what it needs.

Example
python -m venv .venv               # create an isolated environment
source .venv/bin/activate          # activate it (Windows: .venv\Scripts\activate)
pip install requests==2.31.0       # install a pinned dependency
pip freeze > requirements.txt      # record exactly what is installed
Create an isolated environment, install pinned packages, then freeze them.

requirements.txt pins your dependencies

A requirements.txt lists each package with an exact version. Anyone can recreate your environment with pip (pip install -r requirements.txt) and get the same versions you used — no drift, no surprises.

Example · requirements.txt
requests==2.31.0
flask==3.0.0
python-dotenv==1.0.1
Each line pins one package to an exact version.
Example
requirements = """requests==2.31.0
flask==3.0.0
python-dotenv==1.0.1"""

for line in requirements.splitlines():
    package, version = line.split("==")
    print(f"{package} is pinned to {version}")
Output
requests is pinned to 2.31.0
flask is pinned to 3.0.0
python-dotenv is pinned to 1.0.1
Reading the file back: each line splits into a package and a version.

Build a requirements file from your deps

Sometimes you assemble the list yourself from the packages and versions you chose. Sorting by name keeps the file tidy and easy to diff in Git.

Turn a dict of package: version into requirements.txt lines like flask==3.0.0, one per line, sorted by package name.

deps = {"flask": "3.0.0", "requests": "2.31.0", "click": "8.1.7"}

lines = []
# TODO: build a "name==version" line for each package, sorted by name
print("\n".join(lines))

Common mistake: Forgetting to activate the virtual environment

Why it happens:

Without the activation step, pip install still runs — but installs the package system-wide instead of into the project.

How to fix it:

Activate first (source .venv/bin/activate) and confirm with which python that you’re inside the project’s environment before installing.

Common mistake: Not pinning versions

Why it happens:

Writing just flask instead of flask==3.0.0 works today, so the version feels optional.

How to fix it:

Pin exact versions with ==. Unpinned dependencies drift over time and eventually a new release breaks your project for no obvious reason.

Common mistake: Assuming everyone has the same Python version

Why it happens:

Your code runs on your Python, so it’s easy to forget others may be on an older or newer one.

How to fix it:

Note the required Python version in the README alongside the setup steps, so others can match it before installing.

What does pinning a version with == protect against?

Which command records installed packages into requirements.txt?

Why does a reproducible project need a README with setup steps?

Mini exercise (medium)

Write parse_requirements(text) that reads requirements.txt text and returns a dict mapping each package to its pinned version. Blank lines should be ignored.

Do it here. Finish the code below and press Run to try your answer right in the browser — no setup needed.

def parse_requirements(text):
    deps = {}
    for line in text.splitlines():
        # TODO: skip blank lines; split "name==version" and store it
        pass
    return deps

text = "requests==2.31.0\n\nflask==3.0.0\n"
print(parse_requirements(text))

What to learn next

You learned why “works on my machine” happens, how a virtual environment isolates a project’s packages, why pinning versions in requirements.txt prevents drift, and how a README makes setup reproducible. You wrote parse_requirements() to read a requirements file back into a dict.

Now bring the whole unit together. The Unit 5 Project builds a safe file organizer — argparse, pathlib, a dry-run flag, and tested helpers, all in one tool. When you’re ready, continue to the project.