Pular para o conteudo principal

Contratos entre serviços e compatibilidade retroativa

Como evoluir integrações internas sem tratar consumidor de outro time como se fosse detalhe do seu deploy.

Andrews Ribeiro

Andrews Ribeiro

Founder & Engineer

O problema

Tem time que trata integração interna como se fosse conversa privada.

Algo como:

“Se eu mudar aqui e quebrar, o outro time ajusta rápido.”

No papel parece eficiente.

Na prática, isso cria o clássico sistema em que todo deploy tem chance de vazar problema para outro serviço.

O motivo e simples:

  • um serviço publica comportamento
  • outro serviço passa a depender dele

Nesse momento nasceu um contrato.

Mesmo que ninguém tenha dado esse nome.

Modelo mental

Contrato entre serviços e o acordo real entre produtor e consumidor.

Esse acordo inclui:

  • campos
  • tipos
  • obrigatoriedade
  • semântica
  • status codes
  • ordem ou formato de eventos
  • regras de erro

Compatibilidade retroativa significa conseguir evoluir esse acordo sem quebrar quem ainda depende da forma anterior.

Em linguagem simples:

o serviço novo continua entendivel para o consumidor antigo.

Quebrando o problema

Schema e só uma parte

Muita conversa sobre contrato para no schema cedo demais.

Mas contrato não e só isto:

{
  "id": "123",
  "status": "paid"
}

Também e expectativa como:

  • status sempre vem?
  • pode vir valor novo?
  • 404 significa não existe ou não pode ver?
  • evento pode chegar duplicado?

Se a resposta para isso não está clara, o contrato continua fraco mesmo com schema “bonito”.

O que costuma ser compativel

Mudancas que frequentemente sao seguras:

  • adicionar campo opcional
  • aceitar valor novo sem invalidar os antigos
  • adicionar endpoint ou evento novo
  • enriquecer resposta sem mudar significado anterior

Elas tendem a funcionar porque consumidor antigo continua entendendo o que já sabia.

O que costuma quebrar

Mudancas perigosas:

  • remover campo usado por consumidor
  • renomear campo
  • trocar tipo
  • tornar obrigatorio algo que antes não era
  • mudar significado de valor existente
  • mudar comportamento de erro sem transição

O problema quase nunca e a linha de código.

E a expectativa quebrada do outro lado.

Produtor forte pensa em convivencia

Quando uma mudança importante vem, o produtor maduro pensa em transição:

  • adicionar novo campo ou comportamento
  • manter o antigo por um tempo
  • comunicar depreciação
  • medir quem ainda depende
  • remover só depois da migração

Isso vale para API HTTP, eventos e integrações por fila.

Consumidor forte também se protege

Compatibilidade não e responsabilidade só do produtor.

Consumidor forte evita assumir demais:

  • ignora campos extras
  • tolera ausencia de ordem acidental quando o contrato não promete ordem
  • não acopla parse a detalhe inutil
  • trata valor desconhecido com degradação segura quando faz sentido

Se o consumidor quebra porque apareceu um campo extra, ele também esta fragil.

Exemplo simples

Imagine um serviço de pedidos que responde:

{
  "id": "ord_1",
  "status": "paid",
  "total": 150
}

Outro serviço usa isso para emitir nota fiscal.

Agora o time do produtor quer internacionalizar e muda total para:

{
  "total": {
    "amount": 150,
    "currency": "BRL"
  }
}

Para o produtor parece evolução.

Para o consumidor antigo, pode ser quebra direta.

Uma transição melhor seria:

  • manter total
  • adicionar amount e currency ou um novo objeto em paralelo
  • observar consumo
  • remover depois com janela combinada

O ponto forte não e “ficar com legado para sempre”.

E trocar ruptura silenciosa por evolução controlada.

Erros comuns

  • Chamar de “interno” e usar isso como desculpa para quebrar contrato.
  • Achar que schema valido sozinho garante compatibilidade.
  • Mudar semântica mantendo o mesmo nome de campo.
  • Tirar campo antigo assim que o consumidor novo fica pronto.
  • Não medir quem ainda depende da forma anterior.

Como um senior pensa

Quem tem mais experiência trata integração entre serviços como fronteira de produto, mesmo dentro da empresa.

O raciocínio costuma ser:

“Esse outro serviço e meu cliente operacional. Se eu mudo o acordo sem transição, eu exporto risco para ele.”

Esse jeito de pensar reduz acoplamento humano e técnico ao mesmo tempo.

Menos guerra de deploy. Mais previsibilidade.

O que o entrevistador quer ver

Em entrevista, esse assunto aparece quando pedem evolução de API, eventos ou integrações internas.

O avaliador quer ver se você entende que contrato e compromisso real.

Você sobe de nivel quando:

  • diferencia schema de semântica
  • fala de transição e depreciação
  • menciona observabilidade de consumo
  • mostra que consumidor também precisa ser robusto

Uma resposta forte costuma soar assim:

“Eu trataria o contrato entre serviços como acordo estavel. Primeiro tento evolução compativel. Se a mudança for breaking, faco convivencia temporária, meco consumo e removo depois.”

Sistema acoplado demais não falha porque os times são ruins. Falha porque o contrato foi tratado como detalhe.

Resumo rápido

O que vale manter na cabeça

Checklist de pratica

Use isto ao responder

Você concluiu este artigo

Próximo artigo REST vs GraphQL vs RPC: Quando Cada um Faz Sentido Artigo anterior Versionamento interno de regra sem comportamento escondido por flag

Continue explorando

Artigos relacionados