4. SDK版本管理
版本管理这事儿,说简单也简单,说复杂真能坑死人。我见过太多团队,版本号随便拍脑袋定,Changelog 全靠脑补,结果用户升级后一脸懵——「我到底该不该升这个版本?」
今天咱们就把 SDK 版本管理这件事彻底聊透。我会从语义化版本规范讲起,再到版本号怎么生成、Changelog 怎么写,最后聊聊向后兼容性这个老大难问题。
语义化版本规范:为什么是 X.Y.Z?
先问个问题:你见过 1.2.3.4 这种版本号吗?我见过。还有 v2019.08.15-beta2 这种。说实话,这种版本号除了让人头大,没有任何好处。
语义化版本规范(SemVer)的核心思想很简单:版本号要传达含义。格式就是 主版本号.次版本号.修订号,比如 2.1.3。
核心规则:
- 主版本号:做了不兼容的 API 修改时递增
- 次版本号:新增了向下兼容的功能时递增
- 修订号:做了向下兼容的问题修复时递增
举个例子。你的 SDK 当前是 1.0.0。如果修复了一个 bug,变成 1.0.1。如果加了个新接口但没破坏旧的,变成 1.1.0。如果改了某个接口的签名,老代码跑不起来了,那就得 2.0.0。
我个人习惯在项目初期就把这个规范写进 README 里。别觉得多余,等用户来问「为什么 1.2.0 到 1.3.0 我的代码就编译不过了」的时候,你就知道规范有多重要了。
版本号生成策略:手动还是自动?
版本号怎么生成?这里分两种情况聊。
手动管理
小团队、内部 SDK,手动管理完全够用。我建议在项目根目录放一个 VERSION 文件,里面就一行数字:
1.2.3
然后 CI 脚本读这个文件,打 tag、生成包名。简单粗暴,但有效。
自动生成
大型项目或者多人协作的 SDK,手动管理容易出问题。我记得有一次,两个同事同时改了 VERSION 文件,结果一个版本号被覆盖了,发布出去的东西版本号对不上。那叫一个乱。
自动生成策略我推荐两种:
- 基于 Git Tag:每次发布打 tag,CI 自动读取 tag 作为版本号。这是最稳妥的方式。
- 基于 CI 构建号:比如
1.2.3.${BUILD_NUMBER},适合内部测试版本。
小技巧:预发布版本可以用 -alpha、-beta、-rc.1 这样的后缀。比如 2.0.0-alpha.1。这样用户知道这还是个预览版,别用到生产环境。
Changelog 编写规范:别让用户猜
Changelog 这东西,写好了是用户的好帮手,写不好就是废纸一张。我见过最离谱的 Changelog 就一句话:「修复了一些 bug,优化了性能」。你想想看,用户看到这个会怎么想?「到底修了啥?我该不该升?」
好的 Changelog 应该遵循 Keep a Changelog 规范。核心原则:
- 每个版本单独一个区块
- 按
Added、Changed、Deprecated、Removed、Fixed、Security分类 - 每条记录要具体,能让人看懂
举个例子:
## [2.1.0] - 2024-03-15
### Added
- 新增 `UserManager.getProfile()` 接口,支持获取用户详细信息
- 新增日志级别动态调整功能,可通过 `setLogLevel()` 在运行时切换
### Changed
- 优化网络请求的重试策略,默认重试次数从 3 次改为 2 次
- 升级底层加密库从 OpenSSL 1.1.1 到 3.0.0
### Fixed
- 修复在 Android 12 上调用 `startLocation()` 偶现崩溃的问题
- 修复 iOS 端内存泄漏,涉及 `ImageLoader` 类的循环引用
### Deprecated
- `getUserInfo()` 方法已标记为废弃,请使用 `getProfile()` 替代
看到了吗?每条记录都告诉用户「改了啥、为什么改、怎么用新的」。这才是合格的 Changelog。
注意:千万别在 Changelog 里写「内部重构」「代码优化」这种话。用户不关心你重构了什么,他们只关心「这个版本对我有什么影响」。
向后兼容性保证:SDK 的生命线
向后兼容性,说白了就是「老用户的代码能不能继续跑」。这是 SDK 开发中最容易翻车的地方。
我曾经在一个支付 SDK 里,把一个方法的参数从 int 改成了 long。当时觉得「反正 Java 会自动转型,没问题」。结果用户那边用的是 C++ 绑定,类型不匹配直接编译不过。那次事故让我记住了:永远不要假设用户的调用方式和你一样。
保证向后兼容性,我总结了几个实战经验:
- 接口签名不要改:方法名、参数类型、返回值类型,动了就是破坏兼容性。真要改,加新方法,废弃旧方法。
- 新增字段要给默认值:比如 JSON 返回里加了个新字段,老客户端解析不到怎么办?给个合理的默认值。
- 废弃接口要留过渡期:标记
@Deprecated后,至少保留两个大版本再删除。给用户迁移的时间。 - 行为变更要谨慎:有时候接口没变,但内部行为变了。比如之前返回 null,现在返回空列表。这种「隐形破坏」最坑人。
我的原则:每次发布前,跑一遍老版本的单元测试。如果老测试用例在新版本上失败了,那就是破坏兼容性了。别犹豫,改回去。
知识体系总览
下面这张图,把 SDK 版本管理的核心逻辑串起来了。你可以把它当作一个检查清单:
实战中的几个坑
最后分享几个我踩过的坑,希望能帮你少走弯路。
坑一:版本号跳得太快。 有个项目从 1.0.0 直接跳到 2.0.0,就改了个日志格式。用户一看主版本号变了,以为 API 全改了,吓得不敢升级。其实完全没必要。
坑二:Changelog 只写内部编号。 比如「修复了 JIRA-1234」。用户哪知道 JIRA-1234 是啥?他们只想知道「这个 bug 会不会影响我」。
坑三:偷偷改行为。 我曾经在一个次版本更新里,把某个接口的默认超时时间从 30 秒改成了 10 秒。结果用户那边业务逻辑依赖这个超时时间,直接崩了。嗯,从那以后我改任何默认值都会在 Changelog 里大写加粗提醒。
我的建议:每次发布前,花 10 分钟写一份「升级指南」。告诉用户这个版本改了啥、怎么迁移、有什么注意事项。这份文档比 Changelog 更详细,适合大版本升级时用。
版本管理看起来是小事,但做不好真的会劝退用户。你想想看,一个 SDK 连版本号都管不明白,用户怎么敢把它用到生产环境?
好了,这一章的内容就到这里。记住一句话:版本号是 SDK 的名片,Changelog 是 SDK 的说明书,向后兼容是 SDK 的信用。这三样做好了,你的 SDK 就成功了一半。