Skip to content

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 tests
  • go test -run TestSendEmail ./internal/email/ — single test
  • go test -race ./... — run tests with race detector
  • golangci-lint run — lint check
  • sqlc generate — regenerate Go code from SQL queries
  • docker compose up -d — start Postgres + Redis locally
  • make migrate-up — apply database migrations

Always run sqlc generate after editing files in sql/queries/.

Architecture

  • cmd/server/ — main entrypoint, server setup, graceful shutdown
  • internal/api/ — HTTP handlers and middleware
  • internal/email/ — email sending logic (SMTP + templates)
  • internal/webhook/ — outbound webhook delivery with retries
  • internal/queue/ — Redis-backed job queue
  • internal/store/ — database queries (generated by sqlc, do not edit manually)
  • sql/queries/ — SQL query files for sqlc
  • sql/migrations/ — migration files (golang-migrate format)

Code Conventions

  • Follow standard Go project layout — cmd/ for entrypoints, internal/ for private packages
  • Use context.Context as 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 %w for 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/*.sql with sqlc annotations
  • Run sqlc generate to produce Go code in internal/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/json for 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 httptest for handler tests
  • Mock external services with interfaces — no mock frameworks needed
  • Test files alongside source: email.goemail_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 run and go 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{} — use any or 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