← キャッシュ・非同期処理 デバッグの作法 →

📑 この章の目次

title: バージョニング・パッケージ管理 icon: 📦 card_desc: 「1.2.3」の意味と、依存ライブラリの管理 — セマンティックバージョニング/メジャー・マイナー・パッチ・依存関係・ロックファイル slug: 10-versioning-package

10 バージョニング・パッケージ管理 — 「1.2.3」の意味と、依存ライブラリを壊さずに更新する作法

自分の書いたコードだけでは動かない。他人が作ったライブラリ(道具の詰め合わせ)を組み合わせて動かす。その「組み合わせ」を番号で管理し、壊さずに更新する作法。


一言でいうと

モダンな開発は、自分のコード + 無数の外部ライブラリ(部品) の組み合わせで成り立ちます。それぞれの部品には「バージョン(版)」という番号が付いていて、この番号の意味を読み解き、組み合わせを記録しておくのが「バージョニング・パッケージ管理」の作法です。


1. なぜバージョニング・パッケージ管理が必要なのか

「私の環境では動いた」の罠

開発していると、こういうことが起きます。

原因の多くは「使っている部品(ライブラリ)のバージョンが違う」ことです。ある部品が新しくなって仕様が変わり、それに依存していたあなたのコードが動かなくなった、という現象です。これを依存地獄(依存関係の地獄/dependency hell)と呼びます。

2つの問いに答える

バージョニング・パッケージ管理は、次の2つの問いに答える作法です。

  1. 「この部品が新しくなったとき、私のコードは壊れるか?」 — バージョン番号の読み方(セマンティックバージョニング)で分かる
  2. 「半年後にこのコードを別の人が動かしたとき、同じく動くか?」 — 組み合わせの記録(ロックファイル)で保証する

この2つが揃って初めて、「どこでも・いつでも同じように動く」(再現性)が手に入ります。


2. セマンティックバージョニング(SemVer)とは —— 「1.2.3」の読み方

3つの数字

ライブラリのバージョンは、たいてい 1.2.3 のようにピリオドで区切られた3つの数字で表されます。これをセマンティックバージョニングSemantic Versioning/通称 SemVer/セムヴァー)と呼びます。

位置 名前 役割
1 メジャー(major) 後方互換性が壊れる変更
2 マイナー(minor) 後方互換性を保つ機能追加
3 パッチ(patch) バグ修正(機能は変わらない)

後方互換性」(こうほうごかんせい/backward compatibility)とは、古い使い方が新しい版でもそのまま動くという性質のことです。「前の版のコードが、新しい版でも壊れずに動く」という約束を表します。

どう上がるか

あるライブラリが 1.2.3 から次のように変わったとき、意味が違います。

この「どの桁が上がったかで、壊れるかどうかが分かる」という約束こそが、SemVer の核心です。


3. 🔑 番号は「互換性の約束」 —— SemVerの核心

なぜ番号の読み方が重要か

SemVer が単なる「数え方のルール」でなく作法たるゆえんは、番号が作り手と使い手の間の約束だからです。

この約束があるからこそ、無数のライブラリを組み合わせても**「どれを更新していいか・慎重にすべきか」が番号だけで判断**できます。約束が守られていないと、パッチ番号の変更で壊れるようなライブラリは「SemVer を守らない」として不信の目で見られます。

例えていうと: 電化製品のコンセント

「コンセントの形が変わった」のがメジャーアップデートだと分かれば、安易に挿し直さず、変換アダプタ(修正)を用意するか止めるかを判断できます。これと同じことを、番号の桁で行います。


4. パッケージ管理とは —— 依存関係を宣言で管理する

パッケージとパッケージマネージャ

外部ライブラリをパッケージpackage/荷物)と呼びます。そして、パッケージを探して・ダウンロードして・組み込んで・更新する仕組みをパッケージマネージャpackage manager/荷物の管理係)と呼びます。言語ごとに定番があります。

言語 パッケージマネージャ
JavaScript npm(エヌピーエム)・yarn npm install react
Python pip(ピップ)・uv pip install fastapi
Rust cargo(カルゴ) cargo add serde
Ruby bundler(バンドラー) bundle install

「宣言」で管理する

モダンなパッケージ管理の作法は「何を使うかを宣言ファイルに書く」ことです。JavaScript なら package.json、Python なら requirements.txt 等に「このライブラリのこの版を使う」と宣言します。

