- all nine files: trailing "Related" sections removed, their unique links inlined where the topic actually arises (FFI boundary -> c-cpp, embedded frontend -> js-typescript, build scripts -> bash, etc.) - README conventions: rule 2 mandated a Related Skills section, which now contradicts practice — replaced with inline-linking and one-home-per-fact rules. The <repo>/<rel path> rule was appended after the wrong section last commit; moved into the list where it belongs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
153 lines
5.3 KiB
Markdown
153 lines
5.3 KiB
Markdown
---
|
|
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/<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 — the frontend
|
|
itself follows [`js-typescript.md`](skills-public/code-languages/js-typescript.md).
|
|
- 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.
|