2、语义化版本控制(SemVer):主版本号、次版本号、修订号的定义,版本范围与兼容性规则
版本号这东西,说白了就是SDK的身份证。你发布一个SDK,别人怎么知道这次更新能不能直接升?会不会炸?全靠版本号说话。
我个人习惯,所有SDK必须用语义化版本控制,也就是SemVer。这不是什么花架子,是实打实的行业标准。格式很简单:主版本号.次版本号.修订号,比如 2.1.3。
三个数字,各司其职
咱们一个一个拆开看:
- 主版本号(Major):做了不兼容的API改动。说白了,用户升级后,原有代码可能跑不起来。比如我把某个接口的参数类型改了,或者删了一个公开方法。这时候主版本号必须加1。
- 次版本号(Minor):新增了功能,但向下兼容。用户升级后,原有代码完全不受影响。比如我加了一个新的工具类,或者新增了一个可选参数。这时候次版本号加1,修订号归零。
- 修订号(Patch):只做bug修复,不新增功能。比如修复了一个空指针异常,或者优化了某个方法的性能。这时候修订号加1。
嗯,这里要注意:一旦主版本号变了,次版本号和修订号都要归零。同理,次版本号变了,修订号归零。这个规则不能乱。
核心原则:版本号本身就是在告诉用户——这次更新,你该不该慌。
版本范围与兼容性规则
光有版本号还不够。你在SDK的依赖声明里,怎么告诉用户「你可以用哪个版本」?这就涉及到版本范围了。
我见过不少团队,直接写死版本号:依赖: "1.2.3"。这其实很坑。用户想升级修复一个小bug,还得手动改版本号。你想想看,这多麻烦。
更好的做法是用版本范围表达式。不同包管理工具写法略有不同,但核心逻辑一致:
| 表达式 | 含义 | 示例 |
|---|---|---|
^1.2.3 |
兼容主版本号1,允许次版本号和修订号升级 | 允许 1.2.3 到 1.x.x |
~1.2.3 |
兼容次版本号1.2,只允许修订号升级 | 允许 1.2.3 到 1.2.x |
>=1.2.3 <2.0.0 |
指定一个范围区间 | 允许 1.2.3 到 1.999.999 |
1.2.x |
通配符,允许该范围内的任意修订号 | 允许 1.2.0 到 1.2.999 |
我曾经在一个项目里,因为用了 ^1.0.0 的写法,结果依赖的SDK从1.0.0升级到了1.1.0,新增了一个接口。我的代码没用到那个接口,所以完全没问题。这就是次版本号兼容的好处。
我的建议:对外发布的SDK,依赖声明尽量用 ^ 或 ~,别写死。给用户留点升级空间。
避坑指南:版本号不是随便写的
我曾经接手过一个SDK,版本号直接从 1.0.0 跳到了 2.0.0,结果一看改动,只是改了个日志输出格式。这其实是个修订号级别的改动,硬生生被标成了主版本升级。用户一看主版本变了,以为API全改了,吓得不敢升。
反过来,我也见过有人把删了一个公开接口的改动,只改了修订号。结果用户升级后,代码直接编译不过。嗯,这种坑踩一次就够了。
警告:主版本号0(比如0.x.x)表示开发阶段,不保证兼容性。很多新手SDK发布者,在0.x阶段就承诺「稳定」,这是不负责任的。我个人习惯,主版本号不到1.0.0,绝不对外正式发布。
一张图看懂SemVer
下面这张图,是我自己总结的SemVer决策流程。每次发布SDK前,我都会过一遍:
这张图的核心逻辑很简单:先问自己三个问题。破坏兼容性?升主版本。新增功能?升次版本。只修bug?升修订号。如果什么都没改,那就别发版本了。
版本号背后的信任
说实话,版本号这东西,本质上是一种契约。你承诺了兼容性,用户才敢放心升级。一旦你打破了规则,比如在修订号里偷偷改了API,用户发现后,对你的信任就没了。
我记得有一次,一个第三方SDK在修订号里改了接口返回值类型。我升级后,线上直接报错。从那以后,我再也不敢用那个SDK了。你想想看,一个版本号都管不好的团队,代码质量能好到哪去?
一句话总结:SemVer不是束缚,是保护。保护你的用户,也保护你自己的声誉。
好了,版本号的定义和规则就聊到这。记住三个数字的含义,记住版本范围的写法,更记住——别在版本号上耍小聪明。