fastapi == 0.115.0
uvicorn >= 0.30.0

こう書いておけば、パッケージマネージャが宣言を読んで必要な部品を自動で集めてくれます。人数が増えても、別のパソコンでも、宣言ファイル1つあれば「同じ部品構成」が再現できます。


5. 🔑 ロックファイルと範囲指定 —— 再現性の鍵

範囲指定の誘惑と罠

宣言ファイルには「この版ぴったり」でなく「この版以上」のように範囲で書くことができます。

書き方 意味
1.2.3 この版ぴったり(固定)
^1.2.3 1.2.3 以上 2.0.0 未満(マイナー・パッチは自動で新しくなる)
~1.2.3 1.2.3 以上 1.3.0 未満(パッチだけ新しくなる)

範囲指定は「バグ修正や新機能を自動で取り込める」という利点があります。しかしこれには裏があります。範囲を許すと、インストールのたびに実際に入る版が少しずつ違う可能性があるのです。「昨日は動いた、今日は壊れた」の再現です。

💡 0.x 系(1.0.0 未満)は例外: 開発初期のライブラリ(0.2.5 等)は SemVer のルールがまだ緩く、マイナー更新でも壊れる変更が入りうると定められています。0.x 系への範囲指定(^0.2.3 等)はとくに慎重に扱いましょう。

ロックファイル —— 実際の組み合わせを固定する

この問題を解決するのがロックファイルlock file/鍵ファイル)です。パッケージマネージャは、宣言を元に部品を集めたあと、**「実際に入れた版を、部品ごとにすべて書き留めた別のファイル」**を生成します。

言語 ロックファイル
JavaScript(npm) package-lock.json
JavaScript(yarn) yarn.lock
Python(uv) uv.lock
Rust Cargo.lock

ロックファイルのおかげで「半年後に別の人が install しても、私が動かした時と全く同じ版組み合わせが入る」ことが保証されます。範囲指定(宣言)で柔軟性を保ちつつ、ロックファイル(記録)で再現性を固める、という2枚構えがモダンな作法です。

⚠ ロックファイルは原則コミット(Git に保存)します。特にアプリ開発では、再現性のためにメンバー・CI 全員で共有する必須ファイルです。「.gitignore で無視する」は誤解です。※一方でライブラリ開発(他人が組み込む部品を作る側)では、使う側の依存関係を縛りすぎないよう、あえてコミットしない運用も一般的です。


6. なぜ「バージョンを正しく管理する」が開発の作法なのか

作法として位置づけられる理由

面接・実務での意味


7. よくある落とし穴と対処

落とし穴 何が起きるか 正しい対応
ロックファイルをコミットしない メンバー・CI で版がズレて「私の環境では動く」が再発 アプリ開発ではGitに保存(※ライブラリ開発では逆にコミットしないのが一般)
メジャーアップデートを雑に取り込む 後方互換性が壊れ、あちこちでエラー メジャー上がりは必ず動作確認・範囲指定で上限を 2.0.0 未満に抑える
範囲指定の範囲を広くしすぎる 知らぬ間に新しすぎる版が入って壊れる ^~ の意味を理解し、本当に許したい範囲だけ指定する
SemVer を守らずに番号を上げる 使う人が「パッチのはずが壊れた」と不信に陥る 公開側はメジャー=壊れる変更の約束を厳格に守る
使わなくなった部品を放置 セキュリティリスク・無駄な依存が増える 定期的に依存関係を棚卸しし、未使用のものは外す
宣言ファイルと実際が乖離 「書いてある版」と「入っている版」が違う事故 install は必ずパッケージマネージャ経由で、宣言ファイルを正とする

💡 依存地獄dependency hell): 依存する部品が多くなりすぎて、「部品Aのために部品Bは新しくしたいが、部品Cが古いBしか受け付けない」のように版の矛盾で身動きが取れなくなる状態。SemVer・ロックファイル・定期的な棚卸しは、すべてこの地獄に落ちないための作法です。


まとめ

💡 次のステップ: 部品の組み合わせを管理できるようになりました。しかし、どんなに管理してもバグは起きます。第11章デバッグの作法」では、起きたバグの原因を、当てずっぽうでなく論理的に切り分けていく手順を扱います。