go模块化是编译器强制机制:internal/为硬性访问拦截点,module path须匹配托管地址,每个业务域应独立go.mod,pkg/是需sla保障的公共契约。

模块化设计在 Go 里不是风格选择,而是编译器强制执行的边界控制机制——不按规则组织,go build 会直接报错。
internal/ 目录不是“建议放私有代码的地方”,而是 Go 工具链的硬性访问拦截点
只要路径中含 /internal/,Go 编译器就禁止该包被其父目录以外的模块导入。这不是约定,是编译期错误:
- 错误现象:
import "myapp/internal/config"在cmd/api/main.go中能编译,但在外部项目中 import 同一路径会报use of internal package not allowed - 正确用法:把通用配置加载逻辑放在
internal/config,由cmd/api和cmd/worker共享;但绝不允许pkg/util或其他模块去 import 它 - 容易踩的坑:在
go.mod中对internal/xxx加replace——这会绕过 internal 规则,导致本该隔离的实现被意外暴露
模块路径(module path)决定依赖能否被正确解析,不是起个名字就行
初始化模块时写的 go mod init 路径,就是未来所有 import 语句的前缀,也是 go get 的唯一依据:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 错误写法:
go mod init myproject→ 导致别人go get myproject拉不到代码,IDE 无法识别模块,go list -m all显示路径混乱 - 正确写法:用托管地址根路径,如
go mod init github.com/yourorg/payment,即使代码还没 push 到 GitHub 也应如此 - 私有仓库场景:必须匹配
git.company.com/team/project格式,并配好GOINSECURE或 git credential helper,否则go mod download会失败
业务包不能靠目录名“假装”模块化,必须由 go.mod 显式声明边界
Go 不看目录结构是否叫 /user 或 /order,只认 go.mod 文件是否存在、路径是否唯一:
- 单体项目误操作:所有业务包共用一个
go.mod→ 改支付服务时升级了用户服务的依赖版本,CI 构建直接失败 - 推荐做法:每个高内聚业务域(如
user、order)单独建go.mod,主应用通过replace本地联调,上线前发布语义化 tag - 关键验证点:运行
go list -m all | grep user,输出必须是完整模块路径(如github.com/yourorg/user v0.1.0),而不是./internal/user
pkg/ 下的包不是“工具集合”,而是你愿意签 SLA 的对外契约
pkg/ 是团队共识的公共能力出口,不是垃圾桶。一旦放进 pkg/,就得承担向后兼容责任:
- 命名必须语义明确:
pkg/logger可以,pkg/common不行;pkg/httpx表明封装 HTTP 客户端,pkg/util这种模糊命名会让使用者无法判断是否安全依赖 - 使用门槛:每个
pkg/xxx必须有完整单元测试、文档注释,且被至少两个非internal/模块显式 import 才算成立 - 危险信号:当某个
pkg/包被超过 3 个业务模块强依赖,说明它正在演变成“上帝包”,该按场景拆成pkg/auth/jwt、pkg/auth/oauth2等更细粒度子包
真正难的不是写对 go mod init,而是每次新增一个 import 时,能立刻判断出它该落在 internal/、pkg/ 还是独立模块里——这个决策一旦错了,半年后重构成本就是指数级的。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










