Claude Code reads your project structure and go.mod on first load. A well-written CLAUDE.md tells Claude your exact Go version, test flags, and toolchain so it never guesses.
CLAUDE.md template — Go project
# Project: [Your Service Name]
Go 1.23. Module: github.com/yourorg/yourrepo.
## Commands
- Install: go mod download
- Build: go build ./cmd/...
- Test: go test -race -count=1 ./...
- Lint: golangci-lint run ./...
- Generate: go generate ./... # runs protoc, mockgen, stringer
- Run dev: go run ./cmd/server/...
## Key decisions
- HTTP: Gin v1 (router in internal/api/)
- DB: pgx/v5 directly (no ORM)
- Config: viper + .env in dev, env vars in prod
- Errors: fmt.Errorf with %w for wrapping; sentinel errors in pkg/errors/
- Logging: slog (structured, JSON in prod)
## Do NOT touch
- vendor/ (not used — use go.sum)
- internal/generated/ (protoc output)
- .env files
Tip: Add go test -race ./... as your test command, not just go test ./.... The race detector catches data races Claude might introduce when refactoring concurrent code — Claude will fix them before reporting done.
Go Modules & Dependencies
Claude understands Go module semantics: import paths, module versions, replace directives, and workspace mode.
Useful module prompts
Add dependency
"Add github.com/redis/go-redis/v9 to the project. Use it in internal/cache/ to implement a simple TTL cache for user sessions."
Upgrade module
"Upgrade all dependencies to their latest minor versions. Run go mod tidy and go test -race ./... and fix any breaking changes."
Workspace setup
"Set up go.work for this monorepo with modules in services/api, services/worker, and pkg/shared. Make services/api depend on pkg/shared locally."
Vendoring
"Convert this module to use vendor/ mode. Run go mod vendor, confirm all tests pass, and add a make vendor target to the Makefile."
How Claude handles replace directives: Claude reads existing replace and exclude directives before suggesting dependency changes. It won't overwrite a local replace directive with the upstream version. If you want Claude to clean up stale replaces, say so explicitly.
HTTP Servers
Claude supports all major Go HTTP approaches. Select your framework:
Gin patterns
New endpoint
"Add POST /api/v1/users endpoint to the Gin router. Validate request JSON with binding tags, insert into users table via pgx, return 201 with the created user or 400/500 on error."
Auth middleware
"Add a JWT auth middleware to the Gin router that reads the Bearer token, validates it with the secret from config, and sets the user ID in gin.Context."
Error handler
"Add a centralized error-handling middleware to the Gin router that maps domain errors to HTTP status codes and returns consistent JSON error responses."
Integration test
"Write integration tests for the /api/v1/users endpoints using httptest.NewRecorder. Test success, validation error, and auth failure paths."
"Create an Echo route group at /api/v2 with rate-limiting middleware (100 req/min per IP) and JWT auth. Add GET /products and POST /products handlers."
Custom validator
"Register a custom validator with Echo using go-playground/validator. Add validate tags to the OrderRequest struct and return structured validation errors."
WebSocket handler
"Add a WebSocket endpoint at /ws/notifications using Echo's built-in WebSocket support. Broadcast messages to all connected clients when a new event arrives."
net/http stdlib patterns
Handler chain
"Build a middleware chain using http.Handler and adapter functions. Add logging, recovery, and request-ID middleware. Wire up the routes using ServeMux."
Graceful shutdown
"Add graceful shutdown to the http.Server: listen for SIGTERM/SIGINT, call Shutdown with a 30-second context, and drain active connections before exiting."
chi router patterns
REST resource
"Create a chi sub-router for /api/v1/orders. Mount GET, POST, PATCH /orders/{id}, DELETE /orders/{id}. Add chi.URLParam to extract the ID in each handler."
Middleware stack
"Add chi's built-in middleware stack: Logger, Recoverer, RealIP, RequestID, Compress. Add a custom timeout middleware with 10-second context deadline."
gRPC & Protobuf
Claude reads your .proto files, understands service definitions, and generates or fixes the Go implementation layer.
Implement service
"Read the UserService proto in api/proto/user.proto and implement the Go server in internal/grpc/user_server.go. Wire it to the existing UserRepository."
Add RPC method
"Add a ListUsers streaming RPC to the UserService proto. Update the generated stub, implement the server-side stream, and write a client test."
Interceptor
"Add a gRPC unary interceptor that extracts the JWT from metadata, validates it, and sets the user ID in the context. Apply it to all authenticated RPCs."
gRPC-Gateway
"Set up grpc-gateway to expose the UserService as a REST API alongside the gRPC endpoint. Generate the gateway code and update the Makefile generate target."
Error handling
"Audit all gRPC handlers — replace generic Internal errors with correct status codes (NotFound, InvalidArgument, AlreadyExists) using google.golang.org/grpc/status."
Client retry
"Add a gRPC client with retry policy: 3 attempts for Unavailable and DeadlineExceeded, exponential backoff, and a connection pool for the UserService client."
Claude's go generate pattern
//go:generate protoc --go_out=. --go-grpc_out=. \
// --grpc-gateway_out=. \
// --proto_path=api/proto api/proto/user.proto
// Claude reads this directive and knows to run 'go generate ./...'
// after editing .proto files — it won't hand-edit generated files.
Tip: Tell Claude your protoc version and plugin versions in CLAUDE.md. Generated gRPC code varies between plugin versions and Claude will match the style in your existing generated files.
Concurrency Patterns
Claude writes correct Go concurrency: channels, goroutines, sync primitives, and errgroup. Always run tests with -race.
Fan-out
"Refactor this sequential for loop to fan out with goroutines, limit concurrency to 20 using a semaphore channel, and collect results preserving order."
errgroup
"Rewrite these three sequential API calls to run concurrently with golang.org/x/sync/errgroup. Cancel all on first error. Merge the results."
Worker pool
"Create a worker pool that reads jobs from a channel, processes them with 10 concurrent workers, writes results to an output channel, and shuts down cleanly on context cancel."
Context propagation
"Audit all goroutines in internal/worker/ — every goroutine must accept a context.Context as first arg and respect ctx.Done(). Fix any that ignore context."
Race fix
"Run the tests with -race and fix all reported data races. Prefer sync.RWMutex for read-heavy maps and sync.Once for initialization."
Pipeline
"Build a pipeline: stage 1 reads records from Postgres, stage 2 transforms them, stage 3 writes to S3. Connect stages with channels. Respect context cancellation."
Errgroup pattern Claude generates
g, ctx := errgroup.WithContext(ctx)
var mu sync.Mutex
results := make([]Result, 0, len(ids))
for _, id := range ids {
id := id // loop variable capture
g.Go(func() error {
r, err := fetchResult(ctx, id)
if err != nil {
return fmt.Errorf("fetch %s: %w", id, err)
}
mu.Lock()
results = append(results, r)
mu.Unlock()
return nil
})
}
if err := g.Wait(); err != nil {
return nil, err
}
Database & ORM
Query
"Write a GetUserByEmail function using pgx/v5. Use named parameters, scan into the User struct, return sql.ErrNoRows as a sentinel if not found."
Batch insert
"Write a BulkCreateUsers function using pgx SendBatch to insert up to 1000 users per batch. Return a slice of inserted IDs."
Transaction
"Wrap the order creation and inventory decrement in a single pgx transaction. Roll back on any error. Retry on serialization failure (SQLSTATE 40001)."
New query
"Add a ListUsersByRole query to queries/users.sql using the -- name: sqlc annotation. Run 'sqlc generate' and wire the generated method into UserRepository."
Schema update
"Add a nullable bio column to the users table migration. Update sqlc.yaml if needed. Re-generate and update all callers of the User struct."
Model
"Create a GORM model for Order with associations: belongs-to User, has-many OrderItems. Add indexes for user_id and status. Write AutoMigrate call."
Preload
"Rewrite the GetOrdersWithItems function to use GORM Preload instead of multiple queries. Preserve existing filtering and pagination behavior."
New migration
"Create a golang-migrate migration to add a refresh_tokens table with user_id FK, token hash (unique), expires_at, and revoked bool. Add both up and down files."
Index migration
"Write a CONCURRENTLY migration to add a composite index on (user_id, created_at DESC) to the orders table without locking the table."
Testing in Go
Claude writes idiomatic Go tests: table-driven subtests, testify assertions, httptest for HTTP handlers, and gomock / testify/mock for interfaces.
Table-driven tests
"Write table-driven tests for the ValidateEmail function. Use t.Run subtests with descriptive names. Cover empty, valid, invalid format, international, and subaddress cases."
HTTP handler test
"Write tests for the CreateUser handler using httptest.NewRecorder. Test 201 success, 400 validation error, 409 duplicate email, and 500 DB error paths."
Generate mocks
"Generate mockgen mocks for all interfaces in internal/ports/. Add //go:generate mockgen directives to the interface files and run go generate."
Integration test
"Write an integration test for UserRepository using a real Postgres testcontainer. Use TestMain to start/stop the container and run migrations once for the suite."
Benchmark
"Write a benchmark for the ParseConfig function using testing.B. Add BenchmarkParseConfig_Small and BenchmarkParseConfig_Large variants. Run with -bench=. -benchmem."
Fuzz test
"Add a Go fuzz test for the ParseURL function targeting the url parameter. Seed it with valid and edge-case URLs from the existing unit tests."
Claude builds complete Cobra CLI tools with subcommands, persistent flags, and proper exit codes.
Scaffold CLI
"Create a Cobra CLI app in cmd/migrate with subcommands: up, down, status. Each reads DB_URL from env, connects with pgx, and runs golang-migrate operations."
Add subcommand
"Add an 'export' subcommand to the CLI that accepts --format (json|csv) and --output flags. Stream results from the DB to the output file with a progress bar."
Config binding
"Bind all CLI flags to viper keys so they can also be set via config file (config.yaml) or environment variables (APP_DB_URL, APP_LOG_LEVEL). Add --config flag."
Shell completion
"Add shell completion for bash, zsh, and fish to the root command using cobra's built-in completion. Add a 'completion' subcommand that prints the script."
Docker Multi-Stage Builds
Claude generates minimal, secure Go Docker images using multi-stage builds.
Optimal Go Dockerfile Claude generates
# Build stage
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o server ./cmd/server
# Final stage — distroless or scratch
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]
Multi-stage build
"Write a multi-stage Dockerfile for this Go service. Use golang:1.23-alpine for building and distroless/static for the final image. Keep the image under 20MB."
docker-compose
"Create a docker-compose.yaml with the Go service, Postgres 16, Redis 7, and a pgAdmin instance for local development. Add health checks and volume mounts."
Build args
"Add VERSION and GIT_COMMIT build args to the Dockerfile. Inject them as -ldflags into the binary so 'server --version' prints the correct values."
Linting & Code Quality
Claude understands every golangci-lint rule and fixes lint failures before marking a task done.
Fix lint
"Run golangci-lint and fix every warning in internal/api/. Don't change behavior, just fix the lint issues. Explain the most important ones briefly."
Configure linter
"Create a .golangci.yml that enables staticcheck, errcheck, govet, revive, godot, exhaustive, and wrapcheck. Set a 5-minute timeout. Exclude test files from wrapcheck."
Error wrapping audit
"Audit all error returns in internal/ — every error returned to a caller must be wrapped with fmt.Errorf('context: %w', err). Fix any bare returns."
Interface compliance
"Add compile-time interface checks (var _ Repository = (*PostgresRepository)(nil)) to every implementation file. Fix any missing methods the compiler finds."
Common golangci-lint fixes Claude applies
Linter
What Claude fixes
errcheck
Adds _ = or proper error handling for ignored errors
Wraps external package errors with fmt.Errorf(...%w...)
exhaustive
Adds missing switch cases for enum types
godot
Adds periods to comment sentences
revive
Renames exported vars that shadow builtins, fixes comment format
gocritic
Simplifies if-return chains, fixes append misuse
35+ Copy-Paste Prompts
Ready-to-use prompts covering refactoring, performance, security, and code generation.
Refactoring
Extract interface
"Extract an interface for the EmailService struct. Place it in internal/ports/email.go. Update all callers to use the interface. Add a compile-time check."
Constructor injection
"Refactor UserHandler to use constructor injection instead of global variables. Add a NewUserHandler(svc UserService, logger *slog.Logger) function."
Split large file
"Split internal/api/handlers.go (800 lines) into separate files per domain: user_handlers.go, order_handlers.go, product_handlers.go. Keep the same package."
Options pattern
"Refactor NewServer to use the functional options pattern instead of a config struct. Add WithTimeout, WithMaxConnections, and WithLogger option functions."
Performance
Benchmark & profile
"Write a benchmark for ParseRequest. Then profile it with pprof and identify the top allocation. Show me the fix with before/after alloc numbers."
Reduce allocations
"Audit the hot path in ProcessEvents for heap allocations. Pre-size slices, use sync.Pool for reused buffers, and avoid string→byte conversions where possible."
N+1 fix
"Find and fix N+1 queries in the ListOrders function. Batch-load all related Users and Products in two queries instead of one per order."
Security
SQL injection audit
"Audit all database queries in internal/repository/ for SQL injection. Replace any string concatenation with parameterized queries. Show me each fix."
Secrets audit
"Scan the codebase for hardcoded secrets, API keys, and credentials. Replace any found with reads from environment variables or a config struct."
TLS config
"Harden the TLS config on the HTTP server: minimum TLS 1.2, prefer TLS 1.3, disable weak cipher suites, set HSTS header in middleware."
Code Generation
OpenAPI → handlers
"Read the OpenAPI spec in api/openapi.yaml and generate Gin handler stubs for every endpoint. Add request validation based on the schema."
Stringer
"Add //go:generate stringer -type=Status directives to the Status enum. Run go generate and ensure the String() method covers all values."
Makefile
"Write a Makefile with targets: build, test, lint, generate, docker-build, docker-push, migrate-up, migrate-down. Add .PHONY declarations and help target."
Frequently Asked Questions
Does Claude Code understand Go modules and go.mod?
Yes. Claude Code reads go.mod and go.sum to understand your module path, Go version, and dependency tree. It uses the correct import paths for your module and knows when to run go mod tidy. Add your module path and Go version to CLAUDE.md so Claude maintains consistency. For workspace-mode monorepos (go.work), Claude understands multi-module setups and can navigate inter-module dependencies correctly.
How does Claude Code handle Go's error handling pattern?
Claude writes idiomatic Go error handling by default: if err != nil { return fmt.Errorf("context: %w", err) }. It understands errors.Is(), errors.As(), and custom error types. Prompt: "Refactor this function to wrap errors with context using fmt.Errorf and %w" and Claude propagates error context consistently. It avoids panic() for recoverable errors and knows when sentinel errors (io.EOF, sql.ErrNoRows) are appropriate to return unwrapped.
Can Claude Code write table-driven tests in Go?
Table-driven tests are one of Claude Code's strongest Go capabilities. Say: "Write table-driven tests for the Validate function using subtests and t.Run" and Claude generates a complete test matrix with edge cases, named subtests, and proper test helper functions. It also writes testify assertions (require.NoError, assert.Equal) when testify is in go.mod, and knows to use t.Parallel() for independent subtests.
How does Claude Code work with Gin, Echo, and Fiber?
Claude Code supports all major Go HTTP frameworks. It reads your router setup and generates handlers that match your middleware chain and request/response types. Prompt: "Add a GET /users/:id endpoint that validates the ID, queries the database, and returns JSON" — Claude writes the handler, binds the route, adds error response helpers, and suggests integration tests with httptest.NewRecorder. For Gin specifically, it understands gin.Context, ShouldBindJSON, and the abort-with-status pattern.
Can Claude Code help with goroutines and concurrency?
Yes — Claude Code understands Go concurrency primitives: goroutines, channels, sync.Mutex, sync.WaitGroup, sync.Once, errgroup, and context cancellation. For complex patterns say: "Refactor this sequential loop to fan out with goroutines, limit concurrency to 10 using a semaphore, and collect errors with errgroup." Claude correctly handles context propagation through goroutine boundaries. Pair with go test -race ./... in your CLAUDE.md so races are caught automatically.
How do I set up golangci-lint with Claude Code?
Add golangci-lint run ./... to your CLAUDE.md test commands. Claude will fix lint failures it introduces before finishing a task. Check in a .golangci.yml that enables the linters your team cares about (staticcheck, errcheck, govet, revive, godot). Claude understands every golangci-lint rule and can explain why a linter flagged a line. Prompt: "Fix all golangci-lint warnings in internal/api/ without changing behavior" to clean up a legacy package.
What should I put in CLAUDE.md for a Go project?
Essential items for a Go CLAUDE.md: (1) Go version and module path, (2) test command: go test -race ./..., (3) lint command: golangci-lint run ./..., (4) build command: go build ./cmd/..., (5) generate command if using go generate (protoc, mockgen, stringer), (6) key packages Claude should prefer (e.g. "use pgx not database/sql directly"), (7) config approach (env vars, Viper, etc.), (8) directories Claude must not touch (vendor/, generated/). The template at the top of this guide covers all of these.