Compatibilidade invertida
O que é compatibilidade invertida
Compatibilidade invertida (backward compatibility) - propriedade do sistema de aceitar e processar corretamente clientes/consumidores antigos quando o sistema for atualizado. Mais simples: você está lançando uma nova versão do serviço/evento e as integrações já existentes continuam a funcionar sem alterações.
A chave é não quebrar o acordo. Qualquer evolução é adicionar e não remodelar o que já foi lançado.
Princípios básicos
1. Additive-first
Novos campos/métodos/eventos são adicionados opcionalmente. Nada existente é removido ou alterado.
2. Contrato mínimo de garantia (MGC)
Defina o núcleo - conjunto de campos/operações sem os quais o cenário não faz sentido. O núcleo está estável. Tudo o resto são extensões.
3. Tolerant reader
Os clientes ignoram campos desconhecidos e processam corretamente os novos valores enum (fallback).
4. Política de versões
As alterações quebrantes são apenas através da linha maior ('/v2 ',' payments. v2`, `event. v2`). Os menores são aditivos.
5. Observabilidade - parte do contrato
Os logs/trens e métricas mostram a versão do cliente, o formato, as bandeiras capability. Isso permite controlar a migração.
Alterações seguras vs perigosas
Normalmente seguro (BC-OK)
Adição de campos opcionais (JSON/Avro/Protobuf 'optional '/' nullable').
Adicione novos endpoint/métodos/eventos.
Extensão enum com valores adicionais (para tolerant reader).
Enfraquecimento da validação (aumento do máximo, adição de formatos alternativos).
Adicione cabeçalhos/metadados que não afetam o significado.
Perigosos (Breaking)
Remova ou renomeia campos, altere o tipo ou obrigatoriedade dos campos existentes.
Alterar semântica de status/código de erro.
Reutilizar marcas protobuf sob outros campos.
Alterar a chave de particionamento do evento (quebra a ordem do aparelho).
Apertamento de SLA/temporizações, o que faz os clientes antigos caírem.
Por estilos de interação
REST/HTTP + JSON
Adutora: os novos campos são 'optional', e o servidor não os exige dos clientes antigos.
Versões: major - No caminho ('/v2 ') ou no mediatipe; menor - através de extensões e '? inclusive = '/'? fields ='.
Erros: formato único; não alterar códigos/semântica sem major.
ETAG/If-Match: para updates seguros sem corridas.
Idempotidade: 'Idempotency-Key' para POST - os clientes antigos não «dobram» o efeito em retrações.
gRPC / Protobuf
As marcas de formatação estão inalteradas. As marcas de formatação remotas não serão reutilizadas.
Novos campos - 'optional '/' repeated'; os valores padrão são processados corretamente pelo código antigo.
Streaming: não altere a ordem/obrigatoriedade das mensagens dentro do menor.
Erros - conjunto estável de estatais; nova semântica → novo método/serviço ('.v2').
Event-driven (Kafka/NATS/Pulsar) + Avro/JSON/Proto
Nome: 'domain. action. v{major}`.
Core vs Enriched: núcleo estável; enriquecimento - tipos/tópicos individuais ('.enriched').
Modo de compatibilidade de esquema: mais frequente BACKWARD; A CI bloqueia alterações incompatíveis.
Particionamento: chave (por exemplo, 'payment _ id') - parte do contrato; mudá-lo - breaking.
GraphQL
Adicionar campos/tipos - OK; remover/renomear - através de «@ deprecated» e da janela de migração.
Não aumente «nullable → não-nullable» sem o maior.
Controle complexity/depth - Alteração de limites = alteração de contrato.
Pattern que ajudam a salvar o BC
Modelo de pirâmide inversa: estabilize o núcleo, expanda opcionalmente.
Capability negotion: o cliente informa as funcionalidades suportadas ('X-Capabilities '/handshake) e o servidor é ajustado.
Dual-run/dual-emit: mantenha «v1» e «v2» ao mesmo tempo durante a migração.
Adaptadores: proxy/gateway transferem solicitações 'v1↔v2' para clientes 'pesados'.
Expand-and-contract (para BB): Adicione um novo primeiro, comece a escrever/ler, e depois remova o antigo.
Governance e processo
1. Catálogo de contratos (registro de esquemas): uma única fonte de verdade com políticas de compatibilidade.
2. Liners e cheques diff em CI/CD: OpenAPI-diff, Buf-breaking, verificação de compatibilidade Avro/JSON Schema.
3. CDC/Consumer-Driven Contracts: O provedor é verificado para os contratos reais dos consumidores.
4. Golden sample: consultas de referência/respostas/eventos para regresso.
5. Mudar Management: RFC/ADR em breaking, planos sunset, comunicação.
Deprekate e remoção de versões antigas
Marcar antiquado («@ deprecated», descrições, títulos «Deprecation», «Sunset»).
Janela de migração: data pré-anunciada, estande de teste, exemplos de código.
Telemetria de uso: Quem mais está em 'v1'? segmentar as métricas/logs na versão.
Dual-run até zero tráfego, depois remoção.
Observabilidade e métricas operacionais
Porcentagem de solicitações/mensagens por versão.
Número de erros/temporizações em clientes antigos após o lançamento.
Proporção de payload incompatíveis (validação de esquema em parafuso/filtros estrim).
Migração dos consumidores (quantos mais ouvem 'v1').
Testes de compatibilidade invertida
Schema-diff: fail при remove/rename/type-change.
SDK/clientes antigos perseguem a nova implementação.
E2E canário: parte do tráfego antigo para a nova versão, comparação p95/p99, códigos, retrações.
Réplicas de eventos: projeções são reunidas por uma nova lógica do antigo loga sem discrepâncias.
Fault-inhation: atrasos/respostas parciais - clientes antigos não caem.
Exemplos
REST (aditivo)
Foi:json
{ "id": "p1", "status": "authorized" }
Tornou-se:
json
{ "id": "p1", "status": "authorized", "risk_score": 0. 12 }
Clientes antigos ignorando 'risk _ score' continuam a funcionar.
Protobuf (tags)
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
Eventos (núcleo + enriquecimento)
`payment. authorized. v1 é o núcleo (mínimo de factos).
`payment. enriched. v1 '- peças; os consumidores do núcleo são independentes do enriquecimento.
Antipattern
Swagger-wash: atualizou o esquema, mas o serviço age de forma antiga (ou vice-versa).
Quebra oculta: altera o significado do campo/status sem versão.
Reutilização de marcas protobuf: corrupção de dados «silenciosa».
Clientes duros: caem em campos estranhos/enum; não há tolerant reader.
Mega-endpoint: um-em-um - qualquer alteração torna-se potencial quebra-quebra.
Folha de cheque antes do lançamento
- As alterações são aditivas; o núcleo (MGC) não foi tocado.
- Os linters/cheques diff foram ultrapassados; não há bandeiras de breaking.
- SDK cliente atualizado (ou não necessário para extensão aditiva).
- Ativado tolerant reader nos clientes; enum-fallback testado.
- As métricas/logs contêm a versão e as bandeiras capability.
- Para quebra potencial há '/v2 ', dual-run e plano sunset.
- Documentação/exemplos foram atualizados, há conjuntos golden.
FAQ
Backward vs forward - Qual é a diferença?
Backward - novos servidores trabalham com clientes antigos. Forward - novos clientes funcionam corretamente com servidores antigos (por meio de tolerant reader e default cuidadoso). A volta completa é full compatibility.
É sempre necessário fazer '/v2 'para grandes mudanças?
Sim, se os invariantes/tipos/chaves/semântica quebrarem. Senão, mantenha a linha e evolua aditivamente.
Como está o enum?
Adicione novos valores sem alterar o significado dos antigos. Os clientes devem ter fallback com um valor desconhecido.
E se já tivermos partido?
Retrocesso, hot-fix adaptador, lançamento 'v2' com dual-run, comunicação e hyde migratório.
Resultado
A compatibilidade invertida é uma disciplina de evolução: estabilize o núcleo, amplie o adutor, implemente o tolerant reader, automatize as verificações e conduza um deprekate consciente. Assim você pode rapidamente desenvolver a plataforma sem deixar os clientes sob os escombros de mudanças «invisíveis».