Idiomatic Go Rules
Effective Go: error handling, interfaces, goroutines, project layout.
# CLAUDE.md — Idiomatic Go
## Project layout
- `cmd/<app>/main.go` per binary.
- `internal/` for code that other modules can't import.
- Top-level packages for public, importable code.
- Don't create a `pkg/` folder — it adds noise and Go doesn't require it.
- One package per directory. The package name matches the directory.
## Errors
- Errors are values. Return `error` as the last return value of fallible functions.
- Wrap with `%w` to keep the chain: `fmt.Errorf("save user %s: %w", id, err)`.
- Check with `errors.Is` for sentinels and `errors.As` for typed matches. Don't compare error strings.
- Define sentinel errors in the package that produces them: `var ErrNotFound = errors.New("not found")`.
- Don't `panic` for control flow. `panic` is for unrecoverable bugs.
## Interfaces
- Interfaces belong with the **consumer**, not the producer. The package that needs a `Reader` defines it.
- Keep interfaces small. The narrower, the more useful. `io.Reader` is one method.
- Don't write a "marker" interface — that's an OO instinct.
## Concurrency
- Goroutines are cheap; lifetime management is not. Every `go func()` answers: who waits for it, who cancels it?
- Use `context.Context` for cancellation. Pass it as the first parameter of any function that does I/O.
- Use channels for ownership transfer; mutexes for shared state. Don't pick channels just because they're "more Go".
- `sync.WaitGroup` for fan-out/fan-in. `errgroup.Group` when you also want to propagate the first error.
## Structs & methods
- Method receivers: pointer for mutation or large structs; value for small immutable types. Be consistent within a type.
- Constructors are `New<T>` returning a value or pointer. Return errors when construction can fail.
- Tag struct fields for serialization once, at the boundary type — don't tag domain types.
## Standard library first
- The stdlib does more than people remember. Reach for `net/http`, `database/sql`, `encoding/json`, `flag`, `log/slog` before pulling deps.
- Pull deps for boilerplate-heavy areas: routers (`chi`), DB helpers (`sqlx`, `pgx`), assertions (`testify`).
## Testing
- `go test ./...`. Table-driven tests with subtests:
```go
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { ... })
}
```
- Test files live next to the code (`foo.go` + `foo_test.go`).
- Use `t.Helper()` in test helpers so failures point to the call site.
- `testing/synctest` (Go 1.24+) or coordinated mocks for time- or concurrency-dependent tests.
## Style
- `gofmt`, `goimports`, `staticcheck` in CI. No bikeshedding on style.
- Names are short and clear. `i`, `n`, `r` in tight scope are fine; `userRepository` reads better than `userRepo` when you have several `repo`s.
- Comments above exported identifiers start with the identifier name and end with a period.
## Don't
- Don't define interfaces that aren't used yet ("future-proof"). Wait until you have a second implementation.
- Don't use `init()` functions to do work. Make the dependency explicit.
- Don't ignore `error` return values. `_ = ...` is a deliberate choice, not a default.
- Don't return `nil, nil` from a function that returns a pointer and an error. Pick "found", "not found", or "errored".
Other Go templates
Go HTTP Server (net/http + chi)
Production HTTP services with chi router, middleware, and graceful shutdown.
Go + sqlx + Postgres
Database access with sqlx, pgx, and migration discipline.
Go gRPC Services
gRPC + protobuf: service definitions, interceptors, streaming, errors.
Go Testing Rules
Table-driven tests, subtests, testify, integration tests, coverage.