API versioning: estratégias para evoluir contratos sem quebrar clientes

1. Fundamentos do Versionamento de APIs

O versionamento de APIs é uma prática essencial para qualquer serviço que pretenda evoluir sem comprometer a experiência dos consumidores. O dilema central é: como adicionar novas funcionalidades, corrigir bugs ou alterar comportamentos sem quebrar integrações existentes?

Mudanças em APIs podem ser classificadas em dois grandes grupos:

  • Breaking changes: alterações que quebram clientes existentes (remoção de campos, mudança de tipos, alteração de endpoints)
  • Non-breaking changes: adições que não afetam consumidores atuais (novos campos opcionais, novos endpoints, novos métodos HTTP)

A semântica do versionamento semântico (semver) aplicada a APIs sugere que versões MAJOR indicam mudanças incompatíveis, MINOR indicam adições compatíveis e PATCH indicam correções compatíveis.

As consequências de não versionar são graves: clientes quebrados, retrabalho emergencial, perda de confiança dos integradores e danos à reputação do serviço.

2. Estratégias de Versionamento na URL (Path Versioning)

A abordagem mais simples e amplamente adotada é incluir a versão no caminho da URL:

GET /v1/usuarios
GET /v2/usuarios
POST /v1/pedidos

Vantagens:
- Simplicidade de implementação e descoberta explícita
- Fácil cache em CDNs e proxies
- Clareza para clientes sobre qual versão estão consumindo

Desvantagens:
- Poluição de URLs e duplicação de código no backend
- Rigidez em evoluções incrementais (cada mudança exige nova versão)
- Dificuldade em manter múltiplas versões simultâneas

Exemplo prático de resposta diferenciada entre versões:

Request:
GET /v1/usuarios/123

Response (v1):
{
  "id": 123,
  "nome": "Maria Silva",
  "email": "maria@exemplo.com"
}

Request:
GET /v2/usuarios/123

Response (v2):
{
  "id": 123,
  "nome_completo": "Maria Silva",
  "email": "maria@exemplo.com",
  "telefone": "(11) 99999-8888"
}

3. Versionamento via Cabeçalhos HTTP (Header Versioning)

Nesta estratégia, a versão é especificada através de cabeçalhos HTTP, mantendo as URLs limpas:

GET /usuarios/123
X-API-Version: 1

GET /usuarios/123
Accept: application/vnd.minhaapi.v2+json

Prós:
- URLs limpas e consistentes
- Separação de preocupações (a versão é um meta-dado)
- Adequado para APIs RESTful puristas

Contras:
- Descoberta oculta (clientes precisam saber qual cabeçalho usar)
- Complexidade em documentação e testes
- Dificuldade em cachear respostas (cabeçalhos variam)

4. Versionamento por Parâmetro de Query

Uma abordagem simples que utiliza parâmetros na query string:

GET /usuarios?version=1
GET /usuarios?version=2

Casos de uso: APIs públicas com versionamento opcional, migrações graduais.

Limitações:
- Cache agressivo em CDNs (parâmetros podem ser ignorados)
- Ambiguidade em parâmetros (conflito com parâmetros de negócio)
- Difícil versionamento de recursos aninhados

5. Evolução Sem Quebra com Estratégias Não-Quebradoras

O ideal é evitar versionamento sempre que possível, adotando práticas que permitam evolução sem quebra:

Princípio de tolerância:
- Campos opcionais com defaults seguros
- Extensões via campos adicionais em objetos
- Uso de _links e HATEOAS para orientar clientes

Técnicas de depreciação:
- Cabeçalho Sunset: informa quando uma versão será descontinuada
- Cabeçalho Deprecation: avisa que o recurso está obsoleto
- Logs de aviso no corpo da resposta

Exemplo de resposta com depreciação:

HTTP/1.1 200 OK
Deprecation: true
Sunset: Sat, 31 Dec 2024 23:59:59 GMT
Content-Type: application/json

{
  "id": 123,
  "nome": "Maria Silva",
  "_links": {
    "self": "/v2/usuarios/123",
    "migrate": "/docs/migration-v1-to-v2"
  }
}

6. Estratégias de Transição e Suporte Múltiplo

Gerenciar o ciclo de vida das versões é crucial:

Ciclo de vida:
1. Lançamento: nova versão disponível
2. Convivência: múltiplas versões ativas simultaneamente
3. Depreciação: aviso de descontinuação
4. Descontinuação: remoção da versão antiga

Roteamento inteligente:
- Proxies reversos (Nginx, Envoy)
- Gateways de API (Kong, AWS API Gateway, Apigee)

Políticas de suporte:
- Janela de tempo mínima (ex: 6 meses de suporte simultâneo)
- Comunicação clara via changelogs e documentação
- Notificações proativas para clientes impactados

7. Versionamento em APIs GraphQL e gRPC

GraphQL: o versionamento é feito através de depreciação de campos e aliases:

type Usuario {
  id: ID!
  nome: String
  nomeCompleto: String @deprecated(reason: "Use 'nome'")
  email: String
}

gRPC: utiliza protobuf com números de campo imutáveis:

message Usuario {
  int32 id = 1;
  string nome = 2;
  string email = 3;
  string telefone = 4;  // campo adicionado sem quebrar versão anterior
}

Design evolutivo: ambas as tecnologias incentivam tolerância a campos desconhecidos, reduzindo a necessidade de versionamento tradicional.

8. Boas Práticas e Checklist para Decisão

Documentação obrigatória:
- Changelog por versão detalhando mudanças
- Exemplos de migração entre versões
- Guias de depreciação

Testes de compatibilidade:
- Contratos OpenAPI/Swagger versionados
- Testes de regressão automatizados
- Testes de integração com clientes reais

Checklist final para escolha da estratégia:

Critério URL Header Query Evolução
Simplicidade
Descoberta explícita
Cache eficiente
URLs limpas
Evolução incremental

Decisão baseada em:
- Maturidade da API (pública vs interna)
- Perfil dos clientes (mobile, web, IoT)
- Frequência de mudanças (alta vs baixa)

Para APIs públicas com muitos consumidores, recomenda-se versionamento por URL combinado com estratégias de evolução não-quebradoras. Para APIs internas ou com poucos consumidores controlados, header versioning ou evolução sem versionamento são mais adequados.


Referências