- Use only
if,for,switch. - Avoid
goto; forbid recursion (acyclic call graph). - Keep
selectsmall and predictable (few cases, no nesting). - Prefer clarity over cleverness; explain why, not what.
- Every loop must have a provable upper bound.
- Service loops must include documented shutdown (e.g.,
context.Context, closed channel). - Ranged loops over containers are acceptable.
- After initialisation, avoid heap allocation (
make,new,appendthat grow). - Pre-size slices/maps; reuse buffers.
- Allow at most one level of pointer indirection.
- Forbid
unsafeand reflection in critical paths; if used, isolate in a tiny reviewed package.
- Keep functions to ~60 lines max (one screen).
- Split rather than nest logic.
- Average ~2 assertions per function (assertions must not mutate state).
- Never discard
err; always check return values. - Validate inputs at function boundaries.
- Propagate errors with context (
fmt.Errorf("... %w", err)). - Justify any ignored results in comments.
- Declare variables in the smallest possible scope.
- Avoid package-level state.
- Prefer unexported identifiers.
- Pass dependencies explicitly.
- Favour the simplest code that meets requirements.
- Use core language first, then stdlib, then internal libraries—before adding dependencies.
- Match existing code style; consistency beats novelty unless justified.
- Use
MixedCaps; no underscores (except_test.go, cgo, or generated code). - Preserve case for initialisms (
ID,URL,API). - Avoid redundant names (
widget.New, notwidget.NewWidget). - Short names for small scopes, longer for larger ones.
- Package names: short, lowercase, descriptive; no
utilorhelper. - Group strongly related functionality; avoid monoliths and excess fragmentation.
- File names may include underscores; identifiers should not.
- In tests, name doubles clearly (
Stub,AlwaysCharges, etc.).
- Write doc comments as full sentences, starting with the identifier name.
- Focus on rationale (why), not repetition of code (what).
- Provide runnable examples for public APIs.
- Wrap for readability (~80 cols), but no hard limit.
- Organise imports in this order:
- Standard library
- External packages
- (Optional) protobuf imports (
foopb …) - Side-effect imports (
_ "…")
- Keep build tags and generated code minimal, each justified.
- Maintain comprehensive tests with clear diagnostics.
- Incorporate fuzzing tests for critical input validation and parsing logic using Go's built-in fuzzing
go test -fuzz=Fuzzfor functions namedFuzz*. - Run
go vet ./...,golangci-lint run ./..., andgo test -race ./...with zero warnings. - Add benchmarks to enforce allocation rules in hot paths.
- Treat errors as values; design clear, user-friendly messages.
- Prefer direct returns over panics; use
paniconly for truly unrecoverable states.
- Formatting: always
gofmt. - Identifiers: MixedCaps, case-preserving initialisms.
- Functions: small, cohesive, bounded.
- Errors: always checked, propagated with context.
- Safety: no recursion, unsafe, or unchecked allocations.
- Consistency: align with existing code; deviations must be justified.
✅ This unified style blends NASA's Power of Ten reliability rules with Google's Go style guide and best practices into a concise, enforceable set for robust, maintainable Go code.