Packages, Modules & Project Layout Best Practices
Module hygiene, tagging, and publishing conventions.
Search across all documentation pages
Module hygiene, tagging, and publishing conventions.
These habits keep import graphs small, releases predictable, and monorepos maintainable as teams grow.
go mod tidy, go test ./..., go vet ./..., and golangci-lint in CI./v2 for libraries).github.com/org/repo or your real domain so go get resolves forever.go.mod and go.sum together. Reproducible builds and checksum verification depend on both files.go mod tidy before every merge and release tag. Orphans and missing indirects should never surprise CI./v2 in the module path and import paths.go get module@version. Avoid blind @latest on production main without changelog review.go mod why -m to justify new transitive modules. Drop unused weight before it becomes entrenched.GOPRIVATE for private modules and disable sumdb where appropriate. Document VCS auth for CI and laptops.go.work over machine-specific replace paths in shared branches. Local overrides belong in workspace files or documented dev setup.cmd/ thin; inject wiring in main, logic elsewhere. One main package per binary under cmd/<name>/.internal/. Let the compiler enforce boundaries instead of README warnings alone.util or common. Short lowercase names; avoid import stutter at call sites.pkg/ or top-level library packages.go test ./... and go vet ./... on every tag candidate. Module consumers inherit your quality bar./v2 migrations.go mod vendor helps air-gapped CI; do not let vendor rot silently.go.mod files add operational cost.go.work when teams co-develop modules daily. Document GOWORK=off checks for publish validation.internal/ assumptions. internal does not span module boundaries even in one Git repo.When you remove or change exported APIs incompatibly.
Ship example.com/lib/v2 and keep v1 on the old path until deprecation completes.
Only for permanent forks with team agreement.
Temporary local paths belong in go.work or developer docs, not shared main branches.
Tag when consumers need a reproducible upgrade point.
Patch tags for fixes, minor for backward-compatible features.
No - clarity matters more than folder name.
Document supported import paths in README regardless of layout.
The minimum Go toolchain your module requires for language features.
Raise it deliberately when adopting new stdlib or syntax.
Apps are main modules; breaking changes are internal.
Libraries owe semver and /v2 discipline to downstream importers.
go list -m all, dependency scanners, and periodic go mod tidy after refactors.
Remove unused direct requires first.
Air-gapped builds, disaster recovery, or strict third-party audit trails.
Pair with automated vendor refresh in CI.
Treat as breaking: new path, migration guide, and possibly major version bump.
Old path may need a compatibility module or README redirect.
Tests, vet, tidy diff clean, correct tag on commit, and LICENSE present.
Proxy ingestion is eventual; verify with go list -m -versions after tag push.
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 18, 2026