Irbisa · Шпаргалка

Модули, сборка и тулинг

go.mod и MVS, build-теги, embed, ldflags, линтеры, статические бинарники

Middle-разработчик · 24 вопроса

  1. 01

    Что такое GOROOT и GOPATH и почему с появлением модулей GOPATH значит гораздо меньше?

    GOROOT указывает на место, где лежит сам тулчейн Go: компилятор, команда go и стандартная библиотека.

  2. 02

    Чем пакет отличается от модуля в Go?

    Пакетом называют директорию с .go-файлами с одинаковой строкой package: единицу, которую импортируют и собирает компилятор.

  3. 03

    Что хранят go.mod и go.sum и какой из них решает, какие версии зависимостей попадут в сборку?

    Решает go.mod: в нём путь модуля, версия Go и минимальная версия каждой зависимости.

    // go.mod: which versions are builtmodule github.com/acme/billing go 1.25 require (…
  4. 04

    Что делает go mod tidy, чем go get отличается от ручной правки go.mod и что проверяет go mod verify?

    go mod tidy приводит go.mod и go.sum в соответствие с импортами: добавляет недостающие требования и убирает лишние.

  5. 05

    Что значит пометка // indirect у строки require в go.mod и почему в современном go.mod таких строк так много?

    // indirect помечает модуль, который не импортирует ни один ваш пакет: он есть в go.mod, потому что он нужен зависимости или потому что вы сами намеренно подняли его версию.

    // go.mod of a go 1.17+ module: two require blocksmodule github.com/acme/billing go 1.25 require (…
  6. 06

    Как сегодня установить консольную Go-утилиту вроде mockgen и почему go get больше не даёт бинарник?

    Используйте go install <пакет>@<версия>.

    # wrong since Go 1.18: adds a requirement to go.mod, builds nothing$ go get go.uber.org/mock/mockgen$ mockgen -versionzsh: command not found: mockgen # right: build a pinned version into $GOBIN, ignoring the current go.mod…
  7. 07

    Что делает go generate, почему go build никогда его не запускает и нужно ли коммитить сгенерированные файлы?

    go generate выполняет команды, записанные в комментариях //go:generate, в директории каждого файла, чтобы написать Go-код: моки, методы String, запросы sqlc.

    package order //go:generate go tool stringer -type=Status type Status int…
  8. 08

    Что ловят gofmt, go vet, staticcheck и golangci-lint и как запускать их в CI?

    gofmt выравнивает форматирование, go vet находит код, который компилируется, но почти наверняка ошибочен, staticcheck добавляет более глубокие проверки на баги и упрощения, а golangci-lint гоняет их и многие другие за один проход с одним конфигом.

    # .golangci.yml (golangci-lint v2)version: "2" linters:  default: standard   # errcheck, govet, ineffassign, staticcheck, unused  enable:…
  9. 09

    Как документировать Go-код и как выглядят doc-комментарии и команда go doc?

    Doc-комментарием служит обычный комментарий // прямо над экспортируемым объявлением, который начинается с имени описываемой сущности.

    // Package billing charges customers and issues invoices.package billing import "errors" // ErrDeclined is returned when the card is refused.…
  10. 10

    Какая версия golang.org/x/net попадёт в сборку при таких требованиях и почему Go не нужен lock-файл, чтобы она не менялась?

    // your go.mod (without the // indirect lines tidy would add)require (	github.com/acme/kit v1.3.0	github.com/acme/api v0.9.0) // kit v1.3.0's go.mod:  require golang.org/x/net v0.28.0// api v0.9.0's go.mod:  require golang.org/x/net v0.31.0 // newest x/net on the proxy today: v0.45.0

    v0.31.0. Go использует Minimal Version Selection: каждая строка require задаёт минимум, и сборка берёт наибольший из заявленных минимумов, а не более новый релиз только потому, что он существует.

  11. 11

    Зачем модулю v2 нужен /v2 в пути модуля и что сломается, если поставить тег v2.0.0, не поменяв путь?

    Это semantic import versioning: начиная с v2 мажорная версия входит в путь модуля (module github.com/acme/lib/v2) и в каждый импорт, потому что ломающий релиз считается другим модулем.

    // the library's go.mod, from v2 on://   module github.com/acme/lib/v2 package main import (…
  12. 12

    Ваша библиотека обходит сломанную зависимость через replace в своём go.mod, но сервисы, импортирующие библиотеку, всё равно получают сломанную версию. Почему?

    replace и exclude действуют только в главном модуле (том, который собирают).

  13. 13

    Как заставить Go скачивать приватные модули из корпоративного Git и как связаны GOPROXY, GOPRIVATE и вендоринг?

    Задайте в GOPRIVATE префиксы приватных путей, например git.corp.example/*.

    # wrong: a private path goes to the public mirror and checksum DB$ go get git.corp.example/team/lib@v1.2.0verifying module: git.corp.example/team/lib@v1.2.0: reading https://sum.golang.org/lookup/...: 410 Gone # right: one variable covers GONOPROXY and GONOSUMDB for these prefixes$ go env -w GOPRIVATE='git.corp.example/*,github.com/acme/*'…
  14. 14

    Что задаёт строка go в go.mod и как строка toolchain и GOTOOLCHAIN решают, какой Go на самом деле соберёт код?

    Строка go задаёт версию языка, с которой компилируется код модуля, и, начиная с Go 1.21, минимальный релиз Go, которому разрешено его собирать.

    // go.modmodule github.com/acme/billing go 1.24.0          // language version + minimum toolchaintoolchain go1.25.3 // preferred toolchain when this is the main module…
  15. 15

    Как зафиксировать версии dev-утилит вроде mockgen или stringer для всей команды: старый файл tools.go или директива tool из Go 1.24?

    С Go 1.24 используйте директиву tool: go get -tool pkg@version записывает утилиту в go.mod, а go tool <имя> запускает ровно эту версию.

    # Go 1.24+: record the tool in go.mod$ go get -tool go.uber.org/mock/mockgen@v0.6.0 # go.mod now containstool go.uber.org/mock/mockgen…
  16. 16

    Как организовать структуру репозитория Go-сервиса и что гарантирует директория internal?

    Держите структуру плоской и группируйте по доменам: по директории cmd/<бинарник> на каждый исполняемый файл с тонким main, а остальной код кладите в internal/.

    github.com/acme/billing/├── go.mod├── cmd/│   ├── api/main.go        // one directory per binary: config + wiring only│   └── worker/main.go└── internal/              // importable only from github.com/acme/billing/...…
  17. 17

    Почему Go запрещает циклические импорты и как разорвать цикл между двумя вашими пакетами?

    Go компилирует и инициализирует пакеты по одному в порядке зависимостей, а цикл не оставляет допустимого порядка, поэтому import cycle not allowed считается ошибкой сборки.

    package store import (	"context"	"database/sql")…
  18. 18

    Что такое ограничения сборки (//go:build, суффиксы _linux.go) и как кросс-компилировать Go-бинарник под другую ОС и архитектуру?

    Ограничение сборки (build constraint) решает, входит ли файл в сборку при текущих GOOS, GOARCH, версии Go и -tags.

    // file: internal/store/pg_integration_test.go//go:build integration package store import "testing"…
  19. 19

    Программа собрана с -ldflags="-X main.version=1.4.0 -X main.commit=abc -X main.buildTime=2026-10-14 -X main.env=prod" и запущена без APP_ENV. Что она напечатает?

    package main import (	"fmt"	"os") const commit = "none" var version = "dev"var buildTime stringvar env = os.Getenv("APP_ENV") func main() {	fmt.Printf("%s %s %s %q\n", version, commit, buildTime, env)}

    Она напечатает 1.4.0 none 2026-10-14 "".

  20. 20

    Как написать production-Dockerfile для Go-сервиса и что ломается, когда бинарник запускается на scratch?

    Используйте multi-stage сборку: соберите статический бинарник с CGO_ENABLED=0 в образе golang, а затем скопируйте только его в distroless/static или scratch.

    # syntax=docker/dockerfile:1FROM golang:1.25 AS buildWORKDIR /srcCOPY go.mod go.sum ./RUN go mod download                 # cached until go.mod/go.sum changeCOPY . .…
  21. 21

    Чем govulncheck отличается от сканера зависимостей, сравнивающего версии, и что он проверяет?

    govulncheck сообщает об уязвимости, только если ваш код действительно может дотянуться до уязвимой функции: он использует граф вызовов и базу уязвимостей Go.

  22. 22

    Один и тот же коммит собирается на всех ноутбуках, но в CI падает с no required module provides package, а в репозитории лежит go.work из gitignore. Что происходит?

    go.work меняет граф модулей на той машине, где он лежит: его строки use делают перечисленные директории главными модулями, поэтому ноутбуки берут example.com/acme/lib из соседней директории.

    // go.work at the repo root: git-ignored here, which is the whole buggo 1.25 use (	./api	./lib…
  23. 23

    Два прогона CI на одном коммите дают бинарники с разными хешами. Что делает сборку Go воспроизводимой и как бинарник хранит сведения о своей сборке?

    Сборка Go детерминирована при тех же исходниках, зависимостях, тулчейне, флагах и окружении.

    # same commit on two machines: the hashes should match$ CGO_ENABLED=0 go build -trimpath -o app ./cmd/app$ sha256sum app # what the binary says about itself$ go version -m app…
  24. 24

    Что такое profile-guided optimisation (PGO) в Go, что она на самом деле оптимизирует и как внедрить её в сервис?

    PGO передаёт компилятору CPU-профиль из продакшена, и тот оптимизирует код, который действительно горячий.

    # 1. CPU profiles from production, under real traffic$ curl -o cpu-1.pprof 'http://api-1:6060/debug/pprof/profile?seconds=30'$ curl -o cpu-2.pprof 'http://api-2:6060/debug/pprof/profile?seconds=30' # 2. merge them so one instance or one spike does not dominate$ go tool pprof -proto cpu-1.pprof cpu-2.pprof > cmd/api/default.pgo…