向后兼容性
什么是向后兼容性
向后兼容性(Backward compatibility)-系统在系统更新时接受和正确处理旧客户/消费者。更简单:您发布新版本的服务/事件,并且现有集成继续运行而不会发生变化。
关键:不要打破安排。任何演变-通过添加而不是重做已经发布。
基本原则
1.Additive-first
可选地添加新的字段/方法/事件。没有任何现有内容被删除或改变含义。
2.最低保修合同(MGC)
定义内核-一组脚本失去意义的字段/操作。核心是稳定的。其他一切都是扩展。
3.Tolerant reader
客户端忽略未知字段并正确处理新的enum值(fallback)。
4.版本策略
破裂的变化仅通过主要线("/v2","payments"。v2`, `event.v2`).次要是加性的。
5.可观察性-合同的一部分
客户端版本,格式,功能标志在逻辑/预告片和度量标准中可见。这使您可以管理迁移。
安全vs危险变化
通常是安全的(BC-OK)
添加可选字段(JSON/Avro/Protobuf "optional"/"nullable")。
添加新的残局/方法/事件。
Enum扩展附加值(在tolerant reader中)。
放松验证(增加最大值,添加替代格式)。
添加不影响含义的标题/元数据。
危险)
删除/重命名字段,更改现有字段的类型或约束。
更改状态/错误代码的语义。
将protobuf标签重新分配给其他字段。
更改事件分组键(破坏聚合的顺序)。
收紧SLA/taymauts,导致老客户开始下跌。
通过交互样式
REST/HTTP + JSON
可加性:新字段是"可选",服务器不需要旧客户端。
版本:专业-途中("/v2")或中介;minor-通过扩展名和'?include='/'?fields='。
错误:统一格式;不要在没有专业的情况下更改代码/语义。
ETag/If-Match:用于安全升级而无需比赛。
相似性:POST的"Idempotency-Key"-老客户在回避时"加倍"效果。
gRPC / Protobuf
标签不变。已删除的标签不会重新使用。
新字段是"optional"/"repeated";默认值由旧代码正确处理。
流媒体:不要改变次要消息的顺序/强制性。
错误-一组稳定的状态;新语义→新方法/服务(".v2")。
Event-driven (Kafka/NATS/Pulsar) + Avro/JSON/Proto
命名: "域。action.v{major}`.
Core vs Enriched:核心稳定;丰富是不同的类型/主题(".enriched")。
电路兼容性模式:更常见的是BACKWARD;CI阻止不兼容的更改。
分期付款:钥匙(例如"payment_id")是合同的一部分;改变它-打破它。
GraphQL
添加字段/类型-确定;删除/重命名-通过"@deprecated"和迁移窗口。
不要在没有专业的情况下提升"不可→非不可"。
控制complexity/depth-更改限制=更改合同。
帮助保留BC的模式
反向金字塔模型:稳定核心,可选地扩展。
功能性negotation:客户端报告支持的功能("X-Capabilities "/handshake),服务器进行调整。
Dual-run/dual-emit:在迁移过程中同时保持"v1"和"v2"。
适配器:代理/网关为"重型"客户端翻译"v1↔v2"请求。
Expand-and-ontract(用于DB):首先添加新内容,开始写作/阅读,然后再删除旧内容。
政府和流程
1.合同目录(图表):具有兼容性策略的单一真相来源。
2.CI/CD 中的Linters和diff支票:OpenAPI diff,Buf-breaking,Avro/JSON Schema兼容性检查。
3.CDC/Cusumer-Driven Contracts:对提供商进行真实消费者合同审查。
4.Golden samples:参考查询/回答/倒退事件。
5.变革管理:RFC/ADR突破,日落计划,沟通。
Deprekate和删除旧版本
标记过时("@deprecated",描述,标题"Deprecation","Sunset")。
迁移窗口:预先声明的日期、测试台、代码示例。
使用遥测:"v1"上还有谁?按版本细分度量/逻辑。
双重运行至零流量,然后删除。
可观察性和操作指标
按版本分列的请求/消息百分比。
发布后旧客户端的错误/定时比例。
不兼容的负载比例(通过网关/流滤波器上的电路验证)。
消费者迁移差(还有多少人听"v1")。
向后兼容性测试
Schema-diff: fail при remove/rename/type-change.
合同测试:旧的SDK/客户与新实现竞争。
E2E金丝雀:新版本旧流量的一部分,p95/p99比较,代码,中继。
事件反射:投影是由旧逻辑中的新逻辑收集的,没有差异。
故障注入:延迟/部分响应-老客户不会掉落。
示例
REST(加法)
有:json
{ "id": "p1", "status": "authorized" }
成为:
json
{ "id": "p1", "status": "authorized", "risk_score": 0. 12 }
旧客户端忽略了"risk_score",继续运行。
Protobuf(标签)
proto message Payment {
string id = 1;
string status = 2;
optional double risk_score = 3 ;//new field, safe
}
//Tags 1 and 2 cannot be changed/deleted without v2
事件(核心+富集)
`payment.authorized.v1'是核心(事实的最小值)。
`payment.enriched.v1'-细节;核心消费者不依赖浓缩。
反模式
Swagger-wash:更新了电路,但服务表现为旧式(反之亦然)。
隐藏切片:在没有版本的情况下更改字段/状态的含义。
重新使用protobuf标签:"安静"的数据腐败。
强硬的客户:落在陌生的领域/enum;没有tolerant reader。
Mega-endpoint:一对一-任何更改都成为潜在的切片。
发行前的支票清单
- 变化是加法的;内核(MGC)未被感动。
- Lintera/diff支票已通过;没有突破标志。
- 客户端SDK已更新(或添加扩展不需要)。
- 包括客户的tolerant reader;enum-fallback已验证。
- 度量/逻辑包含版本和能力标志。
- 对于潜在的碎片,有"/v2",双跑和日落计划。
- 更新的文档/示例,有金色套件。
FAQ
Backward vs forward-有什么区别?
Backward-新服务器与旧客户端一起使用。Forward-新客户机能够正确地处理旧服务器(通过托勒式阅读器和整洁的默认)。完整圆圈是完全兼容的。
是否总是需要做"/v2"以进行重大更改?
是,如果不变式/类型/键/语义断裂。否则-保持血统并以加法演化。
如何与enum?
在不改变旧含义的情况下添加新含义。客户必须以未知值后退。
如果他们已经"打破"了怎么办?
回滚,热修复适配器,带双跑,通信和迁移海德的"v2"版本。
底线
向后兼容性是一门进化学科:稳定内核,加法扩展,引入tolerant读取器,自动检查并进行有意识的删除。这样你就可以快速开发平台,而不让客户陷入"不可见"的变化之中。