git:20260509.966244f to git:20260510.fe97ca3

45 added, 57 removed. Audit A to A.

---
name: programming-go
title: "Go Development"
- description: "Go conventions, idiomatic patterns, concurrency, standard library, and testing best practices. Auto-activates in Go projects."
+ description: "Idiomatic Go architecture, concurrency design, and standard-library-first patterns. Auto-activates in Go projects."
license: Apache-2.0
compatibility: "Requires Go toolchain (go 1.21+)."
domains: developer
rules:
- file(go.mod)
- content(golang)
---
- ## Conventions
+ ## Mental model
- - Simplicity above all — Go's greatest strength is readability
- - Explicit over implicit — no magic, no hidden behavior
- - Errors are values — handle them explicitly, every time
- - Interfaces are small — prefer 1-2 method interfaces
- - Composition over inheritance — embed, don't extend
- - Standard library first — reach for stdlib before third-party
- - Lint-clean — code must be free of `go vet` and `staticcheck` warnings
- - Formatted — code must follow `gofmt` style
+ Go optimizes for code that ten people can maintain for ten years. Simplicity, explicitness, and a small standard vocabulary matter more than cleverness. Resist the urge to import patterns from Java, Ruby, or TypeScript — interfaces, generics, and inheritance hierarchies that feel natural elsewhere produce unidiomatic Go.
- ## Code Organization
+ ## Architecture
- - Flat package structure — avoid deep nesting
- - Package names: short, lowercase, no underscores (`auth`, not `auth_service`)
- - `cmd/` for binaries, `internal/` for private packages, `pkg/` for public
- - One package per directory — no exceptions
- - `main` package only in `cmd/<name>/main.go`
+ - Package boundaries reflect capabilities, not layers — `auth`, `billing`, `inventory`, not `controllers`/`services`/`repositories`
+ - Define interfaces in the consuming package, not where the type that implements them lives — keeps producers free of test-driven interface noise
+ - Accept interfaces, return concrete types — callers get flexibility, you keep options open
+ - An interface with one implementation is usually premature; wait for the second before extracting
+ - Avoid framework-style "container" packages — a `main` that wires dependencies explicitly beats hidden service locators
- ## Error Handling
+ ## Errors are values
- - Always handle errors — never ignore with `_`
- - Wrap errors: `fmt.Errorf("loading config: %w", err)`
- - Use `errors.Is()` and `errors.As()` for inspection
- - Custom error types for domain errors (implement `error` interface)
- - Sentinel errors (`var ErrNotFound = errors.New(...)`) for expected conditions
- - Return early on error — avoid deep nesting
+ - Errors are returned, not thrown — every error path is visible in the signature
+ - Wrap with `%w` to preserve the chain: `fmt.Errorf("loading %s: %w", path, err)`
+ - Sentinel errors (`var ErrNotFound = errors.New(...)`) for predictable conditions; check with `errors.Is`
+ - Typed errors with fields when callers need to inspect details; check with `errors.As(&target)`
+ - Don't wrap when adding no information; don't ignore — handle, log with context, or return
- ## Interfaces
+ ## Concurrency design
- - Define interfaces where they are USED, not where types are defined
- - Accept interfaces, return concrete types
- - Keep interfaces small: `io.Reader`, `io.Writer` are the gold standard
- - Don't create interfaces for single implementations — wait for the second
+ - Don't reach for goroutines first — sequential code is easier to read and reason about
+ - Every goroutine needs an exit story: a context, a closed channel, or a `WaitGroup` — leaks compound silently
+ - `context.Context` is the first parameter of any function that does I/O, blocks, or starts goroutines; never stored in a struct
+ - Channels coordinate ownership transfer; mutexes guard short critical sections — pick the simpler one for the job
+ - `errgroup.Group` for "do N things in parallel, fail if any fails"; `sync.WaitGroup` for fire-and-collect
+ - Close channels from the sender side only; receivers detect closure via the two-value receive
+ - Shared mutable state crosses fewer goroutines than you think — copy values, send messages, or use immutable snapshots
- ## Concurrency
+ ## Standard library first
- - Don't communicate by sharing memory — share memory by communicating
- - Goroutines are cheap but not free — always have an exit strategy
- - `context.Context` for cancellation and deadlines — first parameter always
- - `sync.WaitGroup` for goroutine lifecycle management
- - `sync.Mutex` for shared state — keep critical sections small
- - Channels for coordination, mutexes for state
- - `errgroup` for parallel work with error collection
- - Never start a goroutine without knowing how it will stop
+ - HTTP: `net/http` with `http.ServeMux` (1.22+ supports path params) — frameworks are rarely necessary
+ - JSON: `encoding/json` with struct tags; for performance-sensitive paths reach for `json.Decoder` streaming
+ - Logging: `log/slog` for structured logs — replaces third-party loggers in new code
+ - `io.Reader` / `io.Writer` are the universal data interfaces — write functions in terms of them, not `[]byte` or `string`
+ - `context`, `errors`, `slices`, `maps`, `cmp` cover most utility needs
- ## Testing
+ ## Generics — sparingly
- - Table-driven tests — idiomatic Go pattern
- - `t.Run()` for subtests with descriptive names
- - `testify/assert` for cleaner assertions
- - `httptest` for HTTP handler testing
- - Use interfaces to make code testable — inject dependencies
- - Benchmark with `testing.B` — use `b.ResetTimer()` after setup
- - `go test -race` — always run with race detector
+ - Generics are for containers and algorithms (`slices.Sort`, `sync.Pool[T]`), not for "abstracting" business logic
+ - A non-generic helper that takes `any` plus a type assertion is usually worse than two specific functions — but two specific functions are usually fine
+ - Type parameters on methods are not allowed; design around it
- ## Standard Library Patterns
+ ## Project layout
- - `http.Handler` interface for HTTP — compose with middleware
- - `context.Context` — propagate through call chains, never store in structs
- - `io.Reader` / `io.Writer` — use for streaming
- - `encoding/json` — struct tags: `json:"field_name,omitempty"`
- - `log/slog` — structured logging (Go 1.21+)
+ - `cmd/<name>/main.go` for each binary; one `main` package per binary
+ - `internal/` for packages not importable outside the module — use it liberally to keep the public API small
+ - `pkg/` only when publishing a reusable library; private services don't need it
+ - Module path matches the repo's import path; one module per repo unless there's a strong reason otherwise
+ - Flat over nested — deep package trees fight Go's import-by-name idiom
- ## Performance
+ ## Testing
- - Preallocate slices: `make([]T, 0, knownCap)`
- - Use `sync.Pool` for frequently allocated objects
- - `strings.Builder` for string concatenation in loops
- - Avoid reflection in hot paths
- - Profile with pprof: `go tool pprof`
+ - Table-driven tests with `t.Run(tc.name, ...)` — the canonical Go pattern
+ - `t.Cleanup` over `defer` for fixtures; `t.TempDir`, `t.Setenv` auto-clean
+ - Test the public API of a package; reach into internals only when an invariant can't be observed from outside
+ - Hand-written fakes beat mock frameworks for clarity; `httptest.Server` for HTTP boundaries
+ - Always run with `-race` in CI; data races are the most common Go bug