Go Service Project Instructions
CLAUDE.md for a Go backend service: package layout, error wrapping with %w, table-driven tests, and context propagation rules an agent must follow on every handler.
Use when: Use in a Go HTTP/gRPC service repository so agents write idiomatic Go — wrapped errors, table tests, contexts passed all the way down — instead of patterns imported from other languages.
The skill file
Claude Code · CLAUDE.mdCLAUDE.md3438 chars
# Project Instructions — Go Service
## Commands
```bash
go build ./... # must compile cleanly
go test ./... # full suite
go test ./internal/orders/ -run TestCreateOrder -v # focused run while iterating
go vet ./... # before every commit
golangci-lint run # lint config in .golangci.yml — do not edit it to silence findings
gofmt -l . # must print nothing; gofmt is non-negotiable
```
Go version is pinned in `go.mod`. Use `go get <module>@<version>` then `go mod tidy` to add dependencies; never edit `go.mod`/`go.sum` by hand.
## Package layout
```
cmd/<service-name>/ # main.go only: flag parsing, wiring, graceful shutdown
internal/<domain>/ # one package per domain (orders, billing, users) — business logic
internal/platform/ # cross-cutting: db, httpserver, config, logging
api/ # transport layer: handlers/proto, request/response types
```
- `cmd/` contains no business logic — if `main.go` exceeds ~150 lines, you are wiring too much there.
- Domain packages do not import each other's internals; they communicate through interfaces defined by the **consumer**, not the provider.
- Keep interfaces small (1–3 methods) and define them where they are used. Do not create an `interfaces` package.
## Error handling
- Wrap with context at every layer boundary: `fmt.Errorf("creating order %s: %w", id, err)`. Lowercase, no trailing punctuation, no "failed to" prefix stacking.
- Sentinel errors are package-level vars: `var ErrOrderNotFound = errors.New("order not found")`. Check with `errors.Is`, extract typed errors with `errors.As` — never string-match error text.
- Handle an error exactly once: either log it or return it, not both. Logging happens at the top of the call stack (handler/worker), with structured fields.
- No `panic` outside of `main` startup invariants. No swallowing errors with `_` unless the line has a comment explaining why it is safe.
## Context discipline
- `ctx context.Context` is the first parameter of every function that does I/O, blocks, or calls something that does. No storing contexts in structs.
- Propagate the incoming request context all the way to the database/HTTP client — never substitute `context.Background()` mid-stack.
- `context.Background()` is allowed only in `main`, tests, and detached background workers (with a comment).
- Apply timeouts at the boundary: `ctx, cancel := context.WithTimeout(ctx, 5*time.Second)` in handlers calling external services; always `defer cancel()`.
## Tests
- Table-driven tests are the default shape: a slice of named cases, `for _, tc := range cases { t.Run(tc.name, ...) }`. Mark independent subtests with `t.Parallel()`.
- Test the exported behavior of a package (`package orders_test`), not private helpers, unless the helper has real complexity.
- Use the standard library plus `go-cmp` for diffs; fakes over mocks — hand-write a small in-memory implementation of the consumer interface instead of generating mocks.
- Integration tests that need Postgres live behind `//go:build integration` and run with `go test -tags integration ./...`.
## Conventions
- Accept interfaces, return concrete types.
- Zero values should be useful; constructors (`NewX`) only when invariants must be enforced.
- No global mutable state; pass dependencies through struct fields set in `cmd/` wiring.
Install
drop into your repoSave this to your project or home directory so Claude Code can load it.
path./CLAUDE.md
Discussion
What people are saying
Forks
0 forks of this skillLoading forks…