API合同兼容性
为什么需要合同兼容性
合同兼容性是API在不破坏现有集成的情况下发展的能力。在不断发展的系统中,API更频繁地更改客户端代码。相容性允许迭代生产菲奇,而无需安排"大移动"。
关键思想:合同-初级,更改遵循兼容性规则并自动验证。
基本概念
合同-正式界面规格:资源/方法/事件,数据电路,错误代码,限制,SLA,安全要求。
提供程序(provider)-API的所有者。消费者(消费者)-客户端/集成。
- Backward:新供应商与老消费者合作。
- 前进:老供应商与新消费者合作(通常通过"宽容的读者"来实现)。
- 满:后卫和前锋(最强选项)都遵循。
- 可加性-添加可选元素而不切断现有元素。
考试政策
语义版本(建议):- MAJOR是断开的更改(仅在发布新的API行时:'/v2',"服务"。v2`).
- MINOR-加法更改(新的可选字段/方法)。
- PATCH-不更改合同的修复程序。
- Deprecation Policy:声明遗留项目,支持窗口(日落),标题/元数据中的警告,删除计划。
安全vs危险变化
安全(通常是反向兼容)
在JSON/Protobuf/Avro中添加可选字段。
添加新的端点/方法/事件。
如果消费者容忍未知值,则以新值扩展enum。
提高限制(例如"maxItems")而无需收紧最小值。
添加带有正确默认值的nullable。
更改描述/示例文本。
危险(中断互操作性)
重命名/删除字段,更改字段的类型或约束。
状态代码/错误语义的变化(例如"200"变为"204"或"404")。
更改标识符格式(UUID → int)。
收紧验证(严格为最小值/模式)而无需版本。
gRPC流/事件中的顺序和结构变化。
将Protobuf中的标记编号重新用于新字段。
互操作性样式
REST/HTTP + JSON Schema
可加性:将新字段标记为"optional"/"nullable"。
客户端的Tolerant Reader:忽略未知字段;不要依靠秩序。
转化: 专业-在路上('/v2')或介质('application/vnd。example.v2+json`).
ETag/If-Match:用于安全升级而无需比赛。
错误:单一格式("type","code","title","detail","trace_id"),不要在没有专业的情况下更改"code"的含义。
分离:游标优于offset;添加"next_cursor"字段,不要改变现有字段的含义。
gRPC / Protobuf
标签编号不变。已删除的标签不会重新使用。
新字段是"optional"/"repeated",服务器上有合理的默认值。
不要在streaming-RPC中更改消息的顺序和约束。
错误状态是稳定的("INVALID_ARGUMENT","FAILED_PRECONDITION"等);新语义→方法/服务的新版本。
Event-driven (Kafka/NATS/Pulsar) + Avro/JSON Schema
事件命名: "域。action.v{major}`.
新字段-可选;释放内核和富集(".enriched")。
电路寄存器:主题/事件的兼容性规则(BACKWARD/FORWARD/FULL)。
当消费者侧的tolerant reader时,enum扩展是允许的。
更改聚合的部分/顺序键=断开更改。
GraphQL
添加字段/类型-安全;删除/重命名-仅通过@deprecated和迁移窗口。
不要在没有专业的情况下改变类型/不可阻挡。
控制complexity/depth-限制是合同的一部分。
可持续进化的模式
Additive-first:在不中断的情况下进行扩展。
功能性negotation:客户端报告他们支持(标题/选项/安排),服务器进行调整。
合同边界:固定MGC(最低保修合同)和分离扩展(反金字塔模型)。
默认收费:客户忽略多余,并正确处理未知的enum值(fallback)。
Dual-write/Dual-emit:在主要更改中,并行发布"v1"和"v2"一段时间。
Sunset headers/Events:提前通知您已删除版本。
政府和自动化
API linters:- OpenAPI/Spectral:命名、分页、错误代码、字段格式。
- Buf/Protobuf:禁止使用标签和数据包符号。
- AsyncAPI/Schema Registry:CI级别的电路兼容性。
- 合同目录(SSOT):具有诽谤历史的集中电路/版本注册表。
- API Guild:接受规则,模板和更改评论的行会/委员会。
- 更改管理:RFC/ADR,发布注释,迁移粘合剂。
兼容性测试
CI中的Schema-diff:阻止断裂的孢子变化(OpenAPI-diff,Buf breaking,SR compatibility)。
Consumer-Driven Contracts (CDC): Pact/相似-验证供应商与特定消费者的合同。
Golden Samples:参考查询/响应和倒退事件。
E2E金丝雀:分成流量份额/单独的控制组。
Chaos/Latency: Taymout/Retraes检查-Latency-SLO更改被视为合同更改。
迁移和删除
1.声明删除:标记项目,指定日落期限和替代方桉。
2.支持互操作性时期:双write/dual-emit、网桥、适配器。
3.收集遥测:还有谁在使用旧的?
4.通讯:邮件、发行说明、测试台。
5.卸载:窗口到期后,使用固定版本卸载。
更改示例
REST
有:json
{ "id":"p1", "status":"authorized" }
已成为(加法,安全):
json
{ "id":"p1", "status":"authorized", "risk_score": 0. 12 }
忽略未知字段的客户不会崩溃。
Protobuf
proto message Payment {
string id = 1;
string status = 2; // don't change tag numbers optional double risk_score = 3; // additive
}
Event
`payment.authorized.v1'(内核)+'payment。enriched.v1'(富集)。关键路径的消费者读取核心,不依赖浓缩。
反模式
Swagger-wash:规范正式存在,但服务行为与它不一致。
破折号:在没有新版本和迁移窗口的情况下更改类型/状态/格式。
原始CDC事件作为公共合同:DB方案泄漏,无法进化。
硬客户端:在未知字段/值下下降;没有tolerant阅读器。
重新使用protobuf标签:无声的数据腐败。
潜伏为"非合同":p95出乎意料地延长了-消费者打破了时间。
兼容性检查表(在默奇之前)
- 更改是可加的(或已准备好主要版本)。
- Linters/diff支票已通过,兼容性规则为绿色。
- 错误/代码/状态没有改变语义。
- Enum在不禁止旧值的情况下进行了扩展;客户是tolerant。
- MGC边界不变。
- 更新示例/文档/SDK。
- 对于专业-双写/双表情计划,日落日期,comm计划。
- 测试CDC/Golden/E2E通过。
FAQ
背面与前面的兼容性有何不同?
Backward-新服务器不会打破旧客户端。Forward-新客户端不会在较旧的服务器上破裂(通过tolerant reader和整洁的默认)。
什么时候做"/v2"?
当不变式/语义发生变化时,删除字段/方法,需要新的安全模型-运行新线更简单,更诚实。
没有Schema Registry/Linters可以生活吗?
从理论上讲-是的,实际上-这些是频繁的倒退和"隐藏"的断裂。自动化正在得到回报。
Enum可以扩展吗?
是的,如果客户正确处理未知值(fallback/ignore)。否则-大调。
底线
合同兼容性是规则+纪律+自动化。加性设计,折断性变化,应用托勒南特读取器,自动检查诽谤和CDC,计划丢弃。因此,API将能够快速发展,集成将保持稳定。