# Go Coding

> Go coding-standards router — Go 1.26.4+ (1.27 supported, its additions flagged as hints), golangci-lint v2. This skill should be used when a Go task spans several areas, is unspecified, or the question is which tool or standard applies — it names the deterministic tool to run, then the focused skill that owns the topic (go-errors, go-concurrency, go-testing, go-idioms, go-layout, go-lint-setup). Loading the router alone does not apply the standards — load the skill it names next. For one already-identified topic, load that skill directly. Go only; not for business rules.

- Skill: `cadasto/go-coding` (Agent Skill)
- Install (CLI): `npx skillmds@latest add cadasto/go-coding`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cadasto/go-coding/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: Cadasto (https://skillmd.com/u/cadasto)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cadasto/go-coding

---


# go-coding — Go standards router

Route the Go task to the right standard and tool — this skill is a router, not an encyclopedia.
Two principles from the project research drive it:

- **Deterministic beats prose.** Whatever a formatter or linter enforces, run the tool — don't
  reason it out by hand. The plugin's value is judgment the model lacks, not re-deriving tooling.
- **Don't rebuild code intelligence.** For defs/refs/diagnostics/rename/vulncheck, recommend the
  official **`gopls-lsp`** plugin (`@claude-plugins-official`).

## Routing table

| Topic | Run now (deterministic) | Deeper skill |
|---|---|---|
| Formatting | `gofmt -l` / `gofumpt -l` (+ `goimports`) — machine-enforced, non-negotiable | — |
| Static analysis / likely bugs | `go vet ./...`, `golangci-lint run` | `go-lint-setup` |
| Modern idioms (range-int, `min`/`max`, `slices`/`maps`, `wg.Go`, `strings.Cut`, `new(expr)`, `errors.AsType`) | `go fix ./...` (the toolchain's modernizer suite), or `golangci-lint run --enable-only=modernize` for CI reproducibility | `go-idioms` |
| Errors (`%w`, `errors.Is`/`AsType`, `errors.Join`, sentinel/typed, enum dispatch) | `golangci-lint run --enable-only=errorlint,exhaustive` | `go-errors` |
| Concurrency (goroutine leaks, ctx lifecycle, atomics) | `go test -race ./...`, `go vet ./...` | `go-concurrency` |
| Testing (table-driven, `t.Parallel`, `t.Context`, `B.Loop`, `testing/synctest`) | `go test -race ./...`; use `testing/synctest` for time/concurrency tests | `go-testing` |
| Layout, naming & API surface (`internal/`, imports, initialisms, receiver type, in-band errors, struct literals, doc comments) | `golangci-lint run --enable-only=revive` (`var-naming`, `receiver-naming`, `exported`, `blank-imports`, `dot-imports`), `go vet` (`composites`), `gofmt` for doc-comment layout; the rest is judgment | `go-layout` |
| Code intelligence (defs/refs/diagnostics/rename/vulncheck) | install the **`gopls-lsp`** plugin | — |

Open the focused `go-*` skill for the topic — it carries the cited rules and the judgment; run the
tool in the middle column to enforce them. Don't invent rules: each skill cites its sources.

## Route, then load

The router is an index, not the standard. Before writing or reviewing Go you MUST load the focused
skill matching the change — with the Skill tool (`go-coding:go-errors`, …), not by recalling it:

| The diff touches… | Load |
|---|---|
| any `_test.go`, a benchmark, a fuzz target, a "verified by temporarily breaking it" claim | `go-testing` |
| `fmt.Errorf`, `errors.*`, a sentinel, a typed error, a `Must` helper, a `switch` over an enum | `go-errors` |
| a loop, map, slice, string split, `interface{}`, a struct literal that could be `new(expr)`, a nested `:=` on `err` | `go-idioms` |
| `go func`, `chan`, `sync.`, `atomic.`, `errgroup`, `context.With*`, a `Close` on a goroutine-owned resource | `go-concurrency` |
| a new package, an exported identifier, a `cmd/` or `internal/` decision, an import block, a struct literal of a foreign type, a doc comment on an API | `go-layout` |
| `.golangci.y*ml`, a linter complaint you do not understand | `go-lint-setup` |

One load per skill per session is enough; the skill stays in context. Orchestrators dispatching
implementer or reviewer subagents carry this table into every brief — a subagent does not inherit
the parent session's skills.

## Minimum checklist (when a second load is not affordable)

Apply these even if you load nothing else; they are the rules the focused skills most often catch:

- Wrap with `fmt.Errorf("…: %w", err)`; inspect with `errors.Is` / `errors.AsType` and guard the
  result (`ok && v != nil` — a typed-nil pointer satisfies the match). Never swallow an error.
- Every guard has a test that fails when the guard is deleted; a "temporarily broke it by hand"
  check is not evidence — commit it as a can-fail test. Table-driven `t.Run` with got/want messages.
- `range n`, `min`/`max`, `slices`/`maps`, `strings.Cut`, `any`; `go fix ./...` before hand-edits.
- `ctx` first; no goroutine without an owner that waits for it; `t.Context()` in tests.
- Run `gofmt`/`gofumpt` and `golangci-lint run` — never reason out what a tool decides.

## Tie-breaks (when two valid forms compete)

When both forms pass the tools, decide by the order Google's Go Style Guide gives for readable code:
clarity, then simplicity (with its rule of least mechanism: the most standard tool that expresses
the idea), then concision, then maintainability, then consistency. Say which attribute decided it;
"more idiomatic" on its own is not a reason. Source: <https://google.github.io/styleguide/go/guide> (normative and canonical).

## Writing for the human

Anything a person reads — a PR description, a review comment, a question, a design choice put to
them — goes in plain English, not Go shorthand. State the effect before the mechanism ("the request
keeps running after the caller gives up", not "ctx leak in the errgroup"), and expand a term the
first time it appears or leave it out. Keep it short: a few sentences per point, and a decision they
must make gets the options plus a recommendation, not an essay. Identifiers, commands and linter
names stay verbatim — it is the prose around them that must be plain.

## Authoritative sources (cite, don't guess)

- Effective Go — <https://go.dev/doc/effective_go>
- Go Code Review Comments — <https://go.dev/wiki/CodeReviewComments>
- Google Go Style Guide — <https://google.github.io/styleguide/go/> — three documents, cite the one a rule comes from: the *Guide* (<https://google.github.io/styleguide/go/guide>, normative and canonical: the principles), *Style Decisions* (<https://google.github.io/styleguide/go/decisions>, normative: the reviewer rulebook), *Best Practices* (<https://google.github.io/styleguide/go/best-practices>, advisory)
- Uber Go Style Guide — <https://github.com/uber-go/guide>
- Package & toolchain docs — <https://pkg.go.dev>
- Linter rule catalogues (name the rule, not just the tool) — `go vet` <https://pkg.go.dev/cmd/vet>; staticcheck <https://staticcheck.dev/docs/checks/>; revive <https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md>; golangci-lint linters <https://golangci-lint.run/docs/linters/>

## For a focused review

Dispatch the `go-reviewer` agent — a report-only, context-isolated reviewer that applies the
review-heuristics catalog and returns severity-ranked findings on a diff or file.

If a workflow already owns the reviewer seat, that reviewer loads the focused skills itself instead —
one review seat per diff. Orchestrators: put the "Route, then load" table into every implementer and
reviewer brief.

`/go-lint-setup` scaffolds the reference golangci-lint v2 config into a repo.

---
*Top-level structure adapted from [`samber/cc-skills-golang`](https://github.com/samber/cc-skills-golang) (MIT © 2026 Samuel Berthe).*

