Go Philosophy Best Practices
Ten principles that keep Go codebases aligned with the language's ethos: readable, explicit, and operable at scale.
Search across all documentation pages
Ten principles that keep Go codebases aligned with the language's ethos: readable, explicit, and operable at scale.
go fix modernizers in 1.26 are a good trigger).golangci-lint, go vet) so philosophy becomes CI signal, not memory.gofmt own formatting. No custom alignment debates in review; use gofmt/goimports in CI.checkout over util.UserStore beats UserDAO; Go is not Java enterprise cosplay.context.Context as the first parameter on I/O boundaries. Never store context in structs.(T, error) and wrap with %w. Document sentinel errors with var ErrFoo = errors.New("...") for stable errors.Is.New in main. Avoid global var db *sql.DB in libraries; wire explicitly in cmd/.ctx.Done(), and avoid unbounded fan-out.go test -race in CI on packages that use goroutines. Philosophy without detection is wishful thinking.net/http, encoding/json, and log/slog cover many services.pprof, execution tracer). Measure aligns with Go culture; guessing does not.SIGTERM, drain handlers, and align with Kubernetes probe semantics.go fix modernizers during upgrades. Let tooling migrate idioms instead of manual churn across monorepos.govulncheck. Philosophy includes supply-chain hygiene, not only syntax./v2 module path plan.The title reflects the core cultural decalogue; sections group related enforceable rules.
Adopt the whole list even though marketing rounds to ten themes.
Enforce gofmt, go vet, golangci-lint, -race, and govulncheck in CI.
Package boundaries and API shape stay human review with ADR backup.
Frameworks are fine when they remove boilerplate without hiding lifecycle.
Philosophy still demands explicit wiring, context, and shutdown in main.
Yes for concurrency, context, and errors.
Internal code can move faster on breaking changes - but prefer clear module boundaries anyway.
Importing Java/C# enterprise patterns - giant DI graphs, wide interfaces, and stringly errors - which Go's toolchain does not reward.
Use generics to remove duplication, not to build generic frameworks.
If func Map is clearer without type parameters, skip them.
When frameworks provide tested middleware ecosystems (auth, binding, OpenTelemetry hooks) that would take quarters to replicate.
Document the dependency in an ADR.
Onboard every engineer, revisit during major Go upgrades, and after incidents caused by concurrency or API drift.
Concurrency and context rules still apply where the runtime supports them.
Memory and syscall constraints may require slimmer dependencies - verify board targets at build.
Explicit reconcile loops, context-aware client calls, structured logging, and leader election shutdown mirror service rules.
Use controller-runtime patterns instead of inventing watch glue.
Lower incident rates from panics/races, faster onboarding surveys, fewer breaking API changes, and upgrade PRs dominated by tooling (go fix) not manual edits.
Move to fundamentals, concurrency, and architecture sections once team norms are aligned.
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