Go-зависимости¶
Правила добавления и поддержки внешних модулей. Каждая зависимость —
это чужой код, который ты взял на supply-chain-ответственность. Прежде
чем делать go get, пройди vetting.
Содержание¶
- Принципы
- Как добавить зависимость
- Vetting checklist
- Популярные choices
go.modconvention- Upgrade (security patches)
- Major version upgrade
- Security response
- Vendor vs module
- CI security checks
- Anti-patterns
- См. также
Принципы¶
- Stdlib first. Прежде чем тянуть внешний модуль, проверь, нет ли
нужного в стандартной библиотеке.
net/http,log/slog,errors,encoding/jsonзакрывают большую часть задач. - Минимум transitive-зависимостей. Библиотека, которая сама тащит 10+ модулей, — красный флаг. Выбирай узкие фокусированные решения.
- Supply chain-проверка обязательна. Любой новый модуль проходит vetting (см. ниже). Это не бюрократия: одна необдуманная зависимость — один CVE или bootstrap malware в прод.
- Старение зависимостей — баг. Модуль без обновлений полгода +
findings в
govulncheck— апгрейд или замена, а не «оставим пока».
Как добавить зависимость¶
Последовательность в репозитории сервиса:
go get github.com/<org>/<module>@<version>
go mod tidy
go build ./...
go test ./...
govulncheck ./...
git add go.mod go.sum
git commit -m "feat: добавил <module> для <причина>"
Правила:
- Всегда коммить и
go.mod, иgo.sumодним PR.go.sumфиксирует checksum'ы — без него подмена в прокси-кэше пройдёт незамеченной. - Не редактируй
go.modруками — используйgo get/go mod tidy. indirect-секцияgo.modуправляетсяgo mod tidy; вручную туда не пиши.
Vetting checklist¶
Перед тем, как выполнить go get, пройди по чек-листу:
- Лицензия совместима. MIT, Apache-2.0, BSD-3-Clause, ISC, MPL-2.0 — принимаем. GPL/LGPL/AGPL — нельзя (копилефт вирусится на сервис). Без лицензии — нельзя (юридически нет права использовать).
- Поддерживается. Commits за последние 6 месяцев, issues не висят годами, есть релизы.
- Adoption. Звёзд > 500, либо это well-known модуль (например,
golang.org/x/*). Единичный репозиторий одного автора — повод трижды подумать. - Нет известных CVE.
govulncheck ./...послеgo get— чисто. - Код читаемый. Бегло прошёлся по исходнику, не увидел
obfuscated-фрагментов,
init()-функций с сетевыми вызовами, криптовалютных hook'ов, shell-callout'ов. - Совместим с Go 1.22+.
go.modмодуля не требует более старой версии Go, тесты проходят на нашей версии. - Автор не в denylist. Если известен случай supply-chain-атаки
от этого автора (
event-streamи прочие прецеденты) — не тянем. - Нет альтернативы в stdlib. Финальный sanity-check.
Если хотя бы один пункт не сошёлся — обсуждай в PR, прежде чем добавлять.
Популярные choices¶
Текущий стек. Перед тем как взять альтернативу любому пункту — обосновывай в PR.
| Область | Модуль |
|---|---|
| HTTP-роутер | github.com/go-chi/chi/v5 |
| Postgres driver | github.com/jackc/pgx/v5 |
| Миграции | github.com/golang-migrate/migrate/v4 |
| Event bus | github.com/ThreeDotsLabs/watermill + watermill-sql, watermill-kafka, watermill-cqrs |
| Redis client | github.com/redis/go-redis/v9 |
| Validation | github.com/go-playground/validator/v10 |
| JWT (если нужен) | github.com/golang-jwt/jwt/v5 |
| Testing helpers | github.com/stretchr/testify |
| Integration containers | github.com/testcontainers/testcontainers-go |
| Env-config | github.com/kelseyhightower/envconfig |
| OpenTelemetry | go.opentelemetry.io/otel, go.opentelemetry.io/otel/sdk |
| Prometheus client | github.com/prometheus/client_golang |
| ULID | github.com/oklog/ulid/v2 |
| UUID (если нужен) | github.com/google/uuid |
Не тянуть (есть лучше):
| Не надо | Вместо |
|---|---|
github.com/pkg/errors |
errors + fmt.Errorf("...%w", err) |
github.com/sirupsen/logrus |
log/slog |
go.uber.org/zap |
log/slog |
github.com/julienschmidt/httprouter |
github.com/go-chi/chi/v5 |
github.com/gorilla/mux |
github.com/go-chi/chi/v5 |
github.com/jinzhu/gorm, gorm.io/gorm |
pgx + ручной SQL |
github.com/lib/pq |
github.com/jackc/pgx/v5 |
go.mod convention¶
module github.com/<org>/<service>-service
go 1.22
require (
github.com/go-chi/chi/v5 v5.x.x
github.com/jackc/pgx/v5 v5.x.x
// ...
)
Правила:
- Module path соответствует hosting'у репозитория сервиса.
- Go-версия —
1.22или новее, синхронно между всеми сервисами. Повышаем одновременно или в пределах одного квартала. - Indirect-секцию управляет
go mod tidy, вручную не правим. replace-директивы допустимы только для локальной разработки (тест libver до релиза) и должны удаляться до merge. Вmain— никакихreplace.
Upgrade (security patches)¶
Minor/patch-обновление:
Правила:
- Читай release notes. Даже patch может нести breaking в документе (deprecation warning, изменение defaults).
- Прогоняй тесты. Unit + integration.
- Прогоняй
govulncheck ./...после каждого upgrade'а. - Один PR = один модуль для значимых апгрейдов. Батч-апгрейды — только если все модули обновляются до patch'а.
Массовый ежемесячный sweep: go list -u -m all → отдельный PR на
каждую цепочку зависимостей, где есть security fix.
Major version upgrade¶
Major (v1 → v2) — почти всегда breaking.
Процесс:
- Отдельный PR, не смешивать с feature.
- Обновить import path (для semver-v2+ модулей путь включает
/v2). - Пройти по breaking-changes релизных notes, починить код.
- Прогнать полный test suite с integration: testcontainers Postgres, Kafka.
- Canary deploy. 1 pod 1 час → 10% 24 часа → 100%. Смотри
http_request_errors_total,go_goroutines, память. - Если регрессия — откат, issue в upstream.
Security response¶
При появлении CVE в зависимости, которая попадает в prod:
- P1 (critical / high). Hotfix в тот же день.
go get <module>@latest govulncheck, canary, deploy. PR merge через expedited review.- P2 (medium). В течение недели. Обычный PR-flow, но приоритет выше обычных задач.
- P3 (low). В ближайшем sweep-PR.
Если upstream не отвечает / не фиксит критичный CVE:
- Fork. Создай
github.com/<your-org>/<module>с patch'ем, укажи в PR ссылку на upstream issue и apologize в code comment. - В
go.modиспользуется fork через обычный путь (fork публикуется с правильным module path), не черезreplace.replace— не для долгоживущих состояний.
Vendor vs module¶
- Module-only — стандарт.
go.mod+go.sumдостаточно, Go-proxy кэширует артефакты. CI и прод скачивают модули из прокси. - Vendor — только если нужен air-gapped build (нет доступа в
интернет из build-окружения). В текущем стеке не используется. Если
потребуется — добавляется в сервис-репо через
go mod vendorи CI-проверкаgo mod verify.
Смешивать режимы (vendor в одном сервисе, не-vendor в другом) — нельзя.
CI security checks¶
В пайплайне сервиса должны стоять следующие проверки (blocker'ы):
govulncheck ./...— блокирует merge при known CVE.go mod verify— checksum'ы вgo.sumсоответствуют скачиваемым модулям.go mod tidy -diff— нет лишних или недостающих записей вgo.mod/go.sum(gated:go mod tidyв CI должен быть no-op).trivy fs --scanners vuln .— дополнительный layer (ловит CVE, которыеgovulncheckне видит из-за слабого symbol-coverage).
SBOM — как артефакт релиза:
Прикладывается к каждому tagged-релизу сервиса.
Anti-patterns¶
go getбез коммитаgo.sum. Checksum'ы нет — кэш прокси может отдать другую версию модуля, CI этого не заметит.- Игнор
govulncheck-findings. «Это не на hot-path» — так не работает: findings это known CVE в символах, которые ты дотащил. Фикси или reproducible-exclude с обоснованием. - Pin на точную patch-версию без апгрейда.
v1.2.3forever — через полгода куча пропущенных security-patch'ей. - Зависимости на
v0.x. По semver-гарантий нет, breaking change возможен в любой patch. Тянем только если альтернативы нет, и в PR — явный комментарий «после стабилизации апгрейд». - Shell-callout'ы в Go-библиотеках. Если библиотека вызывает
внешние бинарники (
exec.Command("sh", "-c", ...)) — это canary: посмотри код и аргументы руками, или выбери другую библиотеку. - «Глубокие» вендоры. Модуль, сам зависящий от десятков
v0.xи forked-репо других авторов. Одно падение любого — сломанный build сервиса. replaceвmain.replaceпереопределяет публикованный модуль на локальный/форкнутый. Долгоживущееreplaceскрывает отgovulncheckнастоящее состояние, и следующий разработчик не поймёт, откуда берётся код.
См. также¶
security.md— supply chain вписан в общую security-модель.commits-and-prs.md— формат commit-message для upgrade-PR'ов.