go mod tidy只删除两类模块:既未被任何.go文件import语句引用,也未被其他已保留模块间接依赖的模块;它无法识别反射调用、配置驱动、测试专用、//go:embed或条件编译下的依赖,需人工交叉验证。

go mod tidy 会删掉哪些依赖
它只删两类:既没出现在任何 .go 文件的 import 语句中,也没被其他已保留模块间接依赖的模块。比如你执行过 go get github.com/uber-go/zap,但后续删光了所有 import "go.uber.org/zap",且没有别的包(如 github.com/labstack/echo/v4)依赖它,那 go mod tidy 就会把它从 go.mod 里清掉。
但它不会删这些:
-
import _ "github.com/lib/pq"这种空白导入——只要存在,就视为显式需要 -
//go:build integration下的import,若当前构建未启用该 tag,tidy 默认忽略,可能误删 -
//go:embed引用的包(如模板、静态文件),不经过 import,tidy 完全看不见 - 运行时通过
plugin.Open()或reflect.ImportPath()加载的包,静态分析无法捕获
为什么 go mod tidy -v 输出里有 “removing unused” 却仍不能放心删
因为 go mod tidy -v 的 “unused” 是基于当前构建环境下的 import 图计算的,而 Go 的构建环境受 GOOS、GOARCH、BUILD tags 影响。同一份代码,在 GOOS=linux 下 tidy 后删掉的模块,可能在 GOOS=windows 下是必需的。
常见踩坑点:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 没在对应平台下运行
GOOS=js go list -deps ./,就贸然 tidy,结果 wasm 构建失败 - 测试文件(
*_test.go)里 import 了大包(如github.com/onsi/ginkgo/v2),但没加-test参数跑go mod why,误判为可删 -
go.mod里有replace指向本地路径,tidy 会照常处理,但实际构建时可能因路径失效导致 panic
怎么确认一个巨大依赖(比如 golang.org/x/tools)真能删
别只看 go mod graph,先查它进来的路径是否真实参与构建:
- 运行
go mod why -m golang.org/x/tools,如果返回main module does not need module,基本安全 - 加
-test再试:go mod why -m golang.org/x/tools -test,确认测试也不依赖 - 检查是否有代码生成工具(如
stringer、mockgen)在go:generate注释里 import 它——这类依赖不会出现在普通import列表中 - 临时注释掉
go.mod中对应行,再跑go build ./和go test ./,尤其注意cmd/目录下的二进制是否还能编译
删完之后体积没变小?别急着怀疑 tidy
Go 编译器本身不做跨包死代码消除,即使依赖被移除了,如果它的类型或接口被其他保留包导出(比如某个 SDK 导出了 golang.org/x/net/http2.ClientConn),那整个 x/net 仍会被链接进去。
真正影响二进制体积的,往往是间接依赖链里的“中间层”:比如你用了 aws-sdk-go-v2,它拉进来 golang.org/x/crypto,而后者又带入 golang.org/x/sys。这时单删 x/crypto 不生效,得靠 go mod graph | grep aws 看清完整路径,再决定是升级 SDK 版本(新版可能已替换底层加密库),还是用 go mod edit -droprequire 强制断开(需验证运行时行为)。
最容易被忽略的是:go mod vendor 后再 tidy,vendor/ 目录不会自动同步更新——删完必须重跑 go mod vendor,否则旧包还在磁盘上占着空间。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










