用途
该技能用于设计稳定、兼容的公共 API,主要面向 NuGet 包、类库以及分布式系统的 API 维护者。其核心目标是减少用户升级时的摩擦,让旧代码在新版本中继续可用。
三类兼容性
技能将兼容性分为三类:
- API/源码兼容:代码能针对更新版本编译,涉及公共方法签名和类型。
- 二进制兼容:已编译代码能在更新版本上运行,涉及程序集布局和方法标记。
- 线格式兼容:序列化数据能被其他版本读取,涉及网络协议和持久化格式。
破坏其中任何一类都会给用户带来升级摩擦。
只扩展设计
稳定 API 的基础是“永不删除或修改,只扩展”,包含三个支柱:
- 既有功能不可变:一旦发布,行为和签名即被锁定。
- 通过新构造提供新功能:新增重载、新类型、可选启用的特性。
- 仅在弃用期后移除:以年计,而非以发布次数计。
其收益是旧代码在新版本中继续工作,新旧路径共存,升级默认不破坏,用户可按自己的节奏升级。
安全变更与不安全变更
安全变更(任何版本均可):新增委托给现有方法的重载、新增类型/接口/枚举、为现有类型新增成员。
不安全变更(永不或仅主版本):删除或重命名公共成员、更改参数类型或顺序、更改返回类型、更改访问修饰符、给现有方法添加可选参数(会破坏二进制兼容,因为可选参数默认值在编译时被写入调用方程序集,旧调用方会在运行时遇到 MissingMethodException)、添加无默认值的必需参数。正确做法是新增重载方法。
弃用模式
- 任意版本中标记为过时并注明版本,如 [Obsolete("Obsolete since v1.5.0. Use ProcessAsync instead.")]。
- 同一版本中新增推荐 API。
- 在下一个主版本(v2.0+)中移除,且需给用户迁移时间。
API 审批测试
通过自动化 API 表面测试防止意外破坏性变更。使用 PublicApiGenerator 与 Verify.Xunit,生成公共 API 快照并写入 .verified.txt 文件。任何 API 变更都会导致测试失败,审查者必须显式批准变更。PR 审查流程中,审查者可在 diff 中看到确切的 API 表面变化,破坏性变更立即可见。
线格式兼容
分布式系统中,序列化数据必须跨版本可读。要求同时满足:向后兼容(旧写入方 → 新读取方)和向前兼容(新写入方 → 旧读取方),二者都是零停机滚动升级所必需的。
安全演进线格式分三阶段:
- 添加读取侧支持(可选启用):先部署能读取新旧格式的读取方。
- 启用写入侧(可选关闭,下一个小版本):通过配置开启新格式,默认关闭。
- 设为默认(未来版本):在安装基础吸收读取侧代码后。
序列化格式选择上,优先基于 schema 的格式而非基于反射的格式:Protocol Buffers(优秀,显式字段编号)、MessagePack(良好,配合契约)、System.Text.Json(良好,配合源生成)、Newtonsoft.Json(较差,载荷中含类型名)、BinaryFormatter(极差,永不使用)。
封装模式
- 内部 API:用 [InternalApi] 特性或 .Internal 命名空间标记非公共 API,并明确文档说明这些类型可能在任何版本间变化而不另行通知。
- 密封类:对不设计用于继承的类使用 sealed,避免用户意外继承而阻碍变更。
- 接口隔离:使用小而专注的接口(如 IOrderReader、IOrderWriter),避免单体接口(新增方法会破坏所有实现)。
版本策略
语义化版本(实用):补丁版本(1.0.x)允许缺陷修复和安全补丁;次版本(1.x.0)允许新功能、弃用、移除过时项;主版本(x.0.0)允许破坏性变更和旧 API 移除。关键原则:不制造意外破坏,即使主版本也应提前公告和规划;扩展随时可发布;移除前先弃用,[Obsolete] 至少保留一个次版本;沟通时间线,让用户能规划升级。
切斯特顿栅栏
在移除或更改某物前,先理解它为何存在。假设每个公共 API 都被某人使用。若要更改:在 GitHub 上征求意见、记录迁移路径、提供弃用期、在计划版本中发布。
PR 审查清单
审查触及公共 API 的 PR 时检查:无移除的公共成员(改用 [Obsolete]);无更改的签名(改为新增重载);无新增必需参数(使用默认值);API 审批测试已更新(.verified.txt 变更已审查);线格式变更为可选启用(先读侧);破坏性变更已记录(发布说明、迁移指南)。
反模式
- 伪装成修复的破坏性变更:如把同步方法“修复”为异步,破坏所有调用方。正确做法是新增方法并弃用旧方法。
- 静默行为变更:更改默认值会破坏依赖旧行为的用户。正确做法是新增参数并保留原默认值。
- 多态序列化:避免在线格式中写入类型名(如 $type),重命名类即导致线格式破坏;优先使用显式判别符(如 type: order)。
适用场景
设计 NuGet 包或类库的公共 API、修改现有公共 API、规划分布式系统的线格式变更、实现版本策略、审查破坏性变更的 PR。
限制
该技能聚焦 .NET 生态(NuGet、程序集、C# 示例),其他语言或平台的具体做法暂未提供。