Skip to content

Linting

FieldValue
TypeSkill Resource
Source~/.copilot/skills/backend/references/lint.md
DescriptionNot 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

LinterCatches
errorlintComparing errors with == or a type switch instead of errors.Is/errors.As, which silently breaks once the error is wrapped (references/errors.md)
nilerrReturning nil when the checked error was actually non-nil — an accidental swallow that turns a real failure into a silent success
forcetypeassertA type assertion x.(T) without the , ok form — panics on a mismatch instead of returning an error
containedctxA 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
fatcontextA context accidentally captured or extended inside a loop, leaking that iteration’s context (and its cancellation/values) across every subsequent iteration
bodycloseAn HTTP response body that is never .Close()’d, leaking the underlying connection
sqlclosecheckThe same leak for *sql.Rows/*sql.Stmt
rowserrcheckMissing 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
durationcheckMultiplying two time.Duration values together, which is almost always a units bug (time.Second * time.Second is not two seconds)
copyloopvarMostly 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
paralleltestA table-driven test missing t.Parallel() where references/testing.md’s own standard calls for it
nolintlintA 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.