O que Estratégia de Versionamento de API faz?
TL;DR: Versões baseadas em data fixadas por chave de API (modelo do Stripe) superam
/v1/,/v2/para APIs públicas de longa duração, pois permitem pequenas mudanças sem forçar clientes a uma nova árvore. Para APIs internas, evolução aditiva + os cabeçalhosDeprecationeSunset(RFC 9745, RFC 8594) geralmente é suficiente. Sempre anuncie, sempre defina uma data de encerramento, nunca apenas remova.
Vá direto ao seu problema
| Sintoma | Seção |
|---|---|
| "Preciso quebrar um campo, mas tenho 100 mil chaves de API" | Versões fixas baseadas em data (modelo Stripe) |
"Devo usar /v1/ ou Accept: application/vnd.foo.v2+json?" | Comparação de Estratégias |
| "Como aviso os clientes que um endpoint será descontinuado?" | Deprecation + Sunset |
| "Manter 6 versões no código está nos matando" | Transformadores de versão |
| "API interna — precisamos mesmo de versionamento?" | Interna vs pública |
Diagrama de decisão
flowchart TD
A[Precisa mudar o comportamento da API] --> B{Apenas aditivo?<br/>novo campo, novo endpoint, novo parâmetro opcional}
B -->|Sim| C[Apenas publique<br/>sem versionamento]
B -->|Não, é quebra| D{A API é pública<br/>+ muitos clientes opacos?}
D -->|Não, interna/poucos clientes| E[Coordene a migração<br/>+ cabeçalhos Deprecation/Sunset]
D -->|Sim, pública| F{Pode ter camada de compat<br/>por requisição?}
F -->|Sim, tem transformador| G[Versão fixa baseada em data<br/>modelo Stripe]
F -->|Não, grande reescrita| H[Aumento de versão na URI<br/>/v1 → /v2]
E --> I[Defina Deprecation: @timestamp<br/>+ Sunset: HTTP-date<br/>+ Link: rel=deprecation]
G --> I
H --> I
1. Versões fixas baseadas em data (modelo Stripe)
O Stripe publicou sua arquitetura em API versioning at Stripe:
Na primeira vez que um usuário faz uma requisição à API, sua conta é automaticamente fixada na versão mais recente disponível e, a partir daí, toda chamada de API que ele fizer recebe essa versão implicitamente.
[As versões são] versões contínuas nomeadas com a data em que são lançadas (por exemplo,
2017-05-24).
As propriedades principais:
| Propriedade | Por que funciona |
|---|---|
| Versão padrão por conta (definida na primeira chamada) | Novos clientes fixados automaticamente na versão mais recente; clientes existentes não quebram quando você publica uma mudança |
Cabeçalho Stripe-Version substitui a versão fixa por requisição | Permite migração gradual do cliente: o SDK pode optar por uma nova versão antes da conta |
Nomes baseados em data (2024-04-10) | Transmite atualidade; sem debate sobre o que "v3" significa; permite várias pequenas quebras em vez de acumulá-las para um big-bang v3 |
| Caminho de upgrade no dashboard | O cliente pode visualizar as diferenças e depois atualizar a versão padrão da conta |
Esta é a única abordagem que escala para APIs públicas verdadeiramente de longa duração (o Stripe tem versões que remontam a uma década). Custa complexidade interna (o padrão de transformador na §4), mas poupa seus clientes do ciclo perpétuo de migração /v1/ → /v2/.
2. Comparação de estratégias de versionamento
| Estratégia | Onde a versão fica | Pró | Contra | Melhor para |
|---|---|---|---|---|
Segmento URI (/v1/, /v2/) | Caminho | Amigável para cache (URL diferente = entrada de cache diferente); configuração zero do cliente | Não permite pequenas quebras; força uma bifurcação de árvore; URLs não são mais "identificadores estáveis de recurso" (segundo Fielding) | APIs internas, APIs públicas que raramente quebram |
Cabeçalho personalizado (Stripe-Version: 2024-04-10) | Cabeçalho de requisição | Muitas versões baratas; granularidade por requisição | Não é possível compartilhar URLs com versão embutida; mais difícil de testar na barra de endereços do navegador | APIs públicas em escala |
Tipo de mídia Accept (Accept: application/vnd.foo.v2+json) | Cabeçalho Accept padrão | "Correto pela especificação" HTTP; reutiliza o mecanismo de negociação de conteúdo | Difícil de configurar; suporte de ferramentas varia; debug via curl é verboso | APIs de hipermídia, designs profundamente RESTful |
Parâmetro de consulta (?version=2) | URL | Fácil de testar; visível em logs | Polui a URL; semanticamente errado (versão não é uma propriedade do recurso) | Prototipagem rápida apenas |
| Sem versionamento, apenas aditivo | n/a | Zero overhead; sem coordenação com cliente | Nunca pode fazer uma mudança quebradiça sem um novo endpoint | Microsserviços internos com deploy compartilhado |
Escolha selecionando sua restrição:
- "Fazemos pequenas quebras com frequência" → cabeçalho baseado em data (Stripe)
- "Fazemos uma grande reescrita a cada 5 anos" → segmento URI (
/v1/,/v2/) - "Nunca quebramos, só adicionamos" → sem versionamento + evolução aditiva
3. O par de cabeçalhos Deprecation/Sunset
Duas RFCs da IETF cobrem o sinal de ciclo de vida:
Cabeçalho Sunset — RFC 8594
O valor de Sunset é um timestamp HTTP-date, conforme definido na Seção 7.1.1.1 da [RFC7231], e DEVE ser um timestamp no futuro.
Sunset: Sat, 31 Dec 2026 23:59:59 GMT
Indica "espera-se que o recurso se torne não responsivo em um ponto específico no futuro." Clientes DEVEM tratar o timestamp como uma dica, não um contrato rígido.
Cabeçalho Deprecation — RFC 9745
Deprecation: @1735689599
Sunset: Sun, 31 Dec 2026 23:59:59 GMT
Link: <https://api.example.com/docs/migrate-v1-to-v2>; rel="deprecation"
O valor de Deprecation é um timestamp Unix (segundos, prefixado com @ conforme Structured Fields). Pode estar no passado ("já depreciado") ou futuro ("será depreciado"). Os DEVE ser:
- DEVE usar o formato de data structured-field conforme RFC 9651
SunsetNÃO DEVE ser anterior aDeprecation— a especificação é explícita; é uma restrição de ordenação temporal- DEVE incluir um
Linkcomrel="deprecation"apontando para a documentação de migração
O ato de enviar Deprecation não altera o comportamento do recurso — é um sinal, não uma degradação. Servidores DEVEM manter o recurso funcional até a data Sunset (salvo emergências).
Cronograma recomendado
T+0 Primeira Deprecation: cabeçalho enviado em produção
T+30d Anúncio público (changelog, e-mail, docs)
T+90d Adicionar logs / métricas de resposta do caminho depreciado
T+180d Data Sunset definida para 90 dias à frente
T+270d Data Sunset chega — endpoint retorna 410 Gone com link de migração
Para mudanças quebradiças menores em uma API de versão principal fixa: 6 meses é a mediana. Para encerramentos completos de v1 → v2: 12-24 meses.
4. O padrão de transformador de versão
A arquitetura interna do Stripe resolve o problema "temos 50 versões em produção":
[O sistema usa] classes de recurso de API que definem as estruturas de resposta atuais da API, combinadas com módulos de mudança de versão que encapsulam transformações incompatíveis com versões anteriores. Ao processar respostas, o sistema caminha de volta no tempo e aplica cada módulo de mudança de versão que encontra até que a versão alvo seja atingida.
A forma:
// resource: a representação canônica (mais recente)
const charge = {
id: 'ch_123',
amount: 1000,
payment_method_details: { card: { brand: 'visa', last4: '4242' } },
}
// Cada mudança quebradiça é um módulo transformador:
const v_2024_03_01 = {
// Remove o aninhamento 'card' dentro de payment_method_details, achata campos
apply(resource) {
return {
...resource,
card_brand: resource.payment_method_details?.card?.brand,
card_last4: resource.payment_method_details?.card?.last4,
}
},
}
const v_2023_10_15 = {
// Renomeia 'amount' para 'amount_cents'
apply(resource) {
const { amount, ...rest } = resource
return { ...rest, amount_cents: amount }
},
}
// Pipeline aplica transformadores em ordem cronológica reversa
// até que a versão solicitada seja alcançada
function transform(resource, requestedVersion) {
const chain = transformers.filter(t => t.date > requestedVersion)
return chain.reduceRight((r, t) => t.apply(r), resource)
}
Os ganhos:
- Código principal permanece moderno — cada endpoint é escrito contra o esquema mais recente; sem
if (version < ...)espalhado pela lógica de negócio. - Cada quebra é um arquivo — fácil de revisar, fácil de testar isoladamente, fácil de excluir quando o último usuário ultrapassa essa versão.
- Telemetria — você pode contar quantos transformadores disparam por dia para planejar depreciações com base no uso real.
O custo: construir o framework de transformadores. Vale a pena se você tiver >3 mudanças quebradiças por ano e >10 mil consumidores de API; é exagero para uma API interna com <50 clientes.
5. APIs internas vs públicas
A assimetria importa:
| Interna | Pública | |
|---|---|---|
| Deploy coordenado possível? | Sim — troca atômica | Não — clientes implantam independentemente |
| Evolução de esquema | Aditivo + mensagem no Slack | Versionamento estrito + e-mail + dashboards + post no blog |
| Janela de encerramento | Dias a semanas | 6-24 meses |
| Necessidade de versionamento | Frequentemente nenhuma — apenas cabeçalhos Deprecation | Obrigatório |
| Melhor estratégia | Evolução aditiva; versão URI apenas em reescrita | Cabeçalhos com data fixa (se a escala justificar), URI para grandes reescritas |
A armadilha: empresas tratam todas as APIs como "públicas" e constroem a infraestrutura pesada de versionamento para uma API usada por apenas 3 serviços internos. A armadilha inversa: uma API interna vazada tem consumidores externos e você não pode realmente quebrá-la.
Defesa: torne "público" uma tag binária no serviço. Serviços públicos recebem a cerimônia completa de versionamento; serviços internos não, mas recebem monitoramento agressivo de quem os chama.
Anti-padrões
| Anti-padrão | Por que morde | Correção |
|---|---|---|
Aumentar /v1/ para /v2/ para uma única mudança quebradiça | Força clientes a migrar tudo por um campo | Versão baseada em data OU evolução aditiva |
Remover endpoints sem cabeçalho Sunset | Clientes quebram sem aviso; tickets de suporte explodem | Sempre combine remoção com Deprecation 90+ dias antes |
Enviar Deprecation mas sem Link | Clientes sabem que está depreciado, mas não o que fazer | Sempre inclua Link: <migration-doc>; rel="deprecation" |
| Isenções de versão fixadas por cliente codificadas | Torna-se insustentável; não é possível raciocinar sobre o comportamento | Use o padrão de transformador; "isenções" viram fixações de versão |
| Versionar APIs internas como as públicas | Sobrecarga de processo sem retorno | Evolução aditiva + deploys atômicos + Slack |
Usar semver (v1.2.3) em APIs REST | Implica significado que não se aplica (o que é um "patch" para um esquema JSON?) | Nomes baseados em data ou apenas versão principal (v1) |
| Data Sunset sem aplicação | Endpoint vive para sempre, código apodrece | Lembrete no calendário; teste de CI que falha 7 dias antes do sunset para forçar ação |
Novato / Especialista / Cronograma
| Novato | Especialista | |
|---|---|---|
| Primeira mudança quebradiça | Aumenta /v1 para /v2, copia-e-cola toda a árvore | Considera fixação baseada em data, avalia se a mudança é realmente quebradiça |
| Avisar clientes sobre depreciação | E-mail em massa | Cabeçalhos Deprecation + Sunset + Link + e-mail + banner no dashboard |
| Manter 5 versões | 5 ramos if/else em cada controlador | Módulos transformadores, código principal apenas com versão mais recente |
| Aplicação de Sunset | "Vamos fazer depois" | Data Sunset no CI; endpoint retorna 410 Gone na data |
| Mudança em API interna | Mesma cerimônia que a pública — lenta | Evolução aditiva + deploy atômico; reserva processo pesado para APIs públicas |
Teste do cronograma: quanto tempo leva de "queremos remover o campo X" a "campo X sumiu em produção"? Uma organização especialista tem uma única resposta repetível (ex.: 90 dias). Uma organização novata não tem resposta porque não há processo.
Portões de qualidade
Uma mudança de versionamento é publicada quando:
- Teste: Remover ou renomear qualquer campo visível externamente é condicionado a um anúncio de depreciação com pelo menos N dias de idade (onde N é a janela de sunset documentada).
- Teste: Todo endpoint depreciado envia cabeçalhos
Deprecation: @<unix-ts>eSunset: <HTTP-date>, verificado em um teste de integração. - Teste: O valor de
Sunsetnunca é anterior ao valor deDeprecation(conforme RFC 9745 DEVE). - Teste: Todo anúncio de depreciação inclui um cabeçalho
Link: <docs-url>; rel="deprecation"E um documento de migração público que existe nessa URL (verificação HEAD no CI). - Teste: A telemetria de uso por versão existe — você pode responder "quantas requisições no caminho v2 ontem?" antes de deletar v2.
- Teste (apenas fixação por data): Novas chaves de API usam a versão mais recente como padrão; chaves existentes mantêm sua fixação entre deploys.
- Manual: Plano de aplicação do Sunset: um lembrete no calendário, uma verificação de CI ou um 410 Gone automatizado — algo dispara quando a data chega.
NÃO faz parte deste skill
- Evolução de esquema GraphQL (use
graphql-schema-evolution) - Compatibilidade de fio gRPC / Protobuf (use
protobuf-evolution-rules) - Versionamento de esquema de banco de dados (use
zero-downtime-database-migration) - Semver de bibliotecas / SDKs (use
npm-package-versioning) - Evolução de esquema de stream de eventos (use
kafka-schema-registry-design)
Fontes
- Stripe: API versioning at Stripe — versões fixas baseadas em data, padrão de transformador
- API do Stripe: Idempotent requests / versioning policy
- IETF: RFC 8594 — The Sunset HTTP Header Field
- IETF: RFC 9745 — The Deprecation HTTP Header Field
- IETF: RFC 9651 — Structured Field Values for HTTP (formato de data para o cabeçalho Deprecation)
- Roy Fielding: REST APIs must be hypertext-driven — o argumento canônico contra versionamento em URI