Example CLAUDE.md — Go Microservice¶
This example shows a CLAUDE.md for a Go HTTP microservice using the standard library for routing, sqlc for database access, and Docker for local development. It covers Go-specific conventions that keep Claude Code generating idiomatic code.
The CLAUDE.md File¶
```markdown
Project: Acme Notification Service¶
Go 1.22 HTTP microservice. Handles email and webhook notifications. PostgreSQL for storage, Redis for queuing.
Commands¶
go run ./cmd/server— start the server (port 8080)go test ./...— run all testsgo test -run TestSendEmail ./internal/email/— single testgo test -race ./...— run tests with race detectorgolangci-lint run— lint checksqlc generate— regenerate Go code from SQL queriesdocker compose up -d— start Postgres + Redis locallymake migrate-up— apply database migrations
Always run sqlc generate after editing files in sql/queries/.
Architecture¶
cmd/server/— main entrypoint, server setup, graceful shutdowninternal/api/— HTTP handlers and middlewareinternal/email/— email sending logic (SMTP + templates)internal/webhook/— outbound webhook delivery with retriesinternal/queue/— Redis-backed job queueinternal/store/— database queries (generated by sqlc, do not edit manually)sql/queries/— SQL query files for sqlcsql/migrations/— migration files (golang-migrate format)
Code Conventions¶
- Follow standard Go project layout —
cmd/for entrypoints,internal/for private packages - Use
context.Contextas the first parameter in all functions that do I/O - Return errors, do not panic —
return fmt.Errorf("send email: %w", err) - Wrap errors with context using
%wfor unwrapping - No global state — pass dependencies through structs
- Use interfaces for external dependencies (SMTP client, database) to enable testing
Error Handling¶
```go // Good: wrap with context if err := store.CreateUser(ctx, user); err != nil { return fmt.Errorf("create user %s: %w", user.Email, err) }
// Bad: bare return if err := store.CreateUser(ctx, user); err != nil { return err } ```
Database (sqlc)¶
- Write SQL in
sql/queries/*.sqlwith sqlc annotations - Run
sqlc generateto produce Go code ininternal/store/ - Never edit files in
internal/store/manually — they are overwritten by sqlc - Migrations use golang-migrate — one up/down pair per change
- Use transactions for multi-step operations:
store.WithTx(ctx, func(q *Queries) error { ... })
HTTP Handlers¶
- Handlers live in
internal/api/and follow this signature:func (h *Handler) methodName(w http.ResponseWriter, r *http.Request) - Use
encoding/jsonfor request/response marshaling - Validate input at the handler level before calling business logic
- Return structured JSON errors:
{"error": "message", "code": "INVALID_INPUT"} - Middleware chain: logging → recovery → auth → rate-limit → handler
Testing¶
- Table-driven tests for all business logic
- Use
httptestfor handler tests - Mock external services with interfaces — no mock frameworks needed
- Test files alongside source:
email.go→email_test.go - Integration tests tagged
//go:build integration— run separately in CI
Git¶
- Conventional commits: feat:, fix:, chore:, refactor:
- Keep PRs focused — one concern per PR
- Run
golangci-lint runandgo test -race ./...before pushing
Do NOT¶
- Do not use third-party HTTP routers (chi, gin, echo) — use net/http
- Do not edit generated files in
internal/store/ - Do not use
init()functions - Do not use package-level variables for state
- Do not use
interface{}— useanyor define proper types ```
Key Sections Explained¶
Code Conventions — Go has strong opinions and Claude needs explicit guidance to stay idiomatic. The error wrapping pattern and dependency injection rules prevent the most common non-idiomatic Go that Claude might generate.
Database (sqlc) — The critical rule is "never edit generated files." Without this, Claude will modify sqlc output directly and the changes will be overwritten on the next generate.
HTTP Handlers — Defines the exact handler signature and middleware order so Claude generates consistent handlers that integrate cleanly with the existing codebase.
Do NOT — Prevents Claude from adding routers, editing generated code, or using patterns the team has explicitly rejected.
See Also¶
- CLAUDE.md Setup Guide — how to structure your own CLAUDE.md
- Minimal Example — a simpler starting point
- Python Example — for Python backend projects
- Monorepo Example — for multi-package setups