Go Service Project Instructions
Claude CodeCLAUDE.mdCoding
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.
Get this skill
Add the skill file with the CLI, by hand, or over MCP.Save this to your project or home directory so Claude Code can load it.
npx goodprompts add go-service-claude-mdSaves to
./CLAUDE.mdnpx goodprompts add go-service-claude-mdSaves to
./CLAUDE.mdSkill file
CLAUDE.md3,438 characters# 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.