go.mod 必须提交且禁止手动编辑版本号,升级降级依赖统一用 go get 命令并运行 go mod tidy;go.sum 必须提交且不可忽略;私有模块需精确配置 GOPRIVATE;replace 仅限本地开发且严禁提交;internal 是访问控制契约而非目录名。

go.mod 必须提交,且不能手动编辑版本号
大厂 CI 流程会严格校验 go.sum 与依赖内容的一致性,一旦 go.mod 中的版本号被手工修改(比如把 v1.2.3 改成 v1.2.4),但没运行 go mod tidy,就会导致 go.sum 缺失对应校验和,CI 直接失败。
正确做法是:升级依赖统一用 go get github.com/foo/bar@v1.2.4,降级或修复漏洞也走这条命令;go mod tidy 会自动补全/清理 go.mod 和 go.sum。所有变更必须由命令驱动,而非编辑器直改。
- 禁止在 PR 中出现仅修改
go.mod而不带go.sum变更的提交 -
go.sum不是“锁文件”,而是校验快照,必须进 Git,且不允许忽略 - CI 中若发现
go.sum与本地不一致,应直接拒绝构建,而非自动重写
私有模块必须配置 GOPRIVATE,且前缀要精确
企业内部模块(如 git.internal.company.com/auth)若未配置 GOPRIVATE,Go 工具链会默认走代理(如 proxy.golang.org),结果就是拉不到、报 module not found 错误,甚至泄露请求日志。
配置方式是设置环境变量:go env -w GOPRIVATE=git.internal.company.com。注意:前缀必须完整匹配模块路径开头,不能只写 company.com —— 否则 github.com/company/private 也会被误判为私有,导致无法拉取公开仓库。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 多个私有域用逗号分隔,不支持通配符(
*不合法) - 团队应统一在 Dockerfile 或 CI 配置中预设该变量,避免开发机遗漏
- SSH 克隆地址需配合
~/.gitconfig的insteadOf规则,否则go get仍会走 HTTPS
replace 指令只允许用于本地开发,严禁提交到主干
replace 是调试利器,但也是技术债源头。大厂代码扫描工具(如 golangci-lint + 自定义规则)会把 replace 行标记为高危,要求说明原因并限期移除。
典型误用:为绕过某个 bug,在 go.mod 中写 replace github.com/bad/lib => github.com/good/fork v0.1.0 并合入 main 分支。后果是下游服务无法复现构建,且 fork 版本无人维护,最终演变成“隐式单点故障”。
- 临时替换必须加注释,例如:
// replace: local dev only, remove before merge - 上线前必须删除
replace,并通过发布正式 tag(v0.1.0)让下游go get到稳定版 - Monorepo 场景下优先用
go.work替代replace,它更透明、可版本化、IDE 支持更好
internal 包不是目录名,而是访问控制契约
很多人以为只要建个 internal/ 目录就能隔离代码,其实关键在路径层级:project/service/user/internal/db 只能被 project/service/user 下的包导入,project/cmd/app 或 project/pkg/util 都不行——哪怕它们物理上在同一 repo。
大厂代码审查会重点检查跨 internal 边界的 import,比如 import "project/service/user/internal/db" 出现在 project/api/handler.go 中,会被自动拦截并要求重构。
-
internal不解决循环依赖,它只做编译期可见性限制 - 不要为了“看起来私有”而把所有工具函数塞进
internal,该放pkg/的就放pkg/ - CI 可用
go list -deps -f '{{.ImportPath}}' ./... | grep internal扫描非法引用
go.mod 的变更节奏、GOPRIVATE 的粒度、replace 的生命周期、internal 的边界感——这些细节一旦松动,大型项目就会在几个月内退化成依赖泥潭。

















