headroom.walls.sh · go

Claude Code for Go

Go's strong conventions — table-driven tests, explicit error returns, interface-based design — are exactly the kind of patterns Claude Code applies consistently at scale. Writing 50 table test cases, adding error wrapping across 30 functions, or redesigning an interface to satisfy a new constraint: all systematic work that Claude Code handles well. This page covers Go-specific workflows.

CLAUDE.md for Go projects

## Build and test
- Build: go build ./...
- Test: go test ./...
- Vet: go vet ./...
- Lint: staticcheck ./...
- Race: go test -race ./...

## Conventions
- Error wrapping: fmt.Errorf("context: %w", err)
- No naked returns in functions longer than 3 lines
- All exported types and functions need doc comments
- Table-driven tests — no single-case test functions

Claude Code reads this at session start. It will use go test ./... as the verification step and apply error wrapping consistently rather than mixing styles.

Table-driven tests

Go's table-driven test pattern is idiomatic and Claude Code applies it correctly:

claude "add table-driven tests for the Parse function in pkg/parser/parser.go — cover happy paths, malformed input, empty input, and boundary cases. Run go test ./pkg/parser/... after."

Claude Code reads the function signature and returns, writes a []struct test table covering the cases you specify, and runs the tests to confirm they pass. It follows the existing test file style — if your project uses t.Run subtests, it will too.

Ask for "boundary cases" explicitly. Claude Code will cover the cases you name, but boundary conditions (empty slice, zero value, max int, nil pointer) need to be requested — it won't add them unless you say so.

Error handling and wrapping

Adding consistent error wrapping across a package:

claude "find all functions in pkg/storage/ that return errors without wrapping context. Add fmt.Errorf wrapping with a description of what operation failed. Run go vet after."
claude "the error messages in this package are inconsistent — some say 'failed to X', some say 'X failed', some say 'error X-ing'. Standardize them to 'failed to X: ...' format across pkg/api/."

Claude Code reads each function, finds the bare return err statements, and wraps them with a description of what the function was doing when the error occurred — not just the generic function name, but the specific operation.

Interface design

Extracting interfaces from concrete types (the Go "accept interfaces, return structs" pattern):

claude "the database package passes a *sql.DB everywhere. Extract a DB interface with just the methods we actually call, and update the callers to use the interface. This will make the code testable with a mock."
claude "design a Cache interface that both the in-memory cache and the Redis cache should implement. Read both implementations first and extract the common method set."

Claude Code reads the concrete type usage, identifies which methods are actually called, and writes the minimal interface — not one method more than necessary.

go vet and staticcheck

claude "run go vet ./... and staticcheck ./... — fix every warning. Don't suppress them with nolint comments unless there's a genuine reason."

Claude Code works through vet and staticcheck output systematically. For each warning it reads the flagged code, understands why the linter flagged it, and fixes the underlying issue — not the symptom.

Common issues it fixes correctly:

Concurrency patterns

claude "the worker pool in pkg/processor/pool.go uses a channel but leaks goroutines on context cancellation. Read the code and fix the cancellation handling."
claude "add a semaphore to limit concurrent goroutines in the batch processor — max 10 at a time. Use a buffered channel, not sync primitives."
claude "run go test -race ./... — there's a data race in the cache. Find it and fix it with proper synchronization."

For concurrency bugs, the race detector output is exactly the kind of structured error message Claude Code can act on — it names the goroutines and the memory address involved.

Adding JSON and struct tags

Go struct tags use backtick syntax. Claude Code handles them correctly:

claude "add json tags to all exported struct fields in pkg/models/ — use snake_case for the field names. Add omitempty to pointer and slice fields."
claude "add validate tags to the UserRequest struct fields using the go-playground/validator conventions: required, email, min, max."

Claude Code reads the struct, infers the correct tag values from field names and types, and writes idiomatic tags — including omitempty for optional fields and - for fields that should be excluded from marshaling.

Module and dependency management

claude "run go mod tidy and explain what changed — which dependencies were added, removed, or version-bumped."
claude "there's a dependency conflict between pkg/a and pkg/b on different versions of the same module. Read go.mod and go.sum, find the conflict, and resolve it with a replace directive or version upgrade."
Session budget note: Go projects tend to have many small files with consistent patterns. Adding table tests to 20 functions or fixing vet warnings across a large package can accumulate 40–60 tool calls before you notice. Check your session usage before starting a codebase-wide sweep.

Monitor session usage during Go development

Headroom — live session usage for Go developers

Systematic Go work — adding tests, fixing vet warnings, wrapping errors across a package — accumulates Claude Code session budget faster than individual edits. Headroom shows your 5h session and 7d weekly utilization live in the macOS menu bar. No token, no API key — reads the file Claude Code writes to ~/.claude/.

Install in one line:

brew install patwalls/tap/headroom

Color-coded from calm to amber to red. Know your headroom before a codebase-wide test sweep.


Writing tests with Claude Code
Refactoring with Claude Code
Claude Code + Zed editor
5-hour session limit explained