Post
Go module 的主版本号路径
把主版本号写进 import path,看起来麻烦,但它把兼容性断点直接变成了可见的命名变化。
前些天修 go-commons-pool 的一个 bug,顺手把它发布成了 Go module。结果很快就收到反馈:用 module 方式依赖时有问题。
查了一下才发现,问题不在功能,而在版本规则。
因为 pool 的版本已经是 2.x 了,按 Go module 的规范,主版本号大于 1 时,import path 里必须带上版本号。也就是说:
- 以前是
import github.com/jolestar/go-commons-pool - 现在要写成
import github.com/jolestar/go-commons-pool/v2
第一次碰到这个规则时,直觉上会觉得很别扭:升级依赖,为什么还要改代码里的路径?
但后来再回头看 Russ Cox 对 vgo 原则的解释,会发现这个规则虽然不优雅,却有它非常明确的目标。
核心其实是三件事:
CompatibilityRepeatabilityCooperation
其中最关键的是兼容性。
软件一旦进入长期维护阶段,最怕的不是版本多,而是不小心升级到了一个不兼容版本。其他很多语言会把这件事留给库作者和用户自己约定,比如:
- 继续沿用原路径,用户自己承担升级修复成本
- 改个库名,等于重新发一个项目
- 或者靠工具链和 lock file 做更多外部约束
而 Go module 相当于把其中一种做法强制标准化了:
只要你升级了主版本号,并且这个升级是不兼容的,那你就必须在路径上把它显式标出来。
效果上,这几乎等于说:不兼容升级就相当于给库改了名字。
这件事最大的好处,是多版本可以明确共存,用户也不会在无意中被“平滑升级”到一个行为已经不兼容的版本。
从工程角度看,这确实挺合理。
所以我现在对这个设计的理解是:它不是为了让语法更好看,而是在用路径层面的显式性,换取更稳定的依赖管理边界。
丑是丑了点,但它把很多原来靠约定和运气维持的事情,变成了工具链能强制执行的规则。
原微博中的媒体
