pip freezeをそのままコミットしてはいけない理由

pip freezeをそのままコミットするのはおすすめしにくい

結論から言うと、pip freezeの結果をそのままrequirements.txtとしてコミットする運用は、多くのプロジェクトでトラブルの種になりやすいため、基本的にはおすすめしにくいです。
一見すると「環境を完全に再現できそう」「バージョンが固定されて安心」というメリットがありそうですが、実際に運用してみると別の問題が表に出てくることが少なくありません。

特にチーム開発や長期運用を前提としたプロジェクトでは、pip freezeをそのまま使うことで、環境構築が難しくなったり、不要な差分が増えたりするケースが目立ちます。
この記事では、なぜpip freezeをそのままコミットしてはいけないと言われるのかを、具体例を交えながら整理していきます。

pip freezeとは何をするコマンドか

pip freezeの基本的な役割

pip freezeは、現在のPython環境にインストールされているパッケージとそのバージョンを一覧として出力するコマンドです。
仮想環境の中で実行すると、以下のような形式で結果が出力されます。

pip freeze
Django==4.2.5
requests==2.31.0
urllib3==2.0.4
idna==3.4

この出力は、その環境が「今どんな状態か」を正確に記録したスナップショットだと考えると分かりやすいです。

本来想定されている使いどころ

pip freezeは、以下のような用途ではとても便利です。

  • 一時的に環境を保存しておきたい
  • ローカル環境を別のマシンにコピーしたい
  • トラブル発生時に、どのパッケージが入っているかを確認したい

つまり、「その瞬間の環境を再現したい」という目的には非常に向いています。
問題になるのは、これをそのままプロジェクトの正式な依存関係定義として扱ってしまう点です。

そのままコミットすると何が起きやすいのか

直接依存していないパッケージまで固定される

pip freezeの大きな特徴は、直接インストールしたパッケージだけでなく、その依存関係として入ったライブラリまで全て列挙される点です。

例えば、requestsを1つ使いたいだけでも、以下のようなライブラリが一緒に含まれます。

  • urllib3
  • certifi
  • idna
  • charset-normalizer

これらはrequestsが内部で使っているものであり、アプリケーション側が直接意識する必要は本来ありません。
しかしpip freezeをそのままコミットすると、これら全てのバージョンが固定されます。

結果として、以下のような状況が起きやすくなります。

  • 本来関係ないライブラリの更新ができない
  • セキュリティ修正が入っても簡単に追従できない
  • なぜこのバージョンなのか誰も説明できない

環境差分がそのままノイズになる

開発者ごとに環境が少しずつ違うと、pip freezeの結果も微妙に変わります。

例えば、以下のような違いです。

  • OS依存のパッケージが含まれる
  • 開発用ツール(lintやformatter)が混ざる
  • 過去に試しただけのライブラリが残っている

これらがそのままコミットされると、差分レビューが非常にしづらくなります。
「本当に必要な変更なのか」「ただの環境差なのか」が分かりにくくなり、チームの認知負荷が上がります。

実際によくある失敗パターン

新しいメンバーが環境構築で詰まる

pip freezeをそのまま使ったrequirements.txtを使うと、以下のようなトラブルが起きがちです。

  • 特定のOSでしか動かないバージョンが含まれている
  • 既に配布が止まっているパッケージが含まれている
  • Pythonのマイナーバージョンに依存している

結果として、新しく参加したメンバーがpip installの段階でエラーに遭遇し、「まず環境が作れない」という状態になります。

小さな修正なのに大量の差分が出る

一つライブラリを追加しただけなのに、pip freezeを更新すると数十行の差分が出ることがあります。
そのほとんどが、間接依存のバージョン差分です。

これにより、以下のような問題が生じます。

  • レビューで本質的な変更が見えにくい
  • どの変更が影響したのか追いづらい
  • ロールバックが怖くなる

それでもpip freezeが役立つ場面

pip freeze自体が悪者というわけではありません。
使いどころを間違えなければ、今でも有用なコマンドです。

例えば以下のようなケースです。

  • 本番障害時の調査用ログとして保存する
  • Dockerイメージのビルド結果を記録する
  • 一時的な検証環境をそのまま複製したい場合

このように、「この環境を完全に再現したい」という目的が明確な場合には適しています。
逆に言えば、日常的な依存関係管理には向いていないと言えます。

ではどう管理するのが現実的か

直接依存だけをrequirements.txtに書く

多くのプロジェクトでは、以下のような方針が現実的です。

  • アプリケーションが直接使うライブラリだけを書く
  • バージョンは完全固定ではなく範囲指定にする
  • 間接依存はpipに解決させる
Django>=4.2,<5.0
requests>=2.31

こうすることで、最低限の再現性と、将来のアップデート余地を両立しやすくなります。

pip freezeはロック用途として分ける

再現性が特に重要な場合は、以下のように役割を分ける方法もあります。

  • requirements.txt:人が管理する依存定義
  • requirements.lock:pip freezeで生成する完全固定版

このように分けることで、「普段触るもの」と「機械的に生成されるもの」を区別できます。

注意点とリスク

この運用にも注意点はあります。
バージョン範囲指定をしていると、ある日突然アップデートで挙動が変わる可能性はゼロではありません。

そのため、以下のような対策が現実的です。

  • 定期的に依存関係を更新する
  • CIで自動テストを回す
  • ロックファイルを併用する

pip freezeをそのまま使わないからといって、何も考えなくてよいわけではない点は意識しておく必要があります。

結局どうすればいいか

pip freezeをそのままコミットする運用は、短期的には楽に見えても、長期的には管理コストを押し上げがちです。
依存関係は「今の環境を写すもの」と「プロジェクトの前提条件」を分けて考える方が、結果的に安定します。

  • pip freezeはスナップショット用途と割り切る
  • requirements.txtは人が読める形で管理する
  • 再現性が必要な場合はロックファイルを併用する

このあたりを意識するだけでも、Pythonプロジェクトの運用はかなり楽になるはずです。