Generics Best Practices
Naming, constraint design, and gradual adoption in codebases.
Search across all documentation pages
Naming, constraint design, and gradual adoption in codebases.
These rules keep generic code readable in review, stable for callers, and aligned with stdlib patterns.
type NodeID interface { ~int64 } beats repeating union lists.cmp.Ordered and comparable over copied unions. Stay aligned with stdlib semantics.~T when named types should match. Celsius/Fahrenheit-style wrappers belong in the type set.T, K, V) or domain names (ID, E). Avoid cute multi-letter abbreviations in public APIs.internal/, simple handlers outside. Matches chi / gin / echo service layout.slices, maps, and cmp before custom copies. Maintain less, gain runtime updates.maps.Clone aliases pointer values.comparable for map-keyed sets and dedupe. Do not fake slice keys.int, string, struct). One test function per behavior, several type args.go test -race on generic containers used from goroutines. Mutex rules unchanged.Example* tests over long comment prose. Compiler checks examples.More than two or three on an exported symbol deserves scrutiny.
Compose helper types instead.
Subtests with multiple instantiations are fine.
Do not genericize testify/assert wrappers without value.
Constraint meaning, example instantiation line, and performance note if non-obvious.
Keep generated API types concrete.
Use generics in hand-written utility packages only.
When stdlib adds equivalent function or only one instantiation remains.
Simpler concrete code wins.
Aliases do not create new generic definitions.
Define generic types on named struct/map types.
Rarely in exported chains.
Internal metadata key helpers may use generics with comparable keys.
Treat like API breaks - search repo for instantiations and run full CI compile matrix.
golangci-lint staticcheck and unused analyzers catch dead instantiations.
Pair with compile-all-variants CI job for libraries.
Yes if exports stay small and constraints are named.
Over-generic public packages hurt onboarding - follow section B.
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 19, 2026