Versioning API e compatibilità contrattuale
TL; DR
La compatibilità è disciplina, non fortuna. Mantenere un chiaro criterio di versione (SemVer), matematica delle modifiche (che «rompe», che no), test contrattuali, registri di schemi e procedure sunset. Per il denaro e la compilazione è un rigido, per le aggregazioni UI è un evolutivo con «@ deprecated». Sempre: traffico canario, interoperabilità inversa, un ciclo di rilascio, gate migratorie, telemetria per l'uso dei campi.
1) Concetti e obiettivi di base
Backwards-compatibile (BC): il nuovo server è adatto ai vecchi client.
Forwards-compatibile (FC): il vecchio server è adatto a nuovi client (limitatamente).
Compatibilità Wire: il formato sul filo non si rompe (particolarmente importante per il gRPC/Protobuf).
SemVer: `MAJOR. MINOR. PATCH - Rompe il contratto, promuove MAJOR.
Obiettivo: ridurre al minimo i cambiamenti e garantire finestre di migrazione prevedibili.
2) Matrice di modifiche: cosa è possibile e cosa non può
3) Criteri per stili di API diversi
3. 1 REST
Versione in URI ('/v1/... ') o dominio (' api-v1 '). L'intestazione è solo per casi interni.
Aggiungi, non rimuovi: nuovi campi - ok, vecchi - contrassegna comè deprecated "nel diagramma e lascia almeno un ciclo.
Stato/errore: non cambiare i codici e la struttura dì errore ". code/error. message/error. details`.
L'idempotenza è immutata: non trasformare il «POST» sicuro con «Idempotency-Key» in una sfida «comportamentalmente diversa».
3. 2 gRPC / Protobuf
I numeri di campo sono sacri: non usare i numeri eliminati, contrassegnare come «riserved».
Solo l'aggiunta di nuovi campi optional/riduce i campi; «rigidi obbligatori» - attraverso la validazione, non «richired».
Pacchetti versioni: 'payments. v1`, `payments. v2`.
Interoperabilità dei servizi: i nuovi RPC → un nuovo metodo; I vecchi comportamenti non cambiano.
proto message Payout {
reserved 4 ;//field deleted, number reserved string id = 1;
string currency = 2;
int64 amount_minor = 3;
// v2: optional string comment = 5;
}
3. 3 GraphQL
Evoluzione senza v2: aggiungere campi/tipi; rimuovendo - «@ deprecated (reason)» con l'annuncio della finestra.
Personed Queries: per i clienti pubblici, utilizzare l'elenco delle richieste in bianco per controllare la compatibilità.
Field-level e telemetria: scoprite quali campi vengono utilizzati prima di eliminare.
graphql type Payout {
id: ID!
amountMinor: Long!
currency: String!
comment: String @deprecated(reason: "Use note")
note: String
}
3. 4 Webhook
Versione nel percorso ('/webhooks/v1/payments ') e busta fissa evento (' event _ id ',' type ',' ts ',' data ').
Le firme/NMAS devono essere mantenute invariate; I nuovi algoritmi sono come un'opzione con una bandiera.
Estensioni - solo con i nuovi campi data e headers - senza eliminare quelli più vecchi.
4) API Gateway e routing versione
Riles-based routing: prefisso «/v1 », titolo« X-Api-Variante », dominio.
Shadow/Canary - Riflettere parte del traffico di produzione sulla nuova versione in ombra, confrontare le risposte.
Rate/Quotas per-versione - Protegge i vecchi client durante la migrazione.
- 'Sunset:
' - Data di spegnimento versione - «Deprecation: true» - La versione è obsoleta
- `Link:
; rel = «deprecation» - su changelog/hyde migrazione
nginx location ~ ^/v2/ {
proxy_pass http://api_v2;
}
location ~ ^/v1/ {
add_header Deprecation "true";
add_header Sunset "Thu, 01 May 2026 00:00:00 GMT";
proxy_pass http://api_v1;
}
5) Registri di schemi e contratti
OpenAPI / JSON Schema для REST; Protobuf descriptors для gRPC; SDL registry для GraphQL.
Controlli CI: linters + «breaking-changes check» a PR.
Test consumer-Driven Contracts (CDC) - Protezione da rotture non visibili.
Changelog: machine-readable (ad esempio, 'CHANGELOG. md' + note di rilascio nel registro).
6) Evoluzione dei campi: regole pratiche
ID/chiavi - Non modificare il formato (UUID↔int) senza il nuovo campo «_ v2» e il periodo di transizione.
Tempo/valuta: tenete UTC ISO-8601/epoch e amount _ minore + currency; non ridimensionare (centesimi/centesimi).
Enum: aggiungi i valori - ok; Non cambiare il significato dei vecchi. Per REST- Restituisci i valori string, non ints.
Paginazione: cursor-based più stabile; Non cambiare la semantica del cursore.
7) Depricaggio e Sunset -
1. Annuncio (T-90/60): changelog, distribuzione ai partner, intestazioni «Deprecation/Sunset».
2. Periodo duplicato: V1 e V2 funzionano in parallelo V1 è dotato di avvisi nelle risposte o nei fogli.
3. Telemetria d'uso: chi altro chiama V1? Contatti a punti.
4. Congelamento V1: solo bagfix/senza fiocco.
5. Disattiva (Sunset): 410 Gone o una pagina di blocco con istruzioni di migrazione.
8) Rilasci senza dolore: strategie di rilascio
Blue/Green o Weighted routing: 1-5-25-50-100% del traffico.
Compatibility window: meno di 1-2 rilasci minori, più di 6-12 mesi per API esterne.
Feature Flags - Per includere nuovi campi/rami di logica senza modificare la versione.
Frammentazione read/Write - Prima aggiungi supporto per leggere un nuovo campo, poi inizia a scriverlo.
9) Test di compatibilità (pacchetto di pratiche)
Test Golden per le risposte delle versioni precedenti.
Test diagrammi differenti - Proibizione breaking in CI.
Replay di produzione su staging per V2 (shadow).
Script Back/Forward: nuovo client sul vecchio server e viceversa (dove FC è valido).
Test contrattuali Web - Verifica firma, formato, tempo.
10) Metriche e SLO del processo di versioning
% dei clienti nell'ultimo MINOR (obiettivo dell '80% prima di Sunset).
Errori di compatibilità/indisponibilità al rilascio (obiettivo 0).
Percentuale di chiamate di versione obsoleta (decrescente a Sunset).
Tempo di migrazione del cliente (media/p95).
Latency/regolution delta tra le versioni (non peggio di quella di base).
11) Esempi di manufatti
OpenAPI (sezione, deprecazione del campo):yaml components:
schemas:
Payout:
type: object properties:
id: { type: string, format: uuid }
amount_minor: { type: integer }
currency: { type: string }
comment:
type: string deprecated: true description: "Use note"
note: { type: string }
Protobuf (riserved e v2 pacchetti):
proto syntax = "proto3";
package payouts. v1;
message Payout { reserved 5; string id=1; int64 amount_minor=2; string currency=3; }
GraphQL (deprimente):
graphql type Query { payout(id: ID!): Payout }
12) Versioning dei canali adiacenti
SDK/CLI: SemVer + dipendenza da API, compatibilità stabilita in README.
Eventi/striam (WS/Kafka): versione dì invelope ". version`; nuovi attributi - opzionali Il nonno e il funzionano allo stesso modo tra le versioni.
Report/CSV: versione in nome file/cappello; Aggiungere colonne a destra non cambiare l'ordine/i tipi.
13) Governance e ruoli
Titolare del contratto (domain owner), API Steward (Regole e Linter), Release Manager (Sunset/Comunicazioni).
Processo RFC per i cambiamenti di breaking: giustificazione aziendale, piano di migrazione, artefatti, date.
Un unico catalogo API in cui sono visibili schemi, versioni, calendario Sunset, contatto.
14) Anti-pattern
Rompe silenziose: cambia stato/errore/tipo di campo senza versione.
Riutilizzo dei numeri protobuf - Distrugge repliche e vecchi clienti.
GraphQL senza telemetria per l'uso dei campi - rimozione del tatto.
Il v2 globale è solo una megamigrazione, invece di un'evoluzione puntuale.
La versione query per API pubblica è uno schema poco chiaro e vulnerabile.
Niente migrazioni e niente esempi. I soci vengono bocciati e le scadenze saltano.
15) Check-list rilascio nuova versione
- È stato aggiornato lo schema (OpenAPI/Protobuf/SDL), sono stati superati i linter e i breaking-checks.
- Sono stati aggiunti i test di integrazione e contrattazione (CDC).
- SDK/SDK/Esempio di codice/Migration Hyde e Changelog.
- Abilitato'Deprecation/Sunset '(versione precedente) + pagina «How to migrate».
- Canary/Shadow piano, alert e dashboard di confronto delle metriche.
- La compatibilità inversa è stata mantenuta per un periodo concordato.
- Il piano di ripristino (rollback) e la matrice di rischio sono coerenti.
Riepilogo
Un'API stabile è un processo, non una volta per tutte. Seguire le regole: evoluzione + add-only + minuscolo diagramma + test contrattuali + procedure sunset. Separare gli stili (REST/gRPC/GraphQL) e i loro criteri, instradare le versioni su API Gateway, estrarre i canarini e misurare gli effetti. In questo modo si evitano le «sorprese distruttive», si accelera l'integrazione dei partner e si mantiene la prevedibilità per i domini critici e in denaro.