向後兼容性
什麼是向後兼容性
向後兼容性(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讀取器,自動檢查並進行有意識的刪除。這樣你就可以快速開發平臺,而不讓客戶陷入「不可見」的變化之中。