Plan 9とGo言語のブログ

主にPlan 9やGo言語の日々気づいたことを書きます。

Go & Versioning(vgo)を読んで大きな変更が入ったなと思った

この記事はQiitaで公開されていました

このQiita記事は、Go & Versioningで掲載された一連の記事を読んで、自分なりのまとめと感想です。私の周りはあまり騒いでないけど、これ感覚的なものが大きく変わるなあ、と思ったので、主に表面上に現れる変更をまとめました。

これは、Go 1.11で試験的な導入、Go 1.12で正式サポートとなる予定の機能に関する話です。

@nekketsuuuさんが原文の和訳をされています。

何が変わるのか

バージョン管理機能の導入

goコマンドにバージョン管理の機能が追加されます。バージョンは常にセマンティックバージョニングで表します。今もdepコマンドが(goとは別に)存在しますが、バージョン管理機能が追加されたgoコマンド(以下vgoと表記)は、depglideなどの依存管理ツールとは別のアプローチでバージョンを管理します。とはいえ、目的はどちらもバージョン管理なので、vgoを使う場合はdepを使いません。また、vgovendorディレクトリを使わないためvendorも不要です。

モジュールという単位の追加

バージョン管理機能に伴って、Goのパッケージにモジュールという単位が追加されます。モジュールは複数のパッケージをまとめたもので、go.modというファイルで管理します。Go & Versioningでは以下の例が記されていました。

// My hello, world

module "rsc.io/hello"

require (
    "golang.org/x/text" v0.0.0-20180208041248-4e4a3210bb54
    "rsc.io/quote" v1.5.2
)

モジュールは、vgoでバージョン管理を行う基本的な単位となります。1つのリポジトリが1つのモジュールに該当し、タグ(git tag)を使ってモジュールにバージョンを与えます。従って、バージョン管理を行う単位はモジュールです。例えば、上のgo.modでリストされているgolang.org/x/textrsc.io/quoteもモジュールです。今まではパッケージという扱いでしたが、モジュールに変わります。

モジュールのアップデートはvgo get

モジュールはgo.modファイルでバージョンを指定しますが、これを人が維持するのは大変です。vgo getで必要なモジュールを追加したり、vgo get -uでアップデートを行ったりするようです。

たくさんのExampleが書かれているのでA Tour of Versioned Goを眺めてみてください。これだけで雰囲気はつかめると思います。

破壊的変更を加える場合はimport pathを変更する

vgoで扱うパッケージは、全て後方互換性を持たなければなりません。例えばAというパッケージの作者は、メジャーバージョンが同じ間は後方互換性を維持する必要があります。この制約によって、v1.1.0を参照しているプログラムは、v1.2.1が使われた場合でも同じように動作することが保証されます。もしマイナーバージョンのアップデートでビルドが壊れた場合、Av1.2.2で過去の互換性を取り戻すべきです。例えば、関数の動作を変更する場合は、仕様変更ではなく名前を変えて新しい関数として追加しましょう。

とはいえ、どうしても破壊的な変更が避けられないケースは存在します。その場合はimport pathを変更することで別のパッケージとして作成してください。例えば、lufia.org/pkg/xに破壊的変更を加える場合、新しいバージョンはlufia.org/pkg/x/v2のようにメジャーバージョンを含むimport pathにしましょう。そうするとv1v2で重複する部分がソースコードの二重管理になりがちですが、それはtypealias等を使って頑張ってください。

最初からimport pathをlufia.org/pkg/x/v1のようにするべきかについては、最終的にどうなるかは分かりませんが、個人的には「最初はバージョンを含めない」でいいと思います。

$GOPATHが不要になる

これまで$GOPATHはGoワークスペースのルートとして必要でしたが、vgoではgo.modによってモジュールのURLやバージョンが明確に特定できるため、$GOPATHがなくてもソースコードの取得やビルドの再現性には困りません。参照するモジュールのマイナーバージョンは上がるかもしれませんが、マイナーバージョンの変更はモジュール作者の努力によって互換性が維持されるため、最終的な動作は変わらないことが保証できます。

これによって、任意のディレクトリでGoのコードを書くことが可能となります。今までのように、$GOPATH配下にワークスペースを構築する必要は無くなります。

ただこれ疑問なのは、今までの$GOPATH直下には

$ ls $GOPATH
bin pkg src

が存在していて、まあsrcpkgは無くてもいいかなと思うのですが、go getでインストールする場所としての$GOPATHはどうなるんだろう?と思いました。

何が変わらないのか

今までのコードはそのままビルド可能

vgoだからといって今までのコードが壊れることはありません。$GOPATHvendorも、不要になるだけで、そのまま使えます。go.modがないリポジトリもそのままビルドできるはずです。

depvgoが普及するまでは残る

長期的にみればdepは無くなるのかもしれませんが、少なくともvgoがリリースされて、十分に普及するまでは継続することが書かれていました。今すぐ何かが変わることはないので、そのまま使い続けても問題ないと思います。

今後の話

vgoは、この記事の最初にも書きましたけれど、Go 1.11で試験的な導入、Go 1.12で正式サポートとなる予定です。今はまだGo 1.10がリリースされたばかりなので何もしなくても良いと思いますが、半年後にGo 1.11がリリースされたら、特にパッケージ作者は以下のことに注意しましょう。

  • go.modを作成しましょう
  • セマンティックバージョニングでタグを打ちましょう
  • import pathが同一である限り後方互換性を維持しましょう