---
applyTo: "**/*.go,go.mod"
description: Go conventions for this repository
---

# Go (1.27)

- Format with `gofmt`/`goimports`; lint with `golangci-lint run` and `go vet ./...`.
- Errors are values: return them, wrap with `fmt.Errorf("load user %d: %w", id, err)`, and check with `errors.Is`/`errors.As`. No `panic` for expected failures.
- `context.Context` is the first parameter of anything doing I/O; never store it in a struct. In tests use `t.Context()`.
- HTTP routing with the standard mux patterns (`mux.HandleFunc("GET /users/{id}", h)`, `r.PathValue("id")`) unless the repo already uses chi/gin/echo.
- Logging with `log/slog` structured key/values.
- Concurrency: prefer `errgroup` or `sync.WaitGroup.Go` over hand-rolled goroutine bookkeeping; every goroutine has a clear exit via context cancellation. Run `go test -race`.
- Accept interfaces, return concrete types; define interfaces where they're consumed and keep them small.
- Use generics only when they remove real duplication. Use `slices`, `maps`, and range-over-int/func from the stdlib.
- Pin dev tools with `tool` directives in `go.mod` and run them via `go tool <name>`.
- Tests are table-driven with `t.Run(tc.name, ...)`; use `httptest` for handlers and `testing/synctest` for time-dependent concurrency.

## Done when
`go build ./... && go vet ./... && go test -race ./...` pass.
