Linting
| Field | Value |
|---|---|
| Type | Skill Resource |
| Source | ~/.copilot/skills/backend/references/lint.md |
| Description | Not specified |
Source Content
Linting
task lint runs golangci-lint v2 in CI and locally, on every PR. The stock set catches the obvious stuff — unused variables, ineffectual assignments, formatting. The linters below are enabled on top of that, and each one earns its place because it catches a real bug class this codebase has seen, not because more linters is better.
Linters enabled beyond the defaults
| Linter | Catches |
|---|---|
errorlint | Comparing errors with == or a type switch instead of errors.Is/errors.As, which silently breaks once the error is wrapped (references/errors.md) |
nilerr | Returning nil when the checked error was actually non-nil — an accidental swallow that turns a real failure into a silent success |
forcetypeassert | A type assertion x.(T) without the , ok form — panics on a mismatch instead of returning an error |
containedctx | A context.Context stored in a struct field instead of passed as a parameter — this skill already bans the pattern in references/concurrency.md; the linter is what enforces it automatically instead of relying on review to catch it |
fatcontext | A context accidentally captured or extended inside a loop, leaking that iteration’s context (and its cancellation/values) across every subsequent iteration |
bodyclose | An HTTP response body that is never .Close()’d, leaking the underlying connection |
sqlclosecheck | The same leak for *sql.Rows/*sql.Stmt |
rowserrcheck | Missing rows.Err() after a for rows.Next() loop — the loop can stop early on a real error that is never surfaced, and the caller reads a silently truncated result set |
durationcheck | Multiplying two time.Duration values together, which is almost always a units bug (time.Second * time.Second is not two seconds) |
copyloopvar | Mostly historical now that Go 1.22+ fixed loop-variable capture at the language level — flags code still working around the old bug unnecessarily, or older-style range code that still has it |
paralleltest | A table-driven test missing t.Parallel() where references/testing.md’s own standard calls for it |
nolintlint | A bare //nolint with no linter name and no reason — every suppression must state which linter it silences and why |
This is additive: it assumes whatever golangci-lint’s default set already enforces, and references/testing.md’s ≥80% coverage floor on domain and service, are both still in force. task lint replaces neither.