← GophersCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
Update to Gophers
Snapshot Sep 30, 2026 · 23:14 UTC · version 0.1.0
Collection source: not recorded for this historical snapshot.
First saved snapshot
No earlier snapshot is available to establish a change.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Use when setting up linting for a Go project, configuring golangci-lint, picking a linter set, suppressing findings with //nolint, or wiring lint checks into CI. Apply proactively whenever a project lacks .golangci.yml, when lint output is unclear, or when a new package needs the project's quality bar. Does not cover code review process (see go-code-review).",
"included_files": [
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 213
},
{
"relative_path": "assets/.golangci.yml",
"size_in_bytes": 2081
},
{
"relative_path": "references/ci-integration.md",
"size_in_bytes": 4049
},
{
"relative_path": "references/linter-catalog.md",
"size_in_bytes": 4380
},
{
"relative_path": "references/nolint-directives.md",
"size_in_bytes": 3961
}
],
"name": "go-linting",
"skill_md_contents": "---\nname: go-linting\ndescription: \"Use when setting up linting for a Go project, configuring golangci-lint, picking a linter set, suppressing findings with //nolint, or wiring lint checks into CI. Apply proactively whenever a project lacks .golangci.yml, when lint output is unclear, or when a new package needs the project's quality bar. Does not cover code review process (see go-code-review).\"\nlicense: MIT\ncompatibility: \"Designed for Claude Code or similar AI coding agents. Targets golangci-lint v2.x and Go 1.21+. Some commands (golangci-lint fmt) require v2.\"\nallowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)\n---\n\n# Go Linting\n\nThe single most important property of a linting setup is **consistency**: every contributor and every CI run uses the same rules. `golangci-lint` is the tool; a checked-in `.golangci.yml` is the contract.\n\n## Core Rules\n\n1. **Every Go project has a `.golangci.yml`** at the repository root. It is the source of truth for which linters run.\n2. **Lint runs in CI on every PR.** A green build means lint is green.\n3. **Lint runs locally before commit.** A pre-commit hook or `make lint` keeps the feedback loop fast.\n4. **Suppress with reasons.** `//nolint:linter // why` — never bare `//nolint`.\n5. **Fix the cause first.** A suppression should be the last resort, not the default reaction.\n6. **Never silence security linters** (`gosec`, `bodyclose`, `sqlclosecheck`) without a strong, documented reason.\n\n## Setup Procedure\n\n1. Install: `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest` (or `brew install golangci-lint`).\n2. Drop a baseline [`.golangci.yml`](assets/.golangci.yml) at the repo root.\n3. Run `golangci-lint run ./...`.\n4. Fix the findings in order — formatting first, `govet` next, style last.\n5. Re-run until clean. Commit `.golangci.yml` and any source fixes together.\n6. Add the CI workflow (see [references/ci-integration.md](references/ci-integration.md)).\n\n## Minimum Linter Set\n\nThese five catch the most common issues and have the lowest noise rate. Start here:\n\n| Linter | Catches |\n|---|---|\n| `errcheck` | Unchecked error returns |\n| `govet` | Mistakes that compile but are wrong (printf args, shifts, etc.) |\n| `staticcheck` | Bug-prone patterns, dead code, simplifications |\n| `ineffassign` | Assignments whose value is never read |\n| `revive` | Style issues (modern replacement for `golint`) |\n\nAdd formatting on top: `gofmt` / `goimports` (or `gofumpt` for stricter rules). With golangci-lint v2 these run via `golangci-lint fmt`.\n\n## Recommended Additional Linters\n\nEnable these once the baseline is clean:\n\n| Linter | When to enable |\n|---|---|\n| `gosec` | Any service that handles untrusted input |\n| `bodyclose` | Any code that calls `http.Client.Do` |\n| `sqlclosecheck` | Any code using `database/sql` |\n| `nilerr` | Any code that does `if err != nil { return nil }` style returns |\n| `misspell` | Always — comments and strings |\n| `unconvert` | Always — flags useless type conversions |\n| `nolintlint` | Always — enforces the `//nolint` rules below |\n| `paralleltest` | If most tests can use `t.Parallel()` |\n| `thelper` | If you write test helpers (enforces `t.Helper()`) |\n| `testifylint` | If the project uses `testify` |\n| `gocyclo` / `gocognit` | When you want a complexity ceiling |\n| `exhaustive` | When you use `iota`-based enums and want full `switch` coverage |\n\n> Read [references/linter-catalog.md](references/linter-catalog.md) when picking from the long tail of correctness, style, security, and complexity linters, or when deciding which ones to enable on legacy code.\n\n## Development Workflow\n\n```makefile\nlint:\n\tgolangci-lint run ./...\n\nlint-fix:\n\tgolangci-lint run --fix ./...\n\nfmt:\n\tgolangci-lint fmt ./...\n\nci-lint:\n\tgolangci-lint run --new-from-rev=origin/main ./...\n```\n\n| Task | Command |\n|---|---|\n| Run all enabled linters | `golangci-lint run ./...` |\n| Auto-fix everything fixable | `golangci-lint run --fix ./...` |\n| Format the tree (v2+) | `golangci-lint fmt ./...` |\n| Lint only changed code | `golangci-lint run --new-from-rev=origin/main ./...` |\n| Run one linter | `golangci-lint run --enable-only=govet ./...` |\n| Show which linters exist | `golangci-lint linters` |\n\n`--new-from-rev` is what makes incremental adoption work: legacy code stays untouched, new and changed code must meet the bar.\n\n> Read [references/ci-integration.md](references/ci-integration.md) when wiring GitHub Actions, pre-commit hooks, or selective linting on PRs.\n\n## Suppressing Findings\n\n```go\n// Good: specific linter + a reason\n//nolint:errcheck // fire-and-forget; Sync error is not actionable on shutdown\n_ = logger.Sync()\n```\n\n```go\n// Bad: blanket, no reason — nolintlint will flag this\n//nolint\n_ = logger.Sync()\n```\n\nRules (enforced by `nolintlint`):\n\n- Name the linter: `//nolint:errcheck`, not `//nolint`.\n- Include a justification after `//`.\n- Place the directive on the same line as the finding, or immediately above the construct it applies to.\n- Prefer per-line suppressions over file-level `//nolint:all`.\n\n> Read [references/nolint-directives.md](references/nolint-directives.md) when deciding between inline, block, and file-scope suppressions, or when reviewing existing `//nolint` for stale rationale.\n\n## Interpreting Output\n\nEach finding looks like:\n\n```\npath/to/file.go:42:10: message describing the issue (linter-name)\n```\n\nThe linter name in parentheses is the key — look it up in the catalog to see what it actually checks, then either fix the code or suppress with a reason that names the same linter.\n\n## Anti-Patterns\n\n| Anti-pattern | Why it hurts | Do this instead |\n|---|---|---|\n| No `.golangci.yml` in the repo | Each contributor lints differently or not at all | Commit a baseline config; CI enforces it |\n| `//nolint` with no linter name | Disables every check on that line, silently | `//nolint:errcheck // reason` |\n| `//nolint:all` at the top of a file | Whole file escapes review | Suppress per construct with a reason |\n| Lint failures non-blocking in CI | \"Green build\" becomes meaningless | Block merges on lint failure |\n| Enabling 100 linters on day one | Noise drowns signal; team gives up | Start with the minimum set, add gradually |\n| Suppressing `gosec` / `bodyclose` without justification | Silently hides real bugs | Fix the cause; if you can't, document why in the suppression |\n| Different lint versions in dev vs CI | \"Works on my machine\" comes back | Pin the version in CI and document it in the README |\n| Linting after the fact, only in CI | Slow iteration; PRs ping-pong | Run locally via `make lint` or a pre-commit hook |\n\n## Verification Checklist\n\nBefore merging a linting change:\n\n- [ ] `.golangci.yml` exists at the repo root and is checked in\n- [ ] `golangci-lint run ./...` is clean (or `--new-from-rev` reports no new issues)\n- [ ] CI runs `golangci-lint` and blocks merges on failure\n- [ ] `nolintlint` is enabled; no bare `//nolint` remains\n- [ ] Every `//nolint` includes a linter name and a one-line reason\n- [ ] Security linters (`gosec`, `bodyclose`, `sqlclosecheck`) are enabled where applicable\n\n## References\n\n- [assets/.golangci.yml](assets/.golangci.yml) — production-ready baseline configuration\n- [references/linter-catalog.md](references/linter-catalog.md) — what each linter checks and when to enable it\n- [references/nolint-directives.md](references/nolint-directives.md) — suppression patterns, scoping rules, anti-patterns\n- [references/ci-integration.md](references/ci-integration.md) — GitHub Actions, pre-commit hooks, incremental adoption\n"
}SHA-256 of public snapshot: fd7a68dcad864557cff3781ff5e533acc6ebb4276e6998c155f6a0a2e24aee32