Architecture & Design Best Practices
Evolving Go codebases without big-bang rewrites.
Search across all documentation pages
Evolving Go codebases without big-bang rewrites.
These habits keep services testable, modules publishable, and refactors incremental as requirements change.
golangci-lint depguard rules, and architecture office hours.cmd/ only. main constructs concrete types; libraries expose New functions.internal/ for non-public implementation. Let the compiler enforce module boundaries.context.Context through request paths. Cancellation belongs on service and repository methods.internal/billing beats layer-only folders that cross-import.context.go mod tidy before merge. Clean graphs reduce surprise upgrades in CI./v2 module paths and migration notes.%w wrapping consistently. Handlers map with errors.Is, not string compare.go test ./... and go vet ./... in CI. Add golangci-lint with depguard for globals and cycles.slog or team standard with trace/request IDs at edges.go build.main. Drain in-flight requests on SIGTERM before exit.Composition in cmd/, handler-service split, internal/, and context on I/O.
Add ADRs and OTel before multi-team scale.
Allow test-only packages or build tags for integration helpers.
Block var globalDB in non-cmd paths first.
When it mirrors a stable external API you cannot split (cloud SDK facade).
Wrap and narrow at your boundary.
No.
Manual wiring until duplication hurts; ADR the switch to codegen.
Stricter exported API discipline, semver tags, and minimal dependencies.
No main - consumers wire your constructors.
Keep generated code in api/ or gen/ packages.
Map into domain types in adapters.
Quarterly or after a Sev1 caused by coupling/globals.
Use the refactoring checklist for scoring.
Yes - practices care about dependency direction, not folder names.
Use go fix modernizers and current go directive in go.mod.
ADR major toolchain bumps.
New global mutable state for request-scoped dependencies.
Reject unless ADR explains unavoidable legacy.
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