go-clean-architecture
Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase, Repository, Delivery layers, inward…
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
$ npx -y skills add muratmirgun/gophers --skill go-linting --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/go-lintingContext preview
The summary Claude sees to decide when to auto-load this skill.
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
name: go-linting 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)." license: MIT compatibility: "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." allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
The 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.
1. **Every Go project has a `.golangci.yml`** at the repository root. It is the source of truth for which linters run. 2. **Lint runs in CI on every PR.** A green build means lint is green. 3. **Lint runs locally before commit.** A pre-commit hook or `make lint` keeps the feedback loop fast. 4. **Suppress with reasons.** `//nolint:linter // why` — never bare `//nolint`. 5. **Fix the cause first.** A suppression should be the last resort, not the default reaction. 6. **Never silence security linters** (`gosec`, `bodyclose`, `sqlclosecheck`) without a strong, documented reason.
1. Install: `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest` (or `brew install golangci-lint`). 2. Drop a baseline [`.golangci.yml`](assets/.golangci.yml) at the repo root. 3. Run `golangci-lint run ./...`. 4. Fix the findings in order — formatting first, `govet` next, style last. 5. Re-run until clean. Commit `.golangci.yml` and any source fixes together. 6. Add the CI workflow (see [references/ci-integration.md](references/ci-integration.md)).
These five catch the most common issues and have the lowest noise rate. Start here:
| Linter | Catches | |---|---| | `errcheck` | Unchecked error returns | | `govet` | Mistakes that compile but are wrong (printf args, shifts, etc.) | | `staticcheck` | Bug-prone patterns, dead code, simplifications | | `ineffassign` | Assignments whose value is never read | | `revive` | Style issues (modern replacement for `golint`) |
Add formatting on top: `gofmt` / `goimports` (or `gofumpt` for stricter rules). With golangci-lint v2 these run via `golangci-lint fmt`.
Enable these once the baseline is clean:
| Linter | When to enable | |---|---| | `gosec` | Any service that handles untrusted input | | `bodyclose` | Any code that calls `http.Client.Do` | | `sqlclosecheck` | Any code using `database/sql` | | `nilerr` | Any code that does `if err != nil { return nil }` style returns | | `misspell` | Always — comments and strings | | `unconvert` | Always — flags useless type conversions | | `nolintlint` | Always — enforces the `//nolint` rules below | | `paralleltest` | If most tests can use `t.Parallel()` | | `thelper` | If you write test helpers (enforces `t.Helper()`) | | `testifylint` | If the project uses `testify` | | `gocyclo` / `gocognit` | When you want a complexity ceiling | | `exhaustive` | When you use `iota`-based enums and want full `switch` coverage |
> 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.
lint: golangci-lint run ./... lint-fix: golangci-lint run --fix ./... fmt: golangci-lint fmt ./... ci-lint: golangci-lint run --new-from-rev=origin/main ./...
| Task | Command | |---|---| | Run all enabled linters | `golangci-lint run ./...` | | Auto-fix everything fixable | `golangci-lint run --fix ./...` | | Format the tree (v2+) | `golangci-lint fmt ./...` | | Lint only changed code | `golangci-lint run --new-from-rev=origin/main ./...` | | Run one linter | `golangci-lint run --enable-only=govet ./...` | | Show which linters exist | `golangci-lint linters` |
`--new-from-rev` is what makes incremental adoption work: legacy code stays untouched, new and changed code must meet the bar.
> Read [references/ci-integration.md](references/ci-integration.md) when wiring GitHub Actions, pre-commit hooks, or selective linting on PRs.
// Good: specific linter + a reason //nolint:errcheck // fire-and-forget; Sync error is not actionable on shutdown _ = logger.Sync()
// Bad: blanket, no reason — nolintlint will flag this //nolint _ = logger.Sync()
Rules (enforced by `nolintlint`):
> 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.
Each finding looks like:
path/to/file.go:42:10: message describing the issue (linter-name)
The 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.
| Anti-pattern | Why it hurts | Do this instead | |---|---|---| | No `.golangci.yml` in the repo | Each contributor lints differently or not at all | Commit a baseline config; CI enforces it | | `//nolint` with no linter name | Disables every check on that line, silently | `//nolint:errcheck // reason` | | `//nolint:all` at the top of a file | Whole file escapes review | Suppress per construct with a reason | | Lint
26 production-grade Go skills for Claude Code, Gemini CLI, and opencode. Battle-tested patterns from the Go community — codified as triggerable AI skills.
Repo: muratmirgun/gophers
Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase, Repository, Delivery layers, inward…
Invoke this skill to systematically review a Go change against community style standards before merging. Walks the diff topic by topic — formatting, errors,…
Use when writing or reviewing Go code for clarity, formatting, control flow, variable declarations, switch usage, and function design. Covers the priority…
Use when writing or reviewing concurrent Go code — goroutines, channels, select, mutexes, atomics, errgroup, singleflight, worker pools, or fan-out/fan-in…
Use when designing, propagating, or debugging context.Context flow in Go — first-parameter placement, deadlines and cancellation, request-scoped values,…
Use when writing conditionals, loops, switches, type switches, or blank-identifier patterns in Go. Covers if-with-initialization, guard clauses, early returns,…