
这是 Go 模块机制里最基础、也最容易被误解的一组文件 。
我用 一句话 + 对照表 + 实际场景 ,把 go.mod 和 go.sum 讲透。
一句话先记住
go.mod 负责"声明我要用什么"
go.sum 负责"证明我用的就是它"
go.mod 是什么?
go.mod 是 模块的"需求说明书"。
它告诉 Go 工具链三件事:
- 这个项目是谁
- 依赖哪些模块
- 用哪些版本规则
示例 go.mod
module github.com/example/myapp
go 1.22
require (
github.com/gin-gonic/gin v1.9.1
go.uber.org/zap v1.27.0
)
go.mod 的核心作用
| 作用 | 说明 |
|---|---|
| 模块名 | module 路径 |
| Go 版本 | 编译语义 |
| 直接依赖 | require |
| 替换规则 | replace |
| 版本收敛 | indirect |
📌 go.mod 决定"依赖关系图"
go.sum 是什么?
go.sum 是 依赖完整性的"校验账本"。
它记录的是:
某个模块的某个版本,对应的内容哈希
示例 go.sum
github.com/gin-gonic/gin v1.9.1 h1:abcd...
github.com/gin-gonic/gin v1.9.1/go.mod h1:efgh...
go.sum 的核心作用
| 作用 | 说明 |
|---|---|
| 校验完整性 | 防止依赖被篡改 |
| 可复现构建 | 每个人拉到的内容一致 |
| 安全 | 防供应链攻击 |
📌 go.sum 决定"内容是不是同一个"
二者的本质区别(重点)
| 对比点 | go.mod | go.sum |
|---|---|---|
| 关注点 | 依赖关系 | 依赖内容 |
| 是否声明依赖 | ✅ | ❌ |
| 是否参与校验 | ❌ | ✅ |
| 是否可手写 | 可以 | 不建议 |
| 是否必须提交 | ✅ | ✅ |
一个真实的工作流(非常重要)
1️⃣ 写代码 / 加依赖
go get github.com/gin-gonic/gin
👉 Go 会自动:
- 修改
go.mod - 更新
go.sum
2️⃣ 构建 / CI
go build
Go 会:
- 按
go.mod决定要拉哪些模块 - 按
go.sum校验模块内容 - 校验失败 → 直接报错
为什么不能删 go.sum?
很多人问:
"我能不能删 go.sum,让它重新生成?"
❌ 生产项目不应该这么做
原因:
- go.sum 是安全边界
- 删了 = 放弃内容一致性
- CI/CD 可能直接失败
- 离线构建直接崩
常见误区(你可能踩过)
❌ 误区 1:只提交 go.mod
构建成功(我机器)
CI 失败(别人机器)
👉 因为 依赖内容不一致
❌ 误区 2:手改 go.sum
go.sum 是 机器生成文件
你手改它 ≈ 手改校验和,毫无意义
❌ 误区 3:go.sum 里有没用到的依赖
这是 正常现象:
- go.sum 记录的是 历史依赖校验
- 即使 go.mod 不再 require
- 只要构建路径出现过,就可能保留
一个非常形象的类比(最好记)
| 文件 | 类比 |
|---|---|
| go.mod | 购物清单 |
| go.sum | 收据 + 防伪码 |
- go.mod:我要买什么
- go.sum:我买到的确实是这件商品
在你这种工程场景里的实际建议
结合你做 Kubernetes / 平台 / Operator / Helm 的背景:
- ✅ go.mod + go.sum 必须一起提交
- ✅ 离线环境尤为重要
- ✅ 构建失败先看 go.sum 校验
- ❌ 不要忽略 indirect 依赖
一句工程师级总结
go.mod 解决"依赖关系"问题
go.sum 解决"依赖可信与可复现"问题
如果你愿意,我可以下一步帮你:
- 讲清楚 indirect 依赖为什么存在
- 解释 为什么 go.sum 会越变越大
- 结合 Operator / Controller 开发讲依赖治理
- 或写一篇你博客风格的《Go 模块机制避坑指南》
你想继续哪一块?