go mod tidy 不能自动删除未被 import 的间接依赖,仅清理 go.mod 中显式声明但未被引用的模块;对测试工具引入的“幽灵依赖”等需配合 go list -m all 和 go mod why 手动排查并用 go mod edit -droprequire 清理。

go mod tidy 不能自动删掉没用的依赖
它只清理 go.mod 中显式声明但未被 import 的模块,对间接引入的“幽灵依赖”无能为力。比如某个测试工具通过 require 拉进来了 github.com/some/old-logger v0.1.0,而你早已不用这个 logger,go mod tidy 不会主动把它踢出去。
- 先运行
go list -m all | grep old-logger确认是否在依赖树里 - 再用
go mod why github.com/some/old-logger查它被谁拉进来——如果输出指向一个已删除或仅用于 test 的 module,就该手动go mod edit -droprequire github.com/some/old-logger - 检查
go.mod里的replace和exclude,长期残留的语句可能锁死旧版本,阻碍自动清理
标准库能搞定的,别碰第三方“全家桶”
很多体积膨胀不是因为代码多,而是框架把一堆你根本不用的功能打包进来了。比如只做 HTTP 转发,却用了 gin 或 echo,它们默认带中间件、模板引擎、日志封装,连带引入 golang.org/x/net、golang.org/x/text 等一长串间接依赖。
- 路由用
net/http+http.ServeMux就够,http.HandleFunc写三行就能跑 - JSON 序列化坚持用
encoding/json,除非真需要jsoniter的性能差异(通常没有) - ORM 场景下,
sqlc生成纯database/sql代码,比gorm少掉一半依赖树
CGO_ENABLED=0 和 -ldflags="-s -w" 必须同时生效
单独开其中一个,效果打对折。特别是 -w 在 Windows 上无效,但 CGO_ENABLED=0 在所有平台都起作用——它防止 net 包 fallback 到 cgo 实现,避免悄悄链接 libc。
- 验证方式:构建后执行
ldd your-binary,输出not a dynamic executable才算成功 - 命令必须写成
CGO_ENABLED=0 go build -ldflags="-s -w" -trimpath -o app .,漏引号或顺序错(如-ldflags=-s -w)会让-w被 shell 当成go build自己的参数,实际没传给 linker -
-trimpath不直接减体积,但它缩短编译路径字符串,让-s剥离得更干净——尤其在 CI 环境路径长时效果明显
UPX 压缩前务必确认 -buildmode=exe 和 PIE 状态
Go 1.16+ 默认启用 PIE(Position Independent Executable),而 UPX 对 PIE 支持不稳定,压缩后大概率 panic 或启动失败。这不是配置问题,是工具链限制。
- Linux/macOS 下先用
file your-binary看是否含PIE字样;含的话,加-buildmode=exe重编再压(Windows 必须加) - UPX 命令要带
--best --lzma,否则压缩率低;但注意部分安全扫描器会将 UPX 打包的二进制标为可疑,上线前得跟安全部门对齐 - distroless 镜像里别装 UPX——它只应在构建阶段用,最终镜像只放压缩后的二进制
replace 锁死了 v1.2.3 版本,导致整个依赖树无法升级到精简过的 v2.0.0;或者以为 go mod tidy 已经干干净净,结果 go list -m all 里还躺着三个 v0.x 的孤儿包。这些细节不查,光调 -ldflags 只能砍掉表层浮肉。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











