Day 050 — go vet & golangci-lint¶
Month 2 · Week 4 · ⬅ Day 049 · Day 051 ➡ · Journal index
🎯 Learning Objective¶
Catch bugs the compiler won't, using go vet (built in) and golangci-lint (an aggregator), and wire them into a reproducible workflow.
📚 Topics¶
go vetanalyzers and what each one catchesstaticcheckvsgolangci-lint(meta-linter)- Configuring linters with
.golangci.yml; suppressing false positives
📖 Reading / Sources¶
📝 Notes¶
go vetships with the toolchain and runs a curated set of analyzers (thegolang.org/x/tools/go/analysisframework). It reports correct-but-suspicious code:Printfverb/arg mismatches, struct tags that won't parse,sync.Mutexcopied by value, unreachable code, shadowederr(via-vetshadow/shadow), lostcontext.CancelFunc, loop-variable capture, etc. → links to [[error-handling]] and [[printf-verbs]].go testautomatically runs a subset ofgo vetbefore tests; a vet failure fails the test run. So clean vet output is table stakes, not optional.go vetis a checker, not a fixer — it never modifies code. Pair it withgofmt/goimports(which do rewrite).golangci-lintis a fast meta-linter that runs many linters in one pass with shared parsing/caching. Default set includesgovet,staticcheck,errcheck(unchecked errors),ineffassign,unused, and more. It is a third-party binary you run as a tool — never an import — so it doesn't affect what your program compiles to.staticcheckis the highest-signal linter: SA-checks find real bugs (e.g.SA4006value never used,SA1029wrong context key type), ST-checks are style.golangci-lintbundles it.- Suppress a single false positive with a
//nolint:lintername // reasoncomment — narrowly, with a reason, never blanket-disabled. - The classic vet catch:
fmt.Printf("%d", "hello")compiles fine (variadicany) but is a bug; vet flags the verb/type mismatch.
💻 Code Examples¶
// vet flags every line below — all compile cleanly.
fmt.Printf("%d\n", "not a number") // %d expects int, got string
type T struct {
Name string `json:Name` // bad struct tag: missing quotes -> json:"Name"
}
var mu sync.Mutex
locked := mu // copies a Mutex by value -> "assignment copies lock value"
_ = locked
A minimal .golangci.yml:
No runnable example:
golangci-lint/staticcheckare external binaries, not importable stdlib. See the tooling drills inexercises/month-02/week-4/.
🏋️ Exercises / Practice¶
| Exercise | Status | Link |
|---|---|---|
Run go vet ./... on the week's exercises |
✅ | exercises/month-02/week-4 |
Add a deliberate Printf bug, confirm vet catches it |
✅ | exercises/month-02/week-4 |
🐛 Mistakes Made¶
- Assumed
go vetwould auto-fix formatting — it doesn't; only reports.gofmt -wfixes formatting. - Tried
import "github.com/golangci/golangci-lint"— it's a CLI tool, not a library; install the binary and run it.
❓ Open Questions¶
- Which linters are worth enabling beyond the default set without drowning in noise? (Leaning:
errcheck,gosec,reviveselectively.)
🧠 Active Recall (answer without looking)¶
- Q: Why can
fmt.Printf("%d", "x")compile yet still be a bug, and what catches it?A
Printf takes ...any, so any argument type compiles. The format string is a runtime contract the compiler doesn't check. go vet's printf analyzer matches verbs to arg types statically and flags the mismatch.
2. Q: Does go test run go vet? A
Yes — go test runs a high-confidence subset of vet analyzers before building/running tests, and a vet error fails the test run. Disable with go test -vet=off (rarely a good idea).
🪶 Feynman Reflection¶
The compiler proves your code is legal Go; vet and linters argue it's probably correct Go. They read patterns a human reviewer would flag — a format verb that doesn't match its argument, an error you forgot to check, a mutex you accidentally copied — and surface them before they become production incidents.
🕳️ Knowledge Gaps¶
- Writing a custom
analysis.Analyzer— deferred; consuming existing ones is enough for now.
✅ Summary¶
I can run go vet and golangci-lint, read their output, configure a sane linter set, and suppress false positives narrowly with //nolint.
⏭️ Next Steps / Prep for Tomorrow¶
- Day 051: capture these commands in a
Makefileso the whole workflow is onemakeaway.
| Time spent | Difficulty | Confidence |
|---|---|---|
| 90 min | 🟦🟦⬜⬜⬜ | 🟦🟦🟦⬜⬜ |
Suggested commit: docs(journal): go vet and golangci-lint (day 050)