モジュールとは、ルートに go.mod ファイルを持つGoパッケージのディレクトリツリーです。go.mod はモジュールの名前を示し、依存する他のすべてのモジュールのバージョンを列挙します。モジュールは go mod init で作ります。
mkdir myapp
cd myapp
go mod init example.com/myapp
go: creating new go.mod: module example.com/myapp
できあがる go.mod は次のとおりです。
module example.com/myapp
go 1.24.5
これだけでビルドできます。Go 1.16以降、すべてのGoプロジェクトはモジュールであり、go build、go run .、go test はモジュールがないと動きません。
go: go.mod file not found in current directory or any parent directory; see 'go help modules'
モジュールパスの決め方
モジュールパスは、モジュール内のすべてのインポートパスの接頭辞になります。モジュールが example.com/myapp なら、internal/store ディレクトリのパッケージは example.com/myapp/internal/store としてインポートします。
| 状況 | モジュールパス |
|---|---|
| 他の人がインポートするかもしれない、GitHubで公開するコード | github.com/yourname/project |
| 会社のコード | yourcompany.com/project またはリポジトリのURL |
| 個人的なプログラムや練習 | example.com/myapp または単に myapp |
| 公開済みモジュールのバージョン2以降 | github.com/yourname/project/v2 |
ライブラリの場合、go get がリポジトリを探すのに使うので、パスはコードの置き場所と一致している必要があります。自分だけが実行するプログラムなら、パスは単なる名前です。go mod init fmt や go mod init strings のように、標準ライブラリのパッケージと同じ1単語は避けてください。ビルドが ambiguous import: found package fmt in multiple modules で失敗します。
古いGOPATHの構成の外で、引数なしで go mod init を実行すると失敗します。
go: cannot determine module path for source directory /home/ana/myapp (outside GOPATH, module path must be specified)
パスを指定してください。
依存関係を追加する
インポートを書いてから、Goに取得させます。main.go が github.com/google/uuid をインポートしているとします。モジュールがそれを知らないうちにビルドすると、はっきりした指示が出ます。
main.go:6:2: no required module provides package github.com/google/uuid; to add it:
go get github.com/google/uuid
どちらのコマンドでも解決できます。
go get github.com/google/uuid
# or, to sync go.mod with every import in the module:
go mod tidy
go get は何を変更したかを報告します。
go: downloading github.com/google/uuid v1.6.0
go: added github.com/google/uuid v1.6.0
go mod tidy はインポートをどう解決したかを報告します。
go: finding module for package github.com/google/uuid
go: downloading github.com/google/uuid v1.6.0
go: found github.com/google/uuid in github.com/google/uuid v1.6.0
これで go.mod に require の行が入ります。
module example.com/myapp
go 1.24.5
require github.com/google/uuid v1.6.0
バージョンを指定したgo get
| コマンド | 効果 |
|---|---|
go get pkg | pkgを最新リリースで追加する。すでに要求されていれば現在のバージョンのまま |
go get pkg@v1.5.0 | ちょうどv1.5.0を使う(アップグレードでもダウングレードでも) |
go get pkg@latest | 最新リリースに移る |
go get pkg@abc1234 | 特定のコミットを使う(疑似バージョンとして記録される) |
go get -u ./... | すべての依存関係を最新のマイナーまたはパッチリリースに上げる |
go get -u=patch ./... | 最新のパッチリリースにだけ上げる |
go get pkg@none | 要求を削除する |
go get go@1.24 | モジュールが必要とするGoの最低バージョンを上げる |
go get は go.mod を変更します。プログラムのビルドやインストールはもう行いません。Go 1.18以降、それは go install pkg@version の役目です。
間接的な依存関係
// indirect と付いた要求は、自分のコードが直接インポートしていないものの、ビルドに必要なモジュールです。Go 1.17以降、go.mod はビルドにパッケージを提供するすべてのモジュールを列挙するので、依存関係の依存関係がこの印付きでここに現れます。この印は go mod tidy が管理するもので、自分で付けることはありません。
go mod tidy
インポートを追加・削除したら、そのたびに go mod tidy を実行します。このコマンドは次のことを行います。
- インポートしているのに足りないパッケージの要求を追加する
- もうどこからもインポートされていない要求を削除する
- ビルドに必要な
go.sumのエントリを追加し、古いものを削除する
テストやあらゆるビルドタグの組み合わせを含め、モジュール内のすべてのパッケージを見るので、自分のプラットフォームでは使われていないように見える依存関係が残ることもあります。コミットのたびに go mod tidy を実行し、CIで差分が出ないことを確認すれば、go.mod を正確に保てます。
go.sum
go.sum は、ビルドが使うモジュールバージョンごとにハッシュを記録します。
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
1行目はモジュールのファイル全体のハッシュ、2行目はその go.mod だけのハッシュです。go コマンドはモジュールをダウンロードすると、ハッシュを go.sum と公開のチェックサムデータベース(sum.golang.org)と照合するので、改ざんされたリリースや黙って変更されたリリースではビルドが失敗します。go.sum は go.mod と一緒にコミットし、決して手で編集しないでください。
Goがバージョンを選ぶ仕組み
Goは 最小バージョン選択(minimal version selection)を使います。各モジュールは依存関係ごとに必要な最低バージョンを列挙し、ビルドはそれらの最低バージョンのうち最も高いものを使い、それより新しいものは決して使いません。自分のモジュールが uuid v1.5.0 を要求し、依存関係が uuid v1.6.0 を要求していれば、v1.7.0が存在していてもビルドはv1.6.0を使います。誰かが go get で頼まない限り、何もアップグレードされません。
その結果、ロックファイルなしでビルドが再現可能になります。go.mod と依存関係のグラフだけで、すべてのバージョンが完全に決まります。go list -m all で結果を表示できます。
go list -m all
example.com/myapp
github.com/google/uuid v1.6.0
メジャーバージョン
v2以上のモジュールは、パスにメジャーバージョンを含める必要があります:github.com/yourname/project/v2。インポートパスもそれに合わせて変わるので、project と project/v2 は別のモジュールであり、1つのビルドに両方が入れます。これは互換性のない変更に関するGoのルールで、github.com/jackc/pgx/v5 のようなインポートを見かけるのはこのためです。
replace:依存関係を手元で修正する
依存関係への変更を公開前に試すには、そのパスをローカルのディレクトリに向けます。
module example.com/myapp
go 1.24.5
require example.com/mylib v1.2.0
replace example.com/mylib => ../mylib
コマンドラインからでも設定できます。
go mod edit -replace example.com/mylib=../mylib
go mod tidy
そのディレクトリには自身の go.mod が必要です。replace はこのモジュールを直接ビルドするときにだけ効き、他の誰かがこのモジュールに依存するときには効きません。リリースのタグを付ける前に削除するのを忘れないでください。
複数のモジュールを、それぞれの go.mod に触れずに同時に編集するために、Go 1.18でワークスペースが追加されました。
go work init . ../mylib
これは go.work ファイルを書き出し、ローカルの mylib を優先させます。チーム全員が同じ構成を使うのでない限り、go.work はバージョン管理に入れないでください。
ツールの依存関係(Go 1.24)
Go 1.24で tool ディレクティブが追加され、コード生成ツールやリンターをグローバルにインストールする代わりに go.mod でバージョン管理できるようになりました。
go get -tool golang.org/x/tools/cmd/stringer
go tool stringer -type=Color
func initは別物
「golang init」で検索する人は、go mod init とは無関係の init 関数を探していることがよくあります。どのパッケージも func init() を宣言できます。引数はなく、自分のコードから呼ぶことはできず、パッケージレベルの変数が設定された後、main の前に一度だけ自動で実行されます。
1つのファイルに init 関数を複数書くこともでき、書かれた順に実行されます。インポートされたパッケージは、先に自身の初期化を終えます。init は小さく保ちましょう。失敗しうる処理は、main から呼ぶ普通の関数にしたほうが扱いやすく、テストもしやすくなります。
プライベートモジュールとプロキシ
デフォルトでは、go は proxy.golang.org を通してモジュールをダウンロードします。このプロキシはプライベートリポジトリを見られないので、どのパスがプライベートかをGoに伝えます。
go env -w GOPRIVATE=github.com/yourcompany/*
GOPRIVATE に一致するモジュールは、gitの認証情報を使ってリポジトリから直接取得され、チェックサムデータベースは使われません。
よくある質問
go mod initは何をしますか?
カレントディレクトリに go.mod ファイルを作り、そのディレクトリをモジュールのルートにします。go mod init example.com/myapp はモジュールパスとGoのバージョンを書き込みます。
module example.com/myapp
go 1.24.5
go build や go get の前に、プロジェクトごとに一度実行します。
go mod initのモジュールパスには何を指定すればよいですか?
他の人がインポートするときにコードを取得する場所で、たいていはリポジトリのパスです:go mod init github.com/yourname/project。誰もインポートしないプログラムならどんな名前でも構いません(go mod init myapp)。ただし example.com/myapp のようにドットを含むドメイン形式のパスにしておくと、標準ライブラリのパッケージ名との衝突を避けられます。
go getとgo mod tidyの違いは何ですか?
go get pkg@version は依存関係を追加するか、そのバージョンを変更します。go mod tidy はソースファイルを読み、インポートに必要なのに go.mod にないモジュールを追加し、どこからもインポートされていない要求を削除し、go.sum を更新します。インポートを書いてから go mod tidy を実行するのがよくある流れです。
go.sumはコミットすべきですか?
はい。go.sum には、ビルドが使うすべてのモジュールバージョンの暗号学的ハッシュが入っています。コミットしておくと、go コマンドが全員がバイト単位で同一の依存関係をダウンロードしていることを検証できます。手で編集してはいけません。go mod tidy が管理します。
「golang init」とgo mod initは同じものですか?
違います。単語が同じだけの別物です。go mod init は go.mod を作るターミナルのコマンドです。func init() はGoのコードに書く関数で、パッケージの変数が初期化された後、main の前に一度だけ自動で実行されます。