Money Forward Developers Blog

株式会社マネーフォワード公式開発者向けブログです。技術や開発手法、イベント登壇などを発信します。サービスに関するご質問は、各サービス窓口までご連絡ください。

20230215130734

最近のGoの後方互換性について(2024年も積極的にバージョンアップしよう)

こんにちは!マネーフォワードエックスカンパニー 個人サービス開発部 バンキングアプリ開発グループの仲川です。

みなさん2024年は気持ち良くスタートを切れたでしょうか?私は年始の初売りセールでスマートロックを購入して自宅に取り付けたんですが、施錠が機能していないことに全く気づかず、1週間ほどノーガード状態で「鍵のある生活から解放された!」と浮かれて過ごしていました。

さてソフトウエアエンジニアにとって鍵より煩わしいのは、後方互換性のないソフトウエアのバージョンアップ作業だと思います。今回のテーマはそんな後方互換性についてです。

Goでは2023年にリリースされたv1.21で後方互換性を向上させるための機能が含まれ、公式ブログで後方互換性に関して改めて言及されました。また2024年2月にリリース予定のv1.22では標準ライブラリで初めてv2パッケージが導入される予定であるなど、後方互換性に関する話題を多く目にする機会がありました。

それらの内容を振り返りつつ、今後のバージョンアップの対応をどのように進めるべきかを改めて考えていきたいと思います。

TL;DL

2024年も積極的にバージョンアップしていきましょう!!(もちろんリリースノートしっかり読んでから)

初期からの後方互換性に対するスタンス

Goのv1.0がリリースされた際に発表されたGo 1 and the Future of Go Programsという文書では、後方互換性について以下のように記述されています。

It is intended that programs written to the Go 1 specification will continue to compile and run correctly, unchanged, over the lifetime of that specification. At some indefinite point, a Go 2 specification may arise, but until that time, Go programs that work today should continue to work even as future "point" releases of Go 1 arise (Go 1.1, Go 1.2, etc.).

※日本語訳※ Go 1の仕様に沿って書かれたプログラムは、その仕様の存続期間中、変わることがなく正しくコンパイルされ、実行され続けることが計画されています。いつかはわからないが、Go 2が出るかもしれません。ただしその時まで、現在動作しているGoプログラムは、将来Go 1の「ポイント」リリース(Go 1.1、Go 1.2など)が出ても動作し続けるでしょう。

Goは半年に1回(2月・8月)のリリースサイクルでバージョンアップが行われていますが、このブログ投稿時点で最新のv1.21に至るまで後方互換性を維持しています。これはv1.18でジェネリクスの導入といった大きな言語仕様の変更が行われた際も同様でした。こういった後方互換性が維持されている点が、開発現場でGoが採用される大きな理由の一つだと考えています。

破壊的な変更が行われるケース

ただし同じ文書では以下のようにも言及されています。

Although we expect that the vast majority of programs will maintain this compatibility over time, it is impossible to guarantee that no future change will break any program.

※日本語訳※ 大半のプログラムはこの互換性が長期に渡って維持される想定ですが、将来いかなる変更もプログラムを破壊しないという保証は不可能です。

例えば次回リリース予定のv1.22ではループ変数の参照に関する修正が含まれる予定ですが、破壊的な変更なので既存コードが従来通り動作しなくなる可能性があります(極めてレアなケースですが)。

この件は以前に別の記事にまとめたので詳細は割愛します。

moneyforward-dev.jp

このような破壊的な変更が行われる背景としては主に、セキュリティ上の問題・仕様の誤り・バグなどの修正があると推察します。 Goの開発チームは、上記の問題が発生しても可能な限り既存コードに影響が出ないように最新の注意を払ってくれています。 しかしながらそれでも、時として破壊的変更を伴う選択を迫られる場合もあります。 プロダクトが予期しない不具合に見舞われないためにも、バージョンアップする際は、一度しっかりとリリース内容を精査した上で行う慎重さを忘れないことが肝要です。

テストとAPIチェックによる互換性の担保

前述のような予測可能な変更であれば問題ありませんが、予期しない破壊的な変更が取り込まれる可能性があります。 これを防ぐために、GoogleではテストとAPIチェックという2つのアプローチを採用しています。

テストでは、Google社内で使用されているGoのコードに対して、開発バージョンで定期的にテストを実施して、その影響を調査しています。

一方APIチェックでは、各パッケージのエクスポートされたAPIのリストを実際のパッケージとは別のファイルに保持しています。 そして出力されたファイルと実際のパッケージを比較して、APIの変更・削除が行われると変更を検出します。

以下がそのAPIリストですが、API バージョンごとにどのようなAPIが変更されたか確認したい時にも役立ちそうです。

github.com

この提供されたAPIリストを使って、破壊的変更が行われたAPIがプロダクトに及ぼす影響範囲を絞り込むことができそうですね。

GODEBUGによる古い動作の維持

またGoでは環境変数GODEBUGによって新たに追加された動作を除外する機能が用意されています。このGODEBUGによる互換性は少なくとも2年間(場合によっては無期限)維持されます。

このアプローチによって、互換性を損なう形で変更された動作が既存コードに影響を及ぼす場合でも、以前の動作を保持しながら、新しいバージョンの恩恵を受けることが可能になっています。

go.dev

標準ライブラリでのv2 パッケージの導入

そして最近のトピックですが、次回リリースのv1.22では標準ライブラリのmath/randで後方互換性のない変更がv2パッケージで追加される予定です。将来のリリース(おそらく Go 1.23)では移行ツールも導入される予定です。これが標準ライブラリで最初のv2パッケージです。他にもencoding/json/v2sync/v2といった提案もされており、それらの導入を進める上での試金石になりそうです。

github.com

Go 2 を気にする必要はない

このようにさまざまなアプローチで後方互換性の維持しつつ新機能の追加が行われているGoですが、公式ブログで以下のような言及がありました。

when should we expect the Go 2 specification that breaks old Go 1 programs?

The answer is never. Go 2, in the sense of breaking with the past and no longer compiling old programs, is never going to happen. Go 2 in the sense of being the major revision of Go 1 we started toward in 2017 has already happened.

※日本語訳※ 古いGo 1のプログラムを破壊するGo 2の仕様は、いつ発表されるのだろうか?

答えは絶対にない。過去と決別して古いプログラムをコンパイルしなくなるという意味でのGo 2は、決して実現しない。私たちが2017年に目指し始めたGo 1の大改訂という意味でのGo 2は、すでに実現した。

tip.golang.org

かつてはジェネリクスはGo 2で実現されるという話もありましたが、v1.18で後方互換性が維持されて実装されました。これまでの動きやこの言及を見ると、Goが後方互換性を重視していて、今後も強化されていくことが期待できると思います。

新しいバージョンの恩恵を受けよう!

ここまでの話を振り返ると、デグレードを懸念してバージョンアップを控えるというのはあまり良い選択に思えません。バージョンアップを行わなければ最新の便利機能の恩恵を受けられないからです。

例えば直近のv1.21で追加されたslicesパッケージではslice関連の冗長な記述がスッキリ書けるようになりました。また次回リリースのv1.22ではループ変数の参照に起因するバグを発生を抑制する効果が期待できます。

後方互換性を維持するために尽力されているGoの開発メンバーの方たちに感謝しつつ、来月はすぐにバージョンアップできるように準備を進めたいです!