セマンティックバージョニング:主番号の約束は「挙動が黙って変わらない」

MAJOR は「変更が大きい」ではなく「破壊的」の意味です。リファクタで主番号を上げたり、破壊的変更を副番号に隠すと、下流の自動更新が事故になります。

1.4.2 → 1.5.0 は「機能追加があり、壊れていない」という意味です。この約束の本当の価値は、下流が ^1.4.2 と書いて版ごとの確認を省けることです。約束しているのは変更の大きさではなく、破壊的変更が黙って起きるかどうかです。

三つの数字の意味

位置 上げる時 下流が仮定してよいこと
MAJOR 非互換の変更 人の介入が必要
MINOR 後方互換の追加 そのまま更新して安全
PATCH 後方互換の修正 そのまま更新して安全

要点は「後方互換」の定義です。既定引数の変更、console.log の削除、整列の安定性の変化。どれも小さく、どれも破壊的です。

0.x の例外性

0.y.z の間はどのリリースも破壊的になり得ます。したがって ^0.4.2 は「0.5 も互換」を意味しません。

npm はこれを特別扱いし、^0.4.2 は実際には 0.4.x だけを許します。この挙動は誤解されがちで、「自動更新が上がらない」原因の多くがここです。

公開前の三つの問い

  1. 古いコードは新版で動くか? 動かないなら MAJOR。
  2. 新しい公開 API はあるか? あるなら MINOR。
  3. 内部の修正だけか? そうなら PATCH。

「インターフェースは同じだが挙動が変わった」も MAJOR です。実行時の観測可能な変化も非互換であり、型検査では見えないため最も見落とされます。

プレリリースとビルドメタデータ

1.5.0-beta.1 の beta.1 はプレリリース識別子で、1.5.0 より下に並びます。< で比べると 1.5.0-beta.1 < 1.5.0 が成り立ちます。

1.5.0+build.7 の +build.7 はメタデータのみで、比較に参加しません。ビルド情報の伝達には使えますが、整列に使うと意外な結果になります。

lockfile と range は別物

package.json の range は意図を、lockfile は解決結果を固定します。前者だけをコミットすると、機械ごとに違う版が入ります。「自分の環境では動く」の典型的な出所です。

主番号を一つ上げるのは「移行手順を読んでください」という宣言です。気軽に言ってはいけませんし、言うべき時に黙ってもいけません。

← 記事一覧に戻る

コメント

…