Files
skills-public/code-languages/go.md
T
BluscreamandClaude Opus 5 57968904bf feat(skills): add coding-dev.md and per-language rules under code-languages/
Generic coding rules and preferences distilled from existing agent rule
files, project AGENTS.md/.rules, and saved memories:

- strictness maximal; suppressions must be reported to the user
- never filter build/lint/test output through grep/tail/head
- reuse > fork > self-implement; generic code becomes its own package
- 1000-line file / 100-line function limits, split by concern
- regression test every fixed bug; never test against real user data
- events over polling; scope discipline; honest reporting

Per-language: rust, js-typescript, bash, python, go, csharp-dotnet, c-cpp.
Each extends coding-dev.md rather than repeating it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 12:33:44 +02:00

5.4 KiB

name, description
name description
go Use this skill when writing, building, linting, testing, or releasing Go — including golangci-lint configuration, strict typing over map[string]interface{}, official SDK preference, CGO-free static builds, context handling, and the build gate.

Go — Rules & Preferences

Extends ../coding-dev.md. Everything there applies; this adds Go specifics.


1. The Gate

gofmt -l .            # must print nothing
go vet ./...
golangci-lint run
go test -race ./...
go build ./...

Wire this into a single entry point (make check, scripts/build.sh) so it cannot be partially run. A skipped test fails the gate. Add govulncheck for binary vulnerability scanning on anything that ships.


2. Linting — Strict Set

golangci-lint with a deliberately broad selection, not the defaults:

linters:
  enable:
    - staticcheck
    - errcheck      # every returned error is handled
    - gosec         # security findings
    - noctx         # no HTTP request without a context
    - revive
    - misspell
    - unconvert
    - bodyclose     # every response body is closed
    - goconst
    - ineffassign
    - nilerr

errcheck and bodyclose catch the two most common real Go bugs. As always: fix rather than suppress, and when suppressing use a specific //nolint:name // reason with the reason.


3. Typing — Concrete, Not Dynamic

  • Strongly typed structs everywhere. Avoid map[string]interface{} and reflection when a struct can be defined — including for JSON, config, and API payloads.
  • Typed constants/enums, not bare strings:
    type SyncStatus string
    
    const (
        StatusSynced   SyncStatus = "synced"
        StatusOutdated SyncStatus = "outdated"
        StatusDirty    SyncStatus = "dirty"
    )
    
  • Concrete error types with errors.Is / errors.As, not string comparison.
  • Keep types shared with a frontend structurally in sync with that frontend's types; when both exist, a change to one is incomplete without the other.

4. Errors

  • Every error is handled. errcheck enforces it; do not _ = an error away without a comment explaining why it cannot matter.
  • Wrap with context: fmt.Errorf("fetching %s: %w", name, err). Always %w, never %v, when the caller might need to inspect it.
  • Sentinel errors as package-level var ErrNotFound = errors.New(...); typed errors when the caller needs data from them.
  • panic only for genuinely unrecoverable programmer error, never for expected runtime conditions. A library never panics on input it was designed to receive.
  • defer the cleanup immediately after acquiring the resource, always.

5. Context

  • Every function that does I/O takes ctx context.Context as its first parameter.
  • Never store a context in a struct field.
  • Never pass nil — context.TODO() marks a genuine gap.
  • Honor cancellation: select on ctx.Done() in loops, and pass ctx down to every HTTP call (noctx enforces this).
  • Set timeouts on outbound requests. A default http.Client has no timeout at all.

6. Dependencies — Official SDKs First

Follow the global reuse-first ordering. In Go, strongly prefer official/first-party SDKs over third-party wrappers — they track API changes and handle auth and pagination correctly.

Choose pure-Go implementations over CGO ones whenever the trade is acceptable: a CGO dependency turns a static single-binary build into a toolchain and glibc problem, and rules out a FROM scratch/Alpine container image.

CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o bin/app ./cmd/app

go mod tidy before committing, and commit go.sum.


7. Concurrency

  • Share memory by communicating. Channels for handoff; sync.Mutex for guarding a struct's own fields. Don't mix both for the same state.
  • go test -race is not optional — it belongs in the gate, not in an occasional manual run.
  • Every goroutine has a defined exit path. errgroup.Group for a set of related goroutines so errors propagate and cancellation fans out.
  • Events over polling — subscribe to a watch/notify API rather than looping on a ticker. Where a ticker is genuinely necessary, make the interval configurable and document why.
  • Never leak a goroutine on a channel nobody reads.

8. Structure

  • cmd/<binary>/ for entry points; pkg/ or internal/ for the actual code.
  • internal/ for anything that must not become public API — it is compiler-enforced.
  • One package per concern; package names are lowercase, single-word, no underscores, and never util or common.
  • Keep the exported surface small. Unexported by default.
  • Embed frontend/static assets with //go:embed for single-binary distribution.
  • File and function size limits from the general rules apply.

9. Testing

  • Table-driven tests are the default idiom.
  • t.Parallel() where tests are genuinely independent.
  • t.TempDir() for filesystem work — it cleans itself up.
  • httptest for HTTP handlers and for faking upstreams; do not hit real services in unit tests.
  • Regression test every fixed bug.
  • Never run destructive operations against real accounts or user data during validation — temporary fixtures only.