装完golangci-lint不等于自动受检——默认仅启用govet、errcheck,staticcheck、unused等关键检查器需在同级go.mod目录下配置.golangci.yml显式启用;多module项目须各module单独配置,CI和IDE均需指定--config=.golangci.yml才生效。

装完 golangci-lint 不等于项目就自动受检了——没配 .golangci.yml,它默认只开 govet 和 errcheck,其余关键检查(如 staticcheck、unused)全关着,CI 和本地都等于白跑。
为什么 golangci-lint run 没报错却像没运行一样
根本不是工具坏了,而是配置压根没加载成功:
-
.golangci.yml必须和go.mod在同一级目录;放在src/下、写成.golangci.yaml或丢在父目录都无效 - 多 module 项目(比如 monorepo),每个 module 都得单独放一份配置,不会继承根目录的
- CI 中直接执行
golangci-lint run很容易漏掉配置,必须显式加参数:golangci-lint run --config=.golangci.yml - VS Code 的 Go 插件也要手动勾选「使用配置」并填对路径,否则可能沿用旧缓存或默认规则
该启用哪几个 linter 才真正有用
别被默认启用的十几个 linter 吓住——多数误报高、维护成本大。最小有效组合是这 5 个,误报低、问题暴露率高:
-
govet:查类型不匹配、死循环、变量 shadowing 等基础语义问题 -
errcheck:强制检查error是否被处理,避免静默失败(比如忘了defer resp.Body.Close()) -
staticcheck:报SA1019(调用弃用 API)、SA4006(死循环)、SA1017(time.After 在循环里滥用)等真实风险 -
unused:发现未使用的变量、函数、import,清理技术债务(但新手可先禁用,避免干扰学习节奏) -
revive:替代已弃用的golint,支持自定义规则(如禁止_ = xxx忽略 error)
明确不推荐:dupl(重复代码检测阈值难调、易误报)、lll(行长限制纯属风格偏好,不该卡 CI)。
立即学习“go语言免费学习笔记(深入)”;
配置文件里最容易踩的坑
.golangci.yml 看似简单,但几处细节一错,整个检查就失效:
-
enable和disable不能共存——选一个策略就行;日常强烈推荐只用enable显式声明,避免隐式继承带来的意外告警 - 必须加
skip-dirs: ["vendor", "generated"],否则staticcheck会在 protobuf 或 swagger 生成代码里狂报错 -
run.timeout建议设为2m以上,小项目无所谓,但 vendor 多或含大量 generated 代码时容易超时失败 -
linters-settings里别乱调min-confidence,尤其对gosec这类安全检查器,调低会漏掉中危漏洞
本地 pre-commit 怎么做才不反人类
手动跑 golangci-lint run 容易忘,CI 报错又太晚。最顺手的是 Git pre-commit 钩子:
- 进
.git/hooks/,重命名pre-commit.sample为pre-commit,加上执行权限:chmod +x pre-commit - 脚本里写清楚路径:
golangci-lint run --config=.golangci.yml --timeout=90s ./...(注意末尾的./...表示只检查暂存区变更文件) - 别用
--fast模式——它跳过unused等耗时检查,适合本地快速验证,但不该进pre-commit,否则漏问题 - 如果团队用不同 shell(zsh/bash),钩子里显式指定解释器:
#!/usr/bin/env bash,避免因$PATH差异导致找不到golangci-lint
真正麻烦的不是配置本身,而是不同 Go 版本下 linter 行为不一致——比如 staticcheck 在 Go 1.21 和 1.22 对泛型的检查粒度就变了,CI 和本地环境务必对齐 Go 版本。


















