## Code Style - Follow standard Go conventions: `gofmt`, `goimports`, and `golangci-lint`. - Keep packages small and focused. Name them short, lowercase, no underscores. - Avoid unnecessary abstractions — prefer simple, readable code over clever code. - Define interfaces at the point of use, not the point of implementation. Keep them small. ## Error Handling - Always handle errors explicitly — never ignore them with `_`. - Wrap errors with context: `fmt.Errorf("doing X: %w", err)`. - Use `errors.Is` / `errors.As` for matching, not string comparison. - Reserve `panic` for unrecoverable programmer mistakes, not runtime errors. ## Concurrency - Pass `context.Context` as the first argument to any blocking or I/O function. - Never store a `Context` in a struct. Never use `context.Background()` deep in business logic. - Always manage goroutine lifecycles — use `errgroup`, `sync.WaitGroup`, or channels. - Protect shared state with mutexes or by confining it to a single goroutine. ## Testing - Write table-driven tests using `t.Run` for clarity and coverage. - Use the standard `testing` package. Reach for `testify` only when it genuinely reduces noise. - Prefer real implementations over mocks where feasible (e.g., `httptest`, in-memory stores). - Keep tests close to the code they test; use `_test` packages for black-box testing. - Benchmark with `testing.B` before optimizing anything. ## Dependencies & Modules - Keep `go.mod` tidy — run `go mod tidy` before committing. - Minimize external dependencies; prefer the standard library. - Pin versions explicitly and review dependency updates carefully. ## General - Measure before optimizing. Use `pprof` for profiling. - Log at boundaries (entry/exit of services), not inside every function. - Prefer explicit over implicit — avoid `init()` and global state. ## Documentation - Document every exported type, function, method, and constant — no exceptions. - Follow Go doc conventions: start the comment with the name of the thing being documented. - Package-level comments should explain purpose and usage, not implementation details. ## Testing (expanded) - Write tests as you write code — not after, not when asked. Tests are not optional. - Every exported function and critical internal path must have at least one test. - Cover edge cases, error paths, and boundary conditions — not just the happy path. - Use `t.Helper()` in shared test utilities to keep failure output pointing at the call site. - Integration tests live in `test/` or behind a build tag (e.g., `//go:build integration`). ## Security - Never log secrets, tokens, passwords, or PII — scrub them before logging. - Validate and sanitize all external input; never trust data from outside the process boundary. - Use `crypto/rand` for all randomness that touches security; never `math/rand`. - Scan dependencies for known CVEs regularly (e.g., `govulncheck`). - Set strict timeouts on all outbound HTTP clients — never use the default zero-timeout client.