Skip to Content
pip freeze vs Writing requirements.txt by Hand: Which One to Use
Intermediate
0 likes
Tutorials

pip freeze vs Writing requirements.txt by Hand: Which One to Use

pip freeze gives exact versions but hides what you chose. A hand-written file shows what you chose but isn't exact. For anything you deploy, keep both: one file for intent, one frozen for exact versions.

There are two ways to make a requirements.txt: run pip freeze and save what it prints, or write the file yourself. People argue about which one is right. I don't think either one wins on its own. They answer two different questions, and most projects that last need both answers.

Skip ahead: Short answer: for an app you run or deploy, keep a hand-written list of what you use and a frozen file of exact versions. For a quick script that only you run, pip freeze is enough. The rest of this explains why.

This assumes you work inside an activated virtual environment. If you don't, pip freeze lists every package on your computer, and none of this works. Set one up first.

What pip freeze actually saves

Install one package and look:

python -m pip install requests
python -m pip freeze
certifi==2026.7.22
charset-normalizer==3.5.2
idna==3.20
requests==2.34.2
urllib3==2.8.0

You installed one package and got five lines. requests needs certifi, charset-normalizer, idna and urllib3, and pip installed them too. pip freeze lists everything installed in the environment, each with an exact version (==). Your versions will probably be newer than the ones above. That's the point of the next section.

The good part: exact versions

A frozen file rebuilds the same environment tomorrow, on another computer, or on a server:

python -m pip freeze > requirements.txt
python -m pip install -r requirements.txt

If requirements.txt just said requests, pip would install the newest version on the day you run it. Most of the time that's fine. Sometimes a new version changes something, and code that worked last week breaks with nothing changed on your side. Exact versions prevent that.

The bad part: you can't tell what you chose

Six months later, you open a frozen file with 40 lines. Which of those did you install on purpose, and which only came along as a dependency? The file doesn't say. You remove the code that used requests, but idna and urllib3 stay in the file forever, because you don't know they're safe to remove.

It also saves mistakes. If you ran pip install pytest once to try something, or installed a package for a different project in the wrong terminal, pip freeze puts it in your requirements anyway. It doesn't know what you meant. It only knows what's there.

The hand-written file

A hand-written list says what you depend on, and nothing else:

requests>=2.31,<3

>=2.31 means "at least this version", and <3 means "not the next major version", which is where breaking changes usually go. It's readable, and it's easy to keep clean. But it's not exact. Two people installing from it on different days can get different versions of urllib3, and only one of them might see a bug.

  • Same versions everywhere: pip freeze yes, hand-written no.
  • Shows what you chose: pip freeze no, hand-written yes.
  • Picks up accidental installs: pip freeze yes, hand-written no.

Use both: one file for intent, one for exact versions

This is what I'd do for anything you deploy or share:

  1. Write requirements.in by hand, with only the packages your code imports.
  2. Install from it in a fresh environment, then freeze the result into requirements.txt.
  3. Commit both files. Install from requirements.txt everywhere else.
python -m pip install -r requirements.in
python -m pip freeze > requirements.txt

When you want to upgrade, you change requirements.in, rebuild a fresh environment from it, and freeze again. The frozen file is never edited by hand. It's output, like a compiled file.

⚠ Warning: Freeze from a fresh environment created only for this. If you freeze your everyday environment, every package you ever tried goes into the file. Delete .venv, create it again, install from requirements.in, then freeze.

💡 Tip: The pip-tools package does steps 1 to 3 for you: pip-compile requirements.in writes a requirements.txt with exact versions and a comment on each line saying which package needed it. If you manage several projects, it's worth installing. If you have one small project, the two commands above are enough.

Which one for your project?

  • A script only you run: pip freeze > requirements.txt is fine. Keep the environment clean and it's honest enough.
  • An app, a website or a tool other people run: both files, as above.
  • A library other people install with pip: neither. Libraries list ranges (like requests>=2.31) in pyproject.toml, and never exact versions, because exact pins would clash with other libraries in the user's environment.

Common mistakes

  • **Running pip freeze outside the virtual environment.** You save the whole computer's packages.
  • Freezing an environment full of experiments. Freeze a fresh one.
  • Editing the frozen file by hand. Change the hand-written file, then freeze again.
  • No versions at all, anywhere. The project works until the day a new version comes out.

What happens if you skip pinning

Nothing, for a while. Then you set up the project on a new laptop or a server, pip installs today's versions, and something that worked for months fails. You didn't change any code, so you look in the wrong places first.

If your requirements.txt has 40 lines today, could you say which five your code actually imports?

About

What pip freeze really saves, what a hand-written requirements file is good for, and the two-file setup that gives you both. After this, you can rebuild the same environment and still know what your code depends on.

Responsible
John G
Last Update
09/21/2026
Reading Time
5 min
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.