𝗣𝗮𝗰𝗸𝗮𝗴𝗲.𝗷𝘀𝗼𝗻 𝘃𝘀 𝗴𝗼.𝗺𝗼𝗱: バージョンフィールドはどこへ消えたのか?
JavaScriptからGoに移行すると、あることに驚かされるでしょう。
package.jsonファイルを開いてみてください。一番上にversionフィールドがあるのがすぐにわかります。読みやすく、プルリクエスト内で変更することも可能です。それはコードの中に存在しています。
次に、go.modファイルを開いてみましょう。
そこにはバージョンがありません。これは間違いではなく、一つの選択なのです。
Goは、自身のモジュールに対してバージョンフィールドを使用しません。代わりにgitタグを使用します。
仕組み:
• git tag v1.2.3 を実行する
• タグをリポジトリにプッシュする
• そのタグがあなたのバージョンになります
誰かが go get を実行すると、Goは適切なコミットを見つけるためにgitタグを確認します。タグが「信頼できる唯一の情報源(single source of truth)」となります。
この設計には大きな強みがあります。バージョンが誤ったコードを指すことが決してないということです。npmでは、公開されたコードとバージョンフィールドが乖離してしまうことがあります。しかしGoでは、それらは同一のものです。バージョンとは、コミットそのものなのです。
ただし、これによりワークフローが変わります:
- 可視性: ファイルを見るだけではバージョンを確認できません。確認するにはgitコマンドを実行する必要があります。
- コードレビュー: バージョンの更新(version bump)はコードの差分(diff)に現れません。それはコードの変更ではなく、タグのプッシュだからです。
- ローカルビルド: タグのないコミットは、乱雑な疑似バージョン(pseudo-version)として表示されます。コミットにタグを付けて初めて、きれいなバージョンになります。
- メジャーバージョン: これが最大の変更点です。Goでは、v2以降はインポートパスにバージョンを含める必要があります。
例: v1 は github.com/you/my-app を使用 v2 は github.com/you/my-app/v2 を使用
これにより、一つのプログラムが同じライブラリの異なる2つのメジャーバージョンを、競合することなく使用できるようになります。
他のほとんどの言語は、ファイル内にバージョンを保持しています: • Node: package.json • Rust: Cargo.toml • Python: pyproject.toml • Java: pom.xml
Goは例外です。gitタグに厳格に依存しています。
npmのような体験が恋しい場合は、ビルド時に ldflags を使用してバイナリにバージョンを注入することができます。これにより、アプリのバージョン確認コマンドに対応させることが可能です。
トレードオフは単純です: バージョンフィールドは読みやすくレビューもしやすいですが、嘘をつく可能性があります。 gitタグは見えにくいですが、常に真実です。
Goは利便性よりも真実を選んだのです。
Goの開発者の皆さんへ:これは最善のモデルでしょうか?もしツールがgitタグと照合して検証してくれるのであれば、go.modにバージョンフィールドがある方が良いと思いますか?
出典: https://dev.to/dalirnet/packagejson-vs-gomod-where-did-the-version-field-go-3301