Compatibilità inversa
Cos'è la compatibilità inversa
La compatibilità inversa (backward compatibility) è una proprietà del sistema che accetta e gestisce correttamente i vecchi clienti/consumatori quando il sistema viene aggiornato. Più semplice: si produce una nuova versione del servizio/eventi e le integrazioni esistenti continuano a funzionare senza modifiche.
La chiave è non rompere gli accordi. Ogni evoluzione è attraverso l'aggiunta, non la rimodulazione.
Principi di base
1. Additive-first
I nuovi campi/metodi/eventi vengono aggiunti opzionalmente. Niente di esistente viene rimosso o cambiato il significato.
2. Contratto minimo di garanzia (MGC)
Definire il kernel - un insieme di campi/operazioni senza i quali lo script perde senso. Il nucleo è stabile. Tutto il resto è dilatazione.
3. Tolerant reader
I client ignorano i campi sconosciuti e gestiscono correttamente i nuovi valori enum (fallback).
4. Criteri di versione
Le modifiche spezzanti sono solo attraverso la linea maggiore ('/v2 ',' payments '. v2`, `event. v2`). I minori sono adduttori.
5. Osservabilità - parte del contratto
I logs/trailer e le metriche mostrano la versione del client, il formato, i flag capability. Questo consente di gestire la migrazione.
Modifiche sicure vs pericolose
Generalmente sicuro (BC-OK)
Aggiunge campi facoltativi (JSON/Avro/Protobuf'optional '/' nullable ').
Aggiunge nuovi endpoint/metodi/eventi.
Estensione enum con valori aggiuntivi (con tolerant reader).
Indebolimento della validazione (aumento dei massimi, aggiunta di formati alternativi).
Aggiunge intestazioni o metadati che non influiscono sul significato.
Pericolosi (Breaking)
Rimuove o rinomina i campi, modifica il tipo o l'obbligatorietà dei campi esistenti.
Cambia la semantica degli stati/codici di errore.
Riutilizza i tag protobuf sotto altri campi.
Cambia la chiave di partizionamento dell'evento (rompe l'ordine dell'aggregazione).
Stretching SLA/timeout, che fa sì che i vecchi clienti inizino a calare.
Stile di interazione
REST/HTTP + JSON
Additività: i nuovi campi sono «optional», il server non li richiede ai vecchi clienti.
Versioni: major - nel percorso ('/v2 ') o mediatip; minor - attraverso le estensioni e «? include = »/«? fields =».
Errore: formato unico; non modificare codici/semantici senza maggiore.
ETAG/If-Match per update sicuri senza corse.
Idempotence: «Idempotency-Key» per POST - I vecchi clienti non «raddoppiano» l'effetto sui retrai.
gRPC / Protobuf
I tag sono invariati. I tag eliminati non vengono riutilizzati.
I nuovi campi sono «optional »/« repeated»; i valori predefiniti vengono gestiti correttamente dal codice precedente.
Streaming - Non modificare l'ordine/l'obbligatorietà dei messaggi all'interno di minor.
Gli errori sono un set stabile di stati. la nuova semantica → un nuovo metodo/servizio ('.v2').
Event-driven (Kafka/NATS/Pulsar) + Avro/JSON/Proto
La denominazione è «domain». action. v{major}`.
Core vs Entriched: il nucleo è stabile; arricchimento - singoli tipi/argomenti ('.enriched').
Modalità di interoperabilità dello schema: più frequente di BACKWARD CI blocca le modifiche incompatibili.
Partizionamento: la chiave (ad esempio, «payment _ id») è una parte del contratto; cambiarlo - breaking.
GraphQL
Aggiungere campi/tipi - OK; rimuovendo/rinominando - attraverso @ deprecated e la finestra di migrazione.
Non alzare «nullable» senza maggiore.
Controlla complexity/depth - Modifica dei limiti = modifica del contratto.
Pattern che aiutano a salvare BC
Modello di piramide inversa - Stabilizzare il nucleo, espanderlo opzionalmente.
Capability negotion: il client comunica le funzionalità supportate ('X-Capabilities '/handshake') e il server viene configurato.
Dual-run/dual-emit: mantenete «v1» e «v2» contemporaneamente durante la migrazione.
Gli adattatori proxy/gateway trasferiscono le richieste dì v1↔v2 "per i clienti" pesanti ".
Expand-and-contract (per il database) - Prima aggiungerne uno nuovo, iniziare a scrivere/leggere, ma poi rimuovere il vecchio.
Governance e processo
1. Elenco dei contratti (registro degli schemi): un'unica fonte di verità con le regole di compatibilità.
2. I lintern e gli assegni diff in CI/CD: OpenAPI-diff, buf-breaking, verifica di compatibilità Avro/JSON Schema.
3. CDC/Consumer-Driven Contracts: il provider viene controllato per i contratti reali dei consumatori.
4. Golden sample: query/risposte/eventi di riferimento per la regressione.
5. Change management: RFC/ADR a breaking, piani sunset, comunicazione.
Deprecate e rimozione versioni precedenti
Contrassegna le descrizioni «@ deprecated», le intestazioni «Deprecation», «Sunset».
Finestra di migrazione: data preannunciata, stand di prova, esempi di codice.
Telemetria d'uso: chi altro è su v1? segmentare le metriche/fogli secondo la versione.
Dual-run a zero traffico, quindi rimozione.
Osservabilità e metriche operative
Percentuale di richieste/messaggi per versione.
Percentuale di errori/timeout dei vecchi clienti dopo il lancio.
Percentuale di payload incompatibili (convalida con schema su gateway/filtri stream).
Migrazione dei consumatori (quanti altri ascoltano «v1»).
Test di interoperabilità
Schema-diff: fail при remove/rename/type-change.
I vecchi SDK/client si oppongono alla nuova implementazione.
E2E canarino: parte del vecchio traffico per la nuova versione, confronto p95/p99, codici, retrai.
Repliche di eventi: le proiezioni vengono raccolte con una nuova logica da un vecchio cavo senza interruzioni.
Fault-injection: ritardi/risposte parziali - I vecchi clienti non cadono.
Esempi
REST (additivo)
C'era:json
{ "id": "p1", "status": "authorized" }
È diventato:
json
{ "id": "p1", "status": "authorized", "risk_score": 0. 12 }
I vecchi clienti ignorano risk _ score e continuano a funzionare.
Protobuf (tag)
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
Eventi (kernel + arricchimento)
`payment. authorized. v1 "- kernel (minimo fatti).
`payment. enriched. v1 '- parti; I consumatori del nucleo non dipendono dagli arricchimenti.
Antipattern
Swagger-wash - Aggiorna lo schema, ma il servizio si comporta in modo vecchio (o viceversa).
Astinenza nascosta: cambia il significato del campo/stato senza versione.
Riutilizzo dei tag protobuf: corruzione dei dati silenziosa.
Clienti rigidi: cadono in campi sconosciuti/enum; niente tolerant reader.
Mega-endpoint: uno-in-uno: qualsiasi modifica diventa un potenziale astinenza.
Foglio di assegno prima del rilascio
- Le modifiche sono additive; il kernel (MGC) non è toccato.
- Gli assegni Linter/diff sono stati completati; non ci sono bandiere breaking.
- SDK client aggiornati (o non necessari per l'estensione additiva).
- Attivato tolerant reader presso i clienti; enum-fallback verificato.
- Le metriche/fogli contengono la versione e i flag capability.
- Per l'astinenza potenziale ci sono «/v2 », dual-run e piano sunset.
- Documentazione/esempi aggiornati, ci sono set golden.
FAQ
Backward vs forward - qual è la differenza?
Backward - I nuovi server funzionano con i clienti più vecchi. Forward - I nuovi client lavorano correttamente con i vecchi server (con tolerant reader e default accurati). Il cerchio completo è full compatibility.
È necessario fare sempre «/v2 »per grandi cambiamenti?
Sì, se si rompono invarianti/tipi/chiavi/semantiche. Altrimenti, mantenete la linea e evolvetevi in modo additivo.
Come si fa con l'enum?
Aggiungi nuovi valori senza cambiare il significato delle vecchie. I client devono avere fallback a un valore sconosciuto.
E se avessimo già rotto?
Ritorno, hot-fix adattatore, rilascio dual-run, comunicazione e migrazione.
Totale
La compatibilità inversa è una disciplina dell'evoluzione: stabilizzare il nucleo, espandere l'additivo, implementare il tolerant reader, automatizzare i controlli e condurre un deprecato consapevole. In questo modo potrete sviluppare rapidamente la piattaforma senza lasciare i clienti sotto le macerie dei cambiamenti «invisibili».