Модуль это дерево папок с пакетами Go, в корне которого лежит файл go.mod. 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: тогда сборка падает с ambiguous import: found package fmt in multiple modules.
Запуск go mod init без аргумента вне старой структуры GOPATH не работает:
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=
Первая строка хеширует файлы модуля, вторая только его 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.6.0, даже если существует v1.7.0. Ничего не обновляется, пока кто-то не попросит через go get.
Следствие: сборки воспроизводимы без lock-файла, потому что 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 это разные модули, и оба могут быть в одной сборке. Это правило 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 появились рабочие пространства (workspaces):
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» часто означают функцию init, которая никак не связана с go mod init. Любой пакет может объявить func init(). Она не принимает аргументов, её нельзя вызвать из вашего кода, и она выполняется один раз, автоматически, после того как заданы переменные уровня пакета, и до main:
В файле может быть несколько функций 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, после инициализации переменных пакета.