Go is friendly to AI coding: the toolchain is fast, formatting is a solved problem, and the test idioms are consistent. Three small pieces make Claude Code reliable in a Go repo: a short CLAUDE.md with the real commands, a hook that formats every edited file, and a standing rule for table-driven tests.
Put this in ./CLAUDE.md (or ./.claude/CLAUDE.md) and commit it. Running /init will draft a starting file from your codebase; then trim it to what Claude couldn't discover alone. The Claude Code docs suggest keeping each CLAUDE.md under about 200 lines. For general structure, see the CLAUDE.md template.
# orders-service
HTTP/JSON API for order management. Go 1.23, Postgres, deployed as one binary.
## Commands
- Build: `go build ./...`
- All tests: `go test -race ./...`
- One test: `go test ./internal/orders -run TestParseOrder/empty_id -v`
- Vet / lint: `go vet ./...` and `golangci-lint run`
Before saying a task is done: build, vet and `go test -race ./...` must pass.
## Layout
- `cmd/orders/`: main package, wiring only
- `internal/`: all business logic, one package per domain
- `internal/db/gen/`: generated by sqlc, never edit by hand
## Conventions
- Wrap errors with context: `fmt.Errorf("load order %s: %w", id, err)`.
- Pass `context.Context` as the first parameter; never store it in a struct.
- Accept interfaces, return structs. Define interfaces where they are used.
- Tests are table-driven with `t.Run` subtests (see Testing).
## Don't
- Don't add dependencies without asking.
- Don't use `panic` for expected errors.
- Don't ignore returned errors, even in tests.
Adjust the Go version, linter and generated paths to match your repo. Only list commands you have actually run.
CLAUDE.md is guidance. A hook is deterministic: Claude Code runs it every time. Per the hooks guide, a PostToolUse hook with an Edit|Write matcher fires after file edits, and the command receives JSON on stdin that includes tool_input.file_path. Add this to .claude/settings.json in the project root:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "f=$(jq -r '.tool_input.file_path'); case \"$f\" in *.go) gofmt -w \"$f\" ;; esac"
}
]
}
]
}
}
jq installed, which is the same approach the official docs use for Prettier.case guard skips non-Go files such as go.mod or Markdown, which gofmt would reject.gofmt -w for goimports -w if you use it, provided it is installed.Table-driven tests are the standard Go idiom, so Claude usually produces them, but you get better results by stating the shape you want. Add this to CLAUDE.md:
## Testing
- Table-driven: a slice of structs with `name`, inputs, and `want`/`wantErr`,
run with `t.Run(tc.name, ...)`.
- Cover the main case, empty/zero values, boundaries and each error path.
- Use `t.Helper()` in helpers and `t.Parallel()` only when cases share no state.
- Never modify or skip existing tests to make them pass; ask instead.
And a prompt that produces the layout you want:
Write a table-driven test for ParseOrder in internal/orders.
Follow the style of the nearest existing _test.go file. Each case needs a
descriptive name. Include error cases with wantErr. Run
`go test ./internal/orders -run TestParseOrder -v` and show the output.
Reference shape, so you can check Claude's output against it:
func TestParseOrder(t *testing.T) {
tests := []struct {
name string
input string
want Order
wantErr bool
}{
{name: "valid", input: `{"id":"a1"}`, want: Order{ID: "a1"}},
{name: "empty id", input: `{"id":""}`, wantErr: true},
{name: "bad json", input: `{`, wantErr: true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got, err := ParseOrder([]byte(tc.input))
if (err != nil) != tc.wantErr {
t.Fatalf("err = %v, wantErr %v", err, tc.wantErr)
}
if got != tc.want {
t.Errorf("got %+v, want %+v", got, tc.want)
}
})
}
}
| Piece | Where | Job |
|---|---|---|
| CLAUDE.md | ./CLAUDE.md | Commands, layout, conventions |
| gofmt hook | .claude/settings.json | Format every edited .go file |
| Testing rules | CLAUDE.md | Table-driven shape, no weakened tests |
Next steps: follow the loop in test-driven development with Claude Code to write the failing table first, then implement.
Keelwork bundles 10 workflow skills, 5 tested safety hooks (including a full guard-bash and a secret scanner), 3 subagents and 5 CLAUDE.md templates, with a one-command installer that safely merges into your settings.
Get Keelwork — $24 →