> Source: https://www.ymotongpoo.com/works/goblog-ja/using-go-modules/


# Goモジュールを使う

[Using Go Modules](https://go.dev/blog/using-go-modules) by Tyler Bui-Palsulich and Eno Compton

## はじめに

この記事はシリーズの第1回です。

* **第1回 — Goモジュールを使う**（本記事）
* 第2回 — [Goモジュールへの移行](https://go.dev/blog/migrating-to-go-modules)
* 第3回 — [Goモジュールの公開](https://go.dev/blog/publishing-go-modules)
* 第4回 — [Goモジュール：v2とその先](https://go.dev/blog/v2-go-modules)
* 第5回 — [モジュールの互換性を維持する](https://go.dev/blog/module-compatibility)

**注：** モジュールを使った依存関係の管理についてのドキュメントは
[Managing dependencies](https://go.dev/doc/modules/managing-dependencies) を参照してください。

Go 1.11およびGo 1.12には、[モジュールのプレリミナリサポート](https://go.dev/doc/go1.11#modules)が
含まれています。モジュールは、依存関係のバージョン情報を明示的にし、管理しやすくするGoの
[新しい依存関係管理システム](https://go.dev/blog/versioning-proposal)です。この記事は、モジュールを
使い始めるために必要な基本的な操作を紹介する入門記事です。

モジュールとは、ルートに `go.mod` ファイルを持つファイルツリーに格納された、
[Goパッケージ](https://go.dev/ref/spec#Packages)の集まりです。`go.mod` ファイルは、モジュールの
_モジュールパス_ （ルートディレクトリに使われるインポートパスでもあります）と、ビルドを成功させるために
必要な他のモジュールである _依存関係の要求_ を定義します。各依存関係の要求は、モジュールパスと
具体的な[セマンティックバージョン](http://semver.org/)の組み合わせとして記述されます。

Go 1.11以降、`go` コマンドは、カレントディレクトリまたはその親ディレクトリのいずれかに `go.mod` が
存在する場合、そのディレクトリが `$GOPATH/src` の _外側_ にあれば、モジュールの利用を有効にします。
（`$GOPATH/src` の内側では、互換性のために、たとえ `go.mod` が見つかったとしても、`go` コマンドは
引き続き従来のGOPATHモードで動作します。詳細は
[goコマンドのドキュメント](https://go.dev/cmd/go/#hdr-Preliminary_module_support)を参照してください。）
Go 1.13以降では、モジュールモードがすべての開発においてデフォルトになる予定です。

この記事では、モジュールを使ってGoのコードを開発する際によく発生する一連の操作を順に見ていきます。

* 新しいモジュールの作成
* 依存関係の追加
* 依存関係のアップグレード
* 新しいメジャーバージョンへの依存関係の追加
* 依存関係の新しいメジャーバージョンへのアップグレード
* 使われていない依存関係の削除

## 新しいモジュールを作成する

新しいモジュールを作成してみましょう。

`$GOPATH/src` の外側のどこかに新しい空のディレクトリを作成し、そのディレクトリに `cd` してから、
新しいソースファイル `hello.go` を作成します。

```go
package hello

func Hello() string {
    return "Hello, world."
}
```

テストも書いてみましょう。`hello_test.go` に書きます。

```go
package hello

import "testing"

func TestHello(t *testing.T) {
    want := "Hello, world."
    if got := Hello(); got != want {
        t.Errorf("Hello() = %q, want %q", got, want)
    }
}
```

この時点では、ディレクトリにはパッケージは含まれていますが、モジュールはまだ含まれていません。
`go.mod` ファイルが存在しないからです。もし `/home/gopher/hello` で作業していて、ここで
`go test` を実行すると、次のようになります。

```
$ go test
PASS
ok      _/home/gopher/hello 0.020s
$
```

最後の行はパッケージ全体のテスト結果の要約です。私たちは `$GOPATH` の外側で作業しており、
かつどのモジュールの外側でもあるため、`go` コマンドはカレントディレクトリのインポートパスを
知りません。そのため、ディレクトリ名に基づいた偽のインポートパス `_/home/gopher/hello` を
作り出します。

`go mod init` を使ってカレントディレクトリをモジュールのルートにし、もう一度 `go test` を
試してみましょう。

```
$ go mod init example.com/hello
go: creating new go.mod: module example.com/hello
$ go test
PASS
ok      example.com/hello   0.020s
$
```

おめでとうございます！ あなたは最初のモジュールを書き、テストしました。

`go mod init` コマンドは `go.mod` ファイルを書き出しました。

```
$ cat go.mod
module example.com/hello

go 1.12
$
```

`go.mod` ファイルはモジュールのルートにのみ存在します。サブディレクトリ内のパッケージは、
モジュールパスにサブディレクトリへのパスを加えたインポートパスを持ちます。たとえば、
サブディレクトリ `world` を作成したとしても、そこで `go mod init` を実行する必要はありませんし、
実行すべきでもありません。そのパッケージは自動的に `example.com/hello` モジュールの一部として
認識され、インポートパスは `example.com/hello/world` になります。

## 依存関係を追加する

Goモジュールを作った主な動機は、他の開発者が書いたコードを利用する（つまり、それに対する
依存関係を追加する）際の体験を改善することでした。

`hello.go` を更新して `rsc.io/quote` をインポートし、それを使って `Hello` を実装してみましょう。

```go
package hello

import "rsc.io/quote"

func Hello() string {
    return quote.Hello()
}
```

もう一度テストを実行してみましょう。

```
$ go test
go: finding rsc.io/quote v1.5.2
go: downloading rsc.io/quote v1.5.2
go: extracting rsc.io/quote v1.5.2
go: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: downloading rsc.io/sampler v1.3.0
go: extracting rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
PASS
ok      example.com/hello   0.023s
$
```

`go` コマンドは、`go.mod` に記載された特定の依存モジュールのバージョンを使ってインポートを
解決します。`go.mod` 内のどのモジュールからも提供されていないパッケージのインポートに
遭遇すると、`go` コマンドは自動的にそのパッケージを含むモジュールを探し出し、最新バージョンを
使って `go.mod` に追加します。（「最新」とは、タグ付けされた最新の安定版
（[プレリリース](https://semver.org/#spec-item-9)ではない）バージョン、それがなければタグ付け
された最新のプレリリースバージョン、それもなければタグ付けされていない最新のバージョンと
定義されます。）今回の例では、`go test` が新しいインポート `rsc.io/quote` を解決し、モジュール
`rsc.io/quote v1.5.2` に対応させました。また、`rsc.io/quote` が使っている2つの依存関係、
`rsc.io/sampler` と `golang.org/x/text` もダウンロードしました。`go.mod` ファイルに記録される
のは直接の依存関係のみです。

```
$ cat go.mod
module example.com/hello

go 1.12

require rsc.io/quote v1.5.2
$
```

2回目の `go test` コマンドはこの作業を繰り返しません。`go.mod` はすでに最新の状態になっており、
ダウンロードされたモジュールはローカル（`$GOPATH/pkg/mod` の中）にキャッシュされているからです。

```
$ go test
PASS
ok      example.com/hello   0.020s
$
```

`go` コマンドによって新しい依存関係の追加は素早く簡単になりますが、それには代償がないわけでは
ないことに注意してください。あなたのモジュールは、正確性、セキュリティ、適切なライセンスと
いった重要な観点において、文字通りその新しい依存関係に依存することになります。これらはほんの
一例にすぎません。より詳しい考察については、Russ Coxのブログ記事
「[Our Software Dependency Problem](https://research.swtch.com/deps)」を参照してください。

先ほど見たように、1つの直接的な依存関係を追加すると、しばしば他の間接的な依存関係も一緒に
持ち込まれます。コマンド `go list -m all` は、カレントモジュールとそのすべての依存関係を
一覧表示します。

```
$ go list -m all
example.com/hello
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
$
```

`go list` の出力では、_メインモジュール_ とも呼ばれるカレントモジュールが常に最初の行にあり、
その後にモジュールパス順にソートされた依存関係が続きます。

`golang.org/x/text` のバージョン `v0.0.0-20170915032832-14c0d48ead0c` は
[疑似バージョン](https://go.dev/ref/mod#pseudo-versions)の一例です。これは、タグ付けされて
いない特定のコミットを表すための `go` コマンドのバージョン構文です。

`go.mod` に加えて、`go` コマンドは `go.sum` というファイルも管理しています。このファイルには、
特定のモジュールバージョンの内容に対する期待される
[暗号学的ハッシュ値](https://go.dev/cmd/go/#hdr-Module_downloading_and_verification)が
含まれています。

```
$ cat go.sum
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZO...
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:Nq...
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3...
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPX...
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/Q...
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9...
$
```

`go` コマンドは `go.sum` ファイルを使って、これらのモジュールを今後ダウンロードする際に、
最初にダウンロードしたものと同じビット列が取得されることを保証します。これにより、悪意による
ものであれ、偶発的なものであれ、その他の理由であれ、プロジェクトが依存しているモジュールが
予期せず変化しないことが保証されます。`go.mod` と `go.sum` の両方をバージョン管理システムに
チェックインすべきです。

## 依存関係をアップグレードする

Goモジュールでは、バージョンはセマンティックバージョンのタグで参照されます。セマンティック
バージョンには、メジャー、マイナー、パッチの3つの部分があります。たとえば `v0.1.2` では、
メジャーバージョンは0、マイナーバージョンは1、パッチバージョンは2です。ここでは、いくつかの
マイナーバージョンのアップグレードを見ていきましょう。次の節では、メジャーバージョンの
アップグレードについて考えます。

`go list -m all` の出力から、`golang.org/x/text` のタグ付けされていないバージョンを使っている
ことがわかります。タグ付けされた最新バージョンにアップグレードして、すべてがまだうまく動くか
テストしてみましょう。

```
$ go get golang.org/x/text
go: finding golang.org/x/text v0.3.0
go: downloading golang.org/x/text v0.3.0
go: extracting golang.org/x/text v0.3.0
$ go test
PASS
ok      example.com/hello   0.013s
$
```

やった！ すべてパスしました。もう一度 `go list -m all` と `go.mod` ファイルを見てみましょう。

```
$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
$ cat go.mod
module example.com/hello

go 1.12

require (
    golang.org/x/text v0.3.0 // indirect
    rsc.io/quote v1.5.2
)
$
```

`golang.org/x/text` パッケージはタグ付けされた最新バージョン（`v0.3.0`）にアップグレードされ
ました。`go.mod` ファイルも `v0.3.0` を指定するように更新されています。`indirect` という
コメントは、その依存関係がこのモジュールから直接使われているのではなく、他のモジュールの
依存関係を通じて間接的にのみ使われていることを示しています。詳細は `go help modules` を
参照してください。

次に `rsc.io/sampler` のマイナーバージョンをアップグレードしてみましょう。先ほどと同じように、
`go get` を実行してからテストを実行します。

```
$ go get rsc.io/sampler
go: finding rsc.io/sampler v1.99.99
go: downloading rsc.io/sampler v1.99.99
go: extracting rsc.io/sampler v1.99.99
$ go test
--- FAIL: TestHello (0.00s)
    hello_test.go:8: Hello() = "99 bottles of beer on the wall, 99 bottles of beer, ...", want "Hello, world."
FAIL
exit status 1
FAIL    example.com/hello   0.014s
$
```

おっと！ このテストの失敗は、`rsc.io/sampler` の最新バージョンが私たちの使い方と互換性が
ないことを示しています。このモジュールで利用可能なタグ付けされたバージョンを一覧表示して
みましょう。

```
$ go list -m -versions rsc.io/sampler
rsc.io/sampler v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99
$
```

私たちはこれまで `v1.3.0` を使っていました。`v1.99.99` は明らかにだめなようです。代わりに
`v1.3.1` を試してみましょう。

```
$ go get rsc.io/sampler@v1.3.1
go: finding rsc.io/sampler v1.3.1
go: downloading rsc.io/sampler v1.3.1
go: extracting rsc.io/sampler v1.3.1
$ go test
PASS
ok      example.com/hello   0.022s
$
```

`go get` の引数の中で明示的に `@v1.3.1` と指定していることに注目してください。一般に、
`go get` に渡す各引数には明示的なバージョンを指定できます。デフォルトは `@latest` で、これは
前述の定義に従って最新バージョンに解決されます。

## 新しいメジャーバージョンへの依存関係を追加する

パッケージに新しい関数を追加してみましょう。`func Proverb` はGoの並行性に関する格言を返します。
これはモジュール `rsc.io/quote/v3` が提供する `quote.Concurrency` を呼び出すことで実現します。
まず `hello.go` を更新してこの新しい関数を追加します。

```go
package hello

import (
    "rsc.io/quote"
    quoteV3 "rsc.io/quote/v3"
)

func Hello() string {
    return quote.Hello()
}

func Proverb() string {
    return quoteV3.Concurrency()
}
```

次に `hello_test.go` にテストを追加します。

```go
func TestProverb(t *testing.T) {
    want := "Concurrency is not parallelism."
    if got := Proverb(); got != want {
        t.Errorf("Proverb() = %q, want %q", got, want)
    }
}
```

これでコードをテストできます。

```
$ go test
go: finding rsc.io/quote/v3 v3.1.0
go: downloading rsc.io/quote/v3 v3.1.0
go: extracting rsc.io/quote/v3 v3.1.0
PASS
ok      example.com/hello   0.024s
$
```

私たちのモジュールが `rsc.io/quote` と `rsc.io/quote/v3` の両方に依存するようになったことに
注目してください。

```
$ go list -m rsc.io/q...
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0
$
```

Goモジュールの異なるメジャーバージョン（`v1`、`v2`、というように）はそれぞれ異なるモジュール
パスを使います。`v2` 以降では、パスの末尾はそのメジャーバージョンで終わらなければなりません。
この例では、`rsc.io/quote` の `v3` はもはや `rsc.io/quote` ではなく、モジュールパス
`rsc.io/quote/v3` によって識別されます。この慣習は
[セマンティックインポートバージョニング](https://research.swtch.com/vgo-import)と呼ばれ、
互換性のないパッケージ（メジャーバージョンが異なるもの）に異なる名前を与えます。対照的に、
`rsc.io/quote` の `v1.6.0` は `v1.5.2` と後方互換性があるはずなので、`rsc.io/quote` という
名前を再利用します。（前節では、`rsc.io/sampler` の `v1.99.99` は `rsc.io/sampler` の
`v1.3.0` と後方互換性があるはずでしたが、バグやモジュールの挙動に対するクライアント側の
誤った想定は、どちらも起こり得ることです。）

`go` コマンドは、1つのビルドに特定のモジュールパスのバージョンを最大でも1つしか含められません。
つまり、メジャーバージョンごとに最大1つ、`rsc.io/quote` を1つ、`rsc.io/quote/v2` を1つ、
`rsc.io/quote/v3` を1つ、というようになります。これによって、モジュールの作者は、1つの
モジュールパスが重複しうるかどうかについて明確なルールを持てます。1つのプログラムが
`rsc.io/quote v1.5.2` と `rsc.io/quote v1.6.0` の両方でビルドされることはあり得ません。
同時に、（パスが異なるため）モジュールの異なるメジャーバージョンを許容することで、モジュールの
利用者は新しいメジャーバージョンへ段階的にアップグレードできるようになります。この例では、
`rsc/quote/v3 v3.1.0` の `quote.Concurrency` を使いたいものの、`rsc.io/quote v1.5.2` の
使用箇所をまだ移行する準備ができていない、という状況でした。段階的に移行できる能力は、
大規模なプログラムやコードベースにおいて特に重要です。

## 依存関係を新しいメジャーバージョンにアップグレードする

`rsc.io/quote` の利用から `rsc.io/quote/v3` のみの利用への移行を完了させましょう。メジャー
バージョンが変わったことで、一部のAPIが削除されたり、名前が変わったり、その他互換性のない形で
変更されたりしていることが予想されます。ドキュメントを読むと、`Hello` が `HelloV3` になって
いることがわかります。

```
$ go doc rsc.io/quote/v3
package quote // import "rsc.io/quote/v3"

Package quote collects pithy sayings.

func Concurrency() string
func GlassV3() string
func GoV3() string
func HelloV3() string
func OptV3() string
$
```

`hello.go` 内の `quote.Hello()` の使用を `quoteV3.HelloV3()` を使うように更新できます。

```go
package hello

import quoteV3 "rsc.io/quote/v3"

func Hello() string {
    return quoteV3.HelloV3()
}

func Proverb() string {
    return quoteV3.Concurrency()
}
```

この時点で、名前を変えてインポートする必要はもうないので、それを元に戻せます。

```go
package hello

import "rsc.io/quote/v3"

func Hello() string {
    return quote.HelloV3()
}

func Proverb() string {
    return quote.Concurrency()
}
```

すべてが正しく動いていることを確認するために、テストを再実行してみましょう。

```
$ go test
PASS
ok      example.com/hello       0.014s
```

## 使われていない依存関係を削除する

`rsc.io/quote` の使用箇所はすべて削除しましたが、`go list -m all` の出力や `go.mod` ファイル
にはまだ表示されています。

```
$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1
$ cat go.mod
module example.com/hello

go 1.12

require (
    golang.org/x/text v0.3.0 // indirect
    rsc.io/quote v1.5.2
    rsc.io/quote/v3 v3.0.0
    rsc.io/sampler v1.3.1 // indirect
)
$
```

なぜでしょうか？ `go build` や `go test` のような単一のパッケージのビルドでは、何かが不足していて
追加が必要かどうかは簡単に判断できますが、何かを安全に削除できるかどうかは判断できないから
です。依存関係の削除は、モジュール内のすべてのパッケージと、それらのパッケージに対して考えられる
すべてのビルドタグの組み合わせを確認した後でなければ行えません。通常のビルドコマンドはこの情報を
読み込まないため、依存関係を安全に削除できないのです。

`go mod tidy` コマンドは、これらの使われていない依存関係を整理してくれます。

```
$ go mod tidy
$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1
$ cat go.mod
module example.com/hello

go 1.12

require (
    golang.org/x/text v0.3.0 // indirect
    rsc.io/quote/v3 v3.1.0
    rsc.io/sampler v1.3.1 // indirect
)

$ go test
PASS
ok      example.com/hello   0.020s
$
```

## まとめ

Goモジュールは、Goにおける依存関係管理の未来です。モジュールの機能は、現在サポートされている
すべてのGoのバージョン（つまりGo 1.11とGo 1.12）で利用できます。

この記事では、Goモジュールを使った次のようなワークフローを紹介しました。

* `go mod init` は新しいモジュールを作成し、それを説明する `go.mod` ファイルを初期化します。
* `go build`、`go test`、その他のパッケージビルドコマンドは、必要に応じて新しい依存関係を
  `go.mod` に追加します。
* `go list -m all` はカレントモジュールの依存関係を表示します。
* `go get` は依存関係の必要バージョンを変更します（あるいは新しい依存関係を追加します）。
* `go mod tidy` は使われていない依存関係を削除します。

ローカルでの開発でモジュールを使い始め、プロジェクトに `go.mod` と `go.sum` ファイルを
追加することをおすすめします。フィードバックを提供し、Goにおける依存関係管理の未来を形作る
手助けをしていただくために、[バグ報告](https://go.dev/issue/new)や
[experience report](https://go.dev/wiki/ExperienceReports)をお送りください。

これまでいただいたフィードバックと、モジュールの改善へのご協力に感謝します。

By Tyler Bui-Palsulich and Eno Compton

