はじめに
この記事は私が開発していて見かける『Semantic Versioning』の間違いについてまとめたものです。
対象は...
- バージョン管理を任された方
- Semantic Versioningを始めようと思っている方 or 始めたばかりの方
- Gitのタグ付けに困っている方
などです。
『Semantic Versioning』は理解できています という方には物足りない内容になってしまうかなと思います。
よく分からないけど、興味はあるという方は、ぜひ『Semantic Versioning 2.0.0』に目を通してみてください。
よくある間違いたち
ここに挙げるものたちは、Semantic Versioningを行う上でよく見かける間違いたちです。
Semantic Versioningを導入する場合には、同じ間違いを犯さないように気を付けましょう
1. すべてメジャーアップデートで対応してしまう。
恐らくこれが一番見かけることが多いかなと思います。
何となくでSemantic Versioningを採用してしまっている場合に多いかなと思っています。
// このようなケースです。
1.0.0 -> 2.0.0 -> 3.0.0
これについては、Semantic Versioningの一番最初で各セクションの役割について言及されています。
バージョンナンバーは、メジャー.マイナー.パッチ とし、バージョンを上げるには、
- APIの変更に互換性のない場合はメジャーバージョンを、
- 後方互換性があり機能性を追加した場合はマイナーバージョンを、
- 後方互換性を伴うバグ修正をした場合はパッチバージョンを上げます。
ここで示されている通り、各セクションはx.y.z(x:メジャー.y:マイナー.z:パッチ)
を表しています。
↑の例だと、パッケージがリリースされる度にメジャーバージョンがインクリメントされているため、リリースするたびに後方互換性のないパッケージがリリースされているように見えてしまいます。
2. メジャー、マイナーのいずれかをインクリメントした際に下位の番号のリセットを忘れる
これも良く見かける間違いだと思います。
例えば下記のような感じです。
// マイナーをインクリメントしたが、パッチがそのまま...
1.1.9 -> 1.2.9
これだと、v1.2.0
に対して、9
個のパッチがあたっているように見えてしまいます。
この点ついても原文に明記されています。
7.マイナーバージョン Y (x.Y.z | x > 0)は、後方互換性を保ちつつ機能性をパブリックAPIに追加した場合、上げなければなりません(MUST)。また、いかなるパブリックAPIも廃止予定としたのなら、上げなければなりません(MUST)。プライベートコード内での新しい機能の追加や改善を取り込んだ場合は、上げてもよいです(MAY)。その際にパッチレベルの変更も含めてもよいです(MAY)。マイナーバージョンを上げた際にはパッチバージョンを0にリセットしなければなりません(MUST)。
8.メジャーバージョン X (X.y.z | X > 0)は、パブリックAPIに対して後方互換性を持たない変更が取り込まれた場合、上げなければなりません(MUST)。その際にマイナーおよびパッチレベルの変更も含めてもよいです(MAY)。メジャーバージョンを上げた際には、パッチおよびマイナーバージョンを0にリセットしなければなりません(MUST) 。
マイナーバージョンを上げた際にはパッチバージョンを0にリセットする。
// パッチを0にリセット
1.2.8 -> 1.3.0
メジャーバージョンを上げた際には、パッチおよびマイナーバージョンを0にリセットする。
// マイナー、パッチを0にリセット
1.2.8 -> 2.0.0
3. パブリックなAPIを廃止しているのにマイナーバージョンをインクリメントしてしまう
これは、マイナーバージョンのインクリメント要件を勘違いしている時に発生することがあります。
7.マイナーバージョン Y (x.Y.z | x > 0)は、後方互換性を保ちつつ機能性をパブリックAPIに追加した場合、上げなければなりません(MUST)。また、いかなるパブリックAPIも廃止予定としたのなら、上げなければなりません(MUST)。プライベートコード内での新しい機能の追加や改善を取り込んだ場合は、上げてもよいです(MAY)。その際にパッチレベルの変更も含めてもよいです(MAY)。マイナーバージョンを上げた際にはパッチバージョンを0にリセットしなければなりません(MUST)。
ここで重要なのは**『廃止予定としたのなら』です。『廃止したら』**ではありません。
公開APIを廃止してしまうと、後方互換性は失われてしまいます。
そのため、最初に掲げている『後方互換性があり機能性を追加した場合はマイナーバージョンを、』に抵触してしまいます。
機能廃止については、FAQでも言及されており、一般的な流れとしては次のようになります。
- ドキュメントを更新し、対象のAPIが非推奨になることを報告する。
- 非推奨の機能を残したまま、マイナーバージョンをリリースする。
- 非推奨の機能を削除して、メジャーバージョンをリリースする。
4. 初期開発フェーズのバージョンを使い続ける
これは以前、私が自身がやっていて注意を受けたものです。
Semantic Versioningでは、メジャーバージョンに0
を使用することで、**『初期段階の開発用』**という意味を持たせることができます。
// 開発用段階のバージョンであることを示している。
0.2.9
Packageを作成している側は、『初期段階の開発用』ということを明示的に表しておくことにより、**公開されているAPIが不安定であること、破壊的な変更などいかなる変更も起こりえる。**ということを対外的に示すことができます。
そのため、利用する側としては『公開されているAPIは不安定である。』と捉える必要があります。
いつ1.0.0
を採番するのかについては、判断の難しいところですが、これについても一応の指針は示されています。
0.y.zのような初期の開発フェーズにおけるバージョンの取り扱いはどのようにすべきでしょうか?
一番簡単な方法は0.1.0からで開発版をリリースし、その後のリリースのたびにマイナーバージョンを上げていけばよいでしょう。1.0.0のリリースはいつすべきでしょうか?
もし既にプロダクション用途であなたのソフトウェアが利用されているのなら、それは1.0.0であるべきでしょう。またもし安定したAPIを持ち、それに依存しているユーザーが複数いるのなら、それは1.0.0であるべきでしょう。もし後方互換性について多大な心配をしているのなら、それは1.0.0であるべきでしょう。
開発する側にとっては、ある意味で『責任を持たなくていい』ので楽かも知れませんが、使う側にすれば採用しづらいことこの上ないので、なるべくは明確なバージョンを採番することをオススメします。
5. プレリリースバージョンの優先度が高くなってしまう
Semantic Versioningは、x.y.z
の記載以外にも、プレリリースを表す識別子の使用も許可されています。
(使用できる識別子は、ASCII英数字とハイフン [0-9A-Za-z-] である必要があります。)
// '-bata'の識別子を使って、プレリリースを表している。
1.0.0-beta
識別子が付いてしまっているためか、優先度が 1.0.0 < 1.0.0-beta
であると勘違いされている方がいますが、識別子の使用はあくまでプレリリース『事前公開』を表すものなので、正式リリースではありません。
そのため、優先度は 1.0.0 > 1.0.0-beta
となります。
(これについては、Semantic Versioningにも明記されています。)
メジャー、マイナー、パッチが同じ場合、プレリリースバージョンを持っている方が通常のバージョンよりも低い優先度です。例:1.0.0-alpha < 1.0.0。
これについては、使用している識別子が紛らわしいということも可能性としてあり得ますので、プレリリースであることが分かりやすい命名を意識する必要があるかなと思います。
判断が難しいものたち
ここに挙げるものは、間違いなのか判断が難しい微妙なものたちです。
(周りの人に聞いても、間違いなのか判断に迷う。ということだったので、↑とは別にしています。)
1. v1.2.3
はSemantic Versioningではない
これだけ書くと、今までの話は何だったのか?となってしまうと思いますが、実はこれもSemantic Versioningに明記されています。
『v1.2.3』はセマンティック バージョンでしょうか?
いいえ、『v1.2.3』はセマンティック バージョンではありません。しかしながら、セマンティック バージョンに接頭辞の『v』を付けるのは英語ではバージョン番号であることを示す一般的な方法です。バージョン管理では、『バージョン』を『v』と略すことがよくあります。たとえば git tag v1.2.3 -m" Release version 1.2.3 " では『v1.2.3』はタグ名であり、セマンティック バージョンは『1.2.3』です。
要するにv
はいらないよ!って話です。
ただ、これについては一概に間違いを指摘するのも、どうかなと私個人は思っています。
(間違いであると知っておくことは重要ですが、頭ごなしに指摘したり、今まで採番したものを全部修正するほどでもないかなと。)
本質的にインクリメントの要件が理解できていれば良いという意見もあるので、判断が難しいものとしました。
2. マイナー、パッチを同時にインクリメントしてしまう
バグ修正と機能追加が同時に行われてしまった場合に、マイナー、パッチを同時にあげるというものです。
// バグと機能追加が一緒なのでマイナー、パッチバージョンをインクリメント
1.1.0 -> 1.2.1
これは個人的には間違いに含まれるのかなと思っていますが、周りには許可するという方もいたので、こちらに記載しています。
(これを許可すると答えた方は、細分化すれば結局同じだから。ということでした。)
// 機能追加後、パッチを当てたと同じ状況では?ということ。
1.1.0 -> 1.2.0 -> 1.2.1
私個人の意見としては、たとえ結果が同じだとしても、同時にバージョンをあげてしまうのはNGかなと思っています。
Gitなどでタグを管理していた場合、↑の例であれば1.2.0
のタグが存在しないことになってしまうというのも理由の一つ理由ですが、そもそも仮に同時に発生したとして、バグを修正した結果として機能性追加になったのであれば、マイナーバージョンアップで良いでは?と思っているからです。
まとめ
今回は『Semantic Versioningでよくある間違いたち』と題して、現場や開発で見かける間違いや勘違いについてまとめてみました。
極論を言ってしまえば、バージョンを管理できていれば何でも良い。と言われる方もおられると思いますし、意見としてもっともです。
ですが、Semantic Versioningを採用すると決めたのであれば、Semantic Versioningの利点である『バージョンのコードが次のバージョンへの変更された際に何が変更されたのかユーザーに伝えることできる。』という点を最大限に生かしていただければと思います。
この記事が他者に優しいVersion管理を行う一助になれば幸いです。