Coding Standards & Doc Conventions for Teams
Team Go standards translate community idioms into review rules your CI can enforce.
This cheatsheet collects the rules senior engineers repeat every sprint so hires and linters align faster.
How to Use This Cheatsheet
- Copy sections into your internal style guide; link each rule to a linter name where possible.
- Enable rules gradually in golangci-lint rather than flipping entire presets at once.
- Pair with an ADR index for intentional exceptions (
//nolintmust cite ADR id). - Revisit after Go minor releases when
go vetadds analyzers.
Formatting & Layout
| Rule | Enforcement | Rationale |
|---|---|---|
gofmt on save/CI | gofmt -l . fails build | Single official formatter |
goimports ordering | goimports -local github.com/yourco | Stable import blocks |
| Line length ~100-120 | golines or review | Wrapping is gofmt's job, not arbitrary breaks |
| File names | snake_case.go for tests _test.go | Matches stdlib conventions |
| Package layout | cmd/, internal/, pkg/ or domain dirs | Document which pattern your org uses |
Naming & API Surface
| Symbol | Convention | Example |
|---|---|---|
| Packages | short, lowercase, no underscores | billing, not billing_service |
| Interfaces | consumer-defined, -er when natural | type Store interface { Save(...) } in caller pkg |
| Errors | ErrFoo vars, FooError types | var ErrNotFound = errors.New("billing: not found") |
| Constructors | NewT or NewTWithOpts | func NewServer(cfg Config) *Server |
| Test helpers | helper_ prefix or t.Helper() | Avoid exporting test-only symbols |
| Generics | type params short but meaningful | func Map[T, U any](...) |
Errors & Context
| Rule | Code pattern | Review trigger |
|---|---|---|
Wrap with %w | fmt.Errorf("load cfg: %w", err) | Bare returns crossing package boundaries |
| Sentinel errors documented | godoc on var Err... | Changing sentinel text is breaking |
context first param | func Fetch(ctx context.Context, id string) | Missing ctx on IO or RPC |
| No panic in libraries | return errors | panic only in main or init misconfig |
%v vs %w | log with %v, wrap with %w | Logging wrapped chain incorrectly |
if err := db.Save(ctx, rec); err != nil {
return fmt.Errorf("billing: save invoice %s: %w", rec.ID, err)
}Testing Standards
| Rule | Tooling | Notes |
|---|---|---|
| Table-driven tests | t.Run subtests | Name cases for failure clarity |
-race on CI | go test -race ./... | Required for concurrency pkgs |
| Examples compile | go test runs Example_* | Docs stay honest in CI |
| Coverage targets | team-defined per package tier | Libraries higher than main |
No sleep in tests | use channels, synctest when available | Flaky tests are defects |
godoc Conventions
| Element | Rule | Example |
|---|---|---|
| Package comment | One per package, package foo block | Explain purpose, not implementation |
| Exported func | Starts with func name | // Parse reads ... for Parse |
| Deprecated | // Deprecated: use NewParse | Link migration issue |
| Parameters | Name in comment when non-obvious | // ctx carries deadline for outbound RPC. |
| Errors section | Document returned errors | // Returns ErrNotFound when ... |
| Example functions | In _test.go, // Output: | Shows up on pkg.go.dev |
// Package billing implements invoice storage for the payments service.
package billing
// Parse reads an invoice ID from raw user input.
// It returns ErrInvalidID when the format is not ULID.
func Parse(id string) (InvoiceID, error)golangci-lint Starter Set
| Linter | Catches | Enable when |
|---|---|---|
govet | stdlib analyzers | Always |
staticcheck | API deprecations, bugs | Always |
errcheck | ignored errors | Always |
ineffassign | dead assignments | Always |
gosec | security smells | Tune false positives per ADR |
revive | style beyond gofmt | Align rules with written guide |
gocritic | opinionated simplifications | After team workshop |
# .golangci.yml excerpt - align with this cheatsheet
linters:
enable:
- govet
- staticcheck
- errcheck
- ineffassign
run:
timeout: 5mReview & ADR Hooks
| Situation | Standard response | Document |
|---|---|---|
| New external dependency | License + module path review | DEP-ADR |
unsafe or cgo | Second reviewer required | SEC-ADR |
| Breaking exported API | Major version module or internal only | API-ADR |
//nolint | Comment cites rule + ADR | STYLE-ADR |
Ignored go vet | Forbidden without platform approval | TOOL-ADR |
FAQs
Is gofmt enough without a written style guide?
gofmt handles layout only.
Teams still need rules for errors, context, interfaces, and dependency policy.
Should we enforce Google Go Style Guide verbatim?
Use it as a baseline.
Adapt sections where your monorepo constraints differ, and record deltas in ADRs.
How do generics change naming rules?
Keep type parameters short in small helpers; exported APIs deserve descriptive constraint names.
Where do struct tags belong in style rules?
Document JSON/protobuf tag conventions once.
Enforce via linters (tagalign) if drift is common.
Can examples live outside `_test.go`?
Example functions must be in tests to compile as docs.
Use regular comments for narrative elsewhere.
How strict should line length be?
Prefer gofmt wrapping.
Hard limits matter mostly for generated code exclusions.
What about internal packages?
Same godoc rules, but exported means "exported to other teams" - still comment stable internal APIs.
How do we onboard linters without freezing CI?
Run new linters in warn mode one sprint, then promote to fail.
Should reviewers run golangci-lint locally?
Yes via make lint mirroring CI.
Authors should not rely on reviewers as linters.
How does this relate to Effective Go?
Effective Go is the philosophical source.
This cheatsheet is enforceable team policy derived from it plus your ADRs.
Related
- Community & Governance Basics - first-week standards index
- Dependency Governance & License Compliance - module policy
- Go Community & Long-Term Craft - sustaining standards over time
- Standards, Governance & Community Best Practices - team habit checklist
- Documentation and godoc Conventions - deeper godoc patterns
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).