go mod tidy仅删除两类模块:未被任何.go文件显式import且未被其他已保留模块间接依赖的模块;它无法识别反射调用、配置驱动、构建标签控制或测试专用依赖,需人工验证。

go mod tidy 不能“彻底”清理,它只删掉静态分析确认无引用的模块;反射、配置驱动、构建标签控制的依赖,它完全看不见。
go mod tidy 到底删什么、不删什么
它只删除两类模块:go mod tidy 扫描所有 .go 文件中**显式、启用状态下的 import 语句**,然后递归计算依赖图。真正被删的只有:
- 没出现在任何
.go文件的import行中(注释掉的// import "net/http"不算) - 也没被其他已保留模块通过其
go.mod间接 require
以下情况它一律保留,哪怕你代码里根本没调用:
-
import _ "github.com/lib/pq"这种空白导入——只要没被其他包依赖且自身无init()副作用,go mod tidy可能删,但风险高 -
//go:embed assets/或//go:generate stringer -type=Mode引用的模块,不算 import,会被删 - 仅在
//go:build integration下才生效的import,若当前构建未启用该 tag,go mod tidy默认忽略,模块可能被误删 - 测试文件(
*_test.go)里的 import,默认不扫描;要覆盖,得加-test参数配合go mod why -m xxx -test
怎么查一个模块到底是不是真没用
别只看 go.mod 里有没有 require 行,那只是声明,不是事实。用 go mod why 看真实路径:
立即学习“go语言免费学习笔记(深入)”;
-
go mod why golang.org/x/sync→ 如果返回main module does not need module,才是安全信号 -
go mod why -m golang.org/x/sync -test→ 加上-test才能看到是否只被测试引用 - 如果返回路径含
vendor/或internal/子目录,说明它被子模块显式 require,不能单靠根目录tidy清理
再补一手静态验证:go list -deps -f '{{if not .Standard}}{{.ImportPath}}{{end}}' ./ | sort -u 输出的是当前构建路径下所有实际展开的非标准库 import,和 go mod graph 对比更准。
清理后必须跑的三件事,缺一不可
删完不验证,等于白干。尤其注意交叉编译和测试场景:
-
go build ./→ 确保所有子命令(尤其是cmd/目录下的二进制)能编译通过;go build -o /dev/null ./...更快 -
go test ./→ 测试文件可能引入独立依赖,go mod tidy默认处理它们,但得确认没 break;CI 中建议强制加-race和-tags=integration覆盖更多路径 -
go list -m all | wc -l→ 对比前后数字,下降明显才说明真删了东西;再跑一次go mod graph | wc -l,边数减少才代表依赖图收缩
最后提醒:如果项目用了 go.work,先 rm go.work 再 tidy,否则工作区会干扰单模块分析;go mod vendor 后的目录不会自动同步 tidy 结果,要删旧 vendor 再 go mod tidy -v。


















