--- name: go description: >- 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`](skills-public/coding-dev.md). Everything there applies; this adds Go specifics. --- ## 1. The Gate ```bash 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: ```yaml 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: ```go 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. ```bash 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//` 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 — the frontend itself follows [`js-typescript.md`](skills-public/code-languages/js-typescript.md). - File and function size limits from the general rules apply — `funlen` in `golangci-lint` covers functions; put the file limit in a test (see [`coding-dev.md` §4](skills-public/coding-dev.md)). --- ## 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.