Mudanças de API

Melhores práticas de versionamento de API, para chamadores

8 min de leitura

Versionamento de API é a prática de manter um contrato antigo funcionando depois que vocês o mudaram, para que chamadores possam avançar no cronograma deles em vez do de vocês. Essa frase contém as duas decisões que importam: o que conta como mudar o contrato, e por quanto tempo o antigo continua funcionando. Onde o número de versão vive, sobre o que trata a maioria dos debates de versionamento, é a menos importante das três e a mais fácil de acertar.

Quando uma API deveria ser versionada?

Versione uma API só quando uma mudança quebraria um chamador correto. Mudanças aditivas, novos campos, novos endpoints, novos parâmetros opcionais, não precisam de uma versão; chamadores escritos contra o contrato antigo continuam funcionando e a nova capacidade simplesmente está lá. Uma mudança que quebra algo precisa de uma, porque a alternativa é um chamador descobrir por um erro. Versionar toda release, incluindo as aditivas, ensina chamadores que versões são ruído, e eles param de ler os avisos que importam.

O teste prático é o mesmo do artigo sobre mudanças que quebram algo: se um chamador que dependia apenas de comportamento documentado precisa mudar algo para continuar funcionando, a mudança precisa de uma versão. Se não, lance sob a versão atual e escreva uma entrada de changelog.

Qual esquema de versionamento de API deveria ser usado?

Use o esquema que seus chamadores conseguem ver e fixar mais facilmente, o que para a maioria das APIs públicas é uma versão no caminho da URL ou um header de versão datado. Os quatro esquemas comuns diferem menos em capacidade do que no que pedem do chamador, e essa é a base certa para escolher.

EsquemaExemploO que o chamador deve fazerQuem usa
Caminho de URL/v2/invoicesMudar a URL ao migrarA maioria das APIs REST públicas
Header de versãoX-GitHub-Api-Version: 2022-11-28Enviar um header, ou aceitar o padrãoGitHub
Versão de conta datadaStripe-Version: 2026-08-26Fixar uma data por requisição ou por contaStripe
Parâmetro de query/invoices?version=2Adicionar um parâmetroAPIs mais antigas; raramente escolhido agora
Media typeAccept: application/vnd.example.v2+jsonNegociar tipos de conteúdoPuristas; poucos chamadores dominam

Caminho de URL é o mais visível e o menos flexível. Todo chamador consegue ver em qual versão está lendo uma linha de log, e um salto de versão é um buscar-e-substituir. O custo: toda a superfície se move de uma vez, vocês não conseguem mudar o contrato de um único endpoint sem cunhar uma nova versão para todos, então versões de caminho tendem a ser raras e grandes.

Header de versão mantém as URLs estáveis e deixa o servidor escolher um padrão para chamadores que não enviam nada, como funciona o versionamento da API REST do GitHub: uma versão nomeada por data em X-GitHub-Api-Version, com a versão suportada mais antiga como padrão para que chamadores sem versão não quebrem. O custo: a versão é invisível em uma URL e fácil de esquecer em um cliente novo.

Versão de conta datada é o esquema de header mais uma adição: a versão é armazenada contra a conta, então toda requisição a recebe sem enviar nada. O versionamento de API do Stripe fixa cada conta na versão com que foi criada e deixa uma requisição sobrescrever isso com Stripe-Version. Esse é o esquema mais amigável ao chamador e o que dá mais trabalho para operar, porque o servidor precisa traduzir entre cada versão suportada e a atual.

Parâmetro de query e media type ambos funcionam e ambos falham no teste de visibilidade de formas diferentes: um parâmetro de query cai facilmente ao construir uma URL, e uma versão de media type é invisível para quase qualquer ferramenta com a qual um chamador debugaria. O esquema por data do Stripe é o exemplo mais conhecido da abordagem por data, e como o Stripe versiona sua API o detalha.

Como se faz versionamento de API na prática?

Na prática uma versão é um conjunto nomeado de comportamentos, e o servidor mapeia cada requisição para um deles. Os passos são os mesmos independentemente de qual esquema carrega o nome.

  1. Nomeie versões por data ou por número inteiro, não por versão semântica. Uma API web não é um pacote. Chamadores não conseguem fixar uma versão minor de uma URL, então v2 ou 2026-08-26 diz tudo que um chamador precisa, e o versionamento semântico implica uma promessa de compatibilidade que o esquema não consegue cumprir.
  2. Mantenha a versão fora dos caminhos de código que não se importam com ela. Uma versão deveria selecionar uma camada de tradução na borda, não bifurcar a lógica de negócio. Duas cópias completas da base de código é como uma versão acaba sem manutenção.
  3. Dê a cada versão um padrão e um documento. Chamadores que não enviam versão recebem a mais antiga suportada, nunca a mais nova, para que um cliente não fixado não quebre no dia do lançamento. Cada versão tem uma página dizendo o que mudou em relação à anterior.
  4. Defina uma janela de suporte e publique-a. A orientação de versionamento do Google, a AIP-185, pede um período de transição razoável e bem comunicado e recomenda 180 dias mesmo para funcionalidades beta. Escolham uma janela, escrevam-na, e apliquem sem renegociar por versão.
  5. Aposentem versões da mesma forma que aposentam endpoints. Uma versão que passou de sua janela recebe o mesmo tratamento que qualquer API depreciada: um anúncio, um header Sunset (RFC 8594) em toda resposta, um lembrete na metade do caminho aos chamadores restantes, e uma data de remoção que se mantém.

O que são v1 e v2 em uma API REST?

v1 e v2 são nomes para dois contratos que o mesmo servidor suporta ao mesmo tempo. Um v2 existe porque algo em v1 não podia ser mudado sem quebrar seus chamadores, então a mudança foi para um novo contrato e o antigo continuou funcionando. Os números não implicam que v2 está completo ou que v1 está morto; ambos só são verdade se a documentação disser. Um v3 que aparece a cada trimestre é um sinal de que mudanças aditivas estão sendo versionadas, ou que o contrato nunca foi desenhado para absorver mudança.

Esse é o modelo de versionamento por caminho de URL, onde o número de versão é o segmento que a chamadora disca. Serviços gRPC geralmente resolvem o mesmo problema de outra forma: a versão mora no nome do pacote dentro do próprio arquivo .proto. gRPC e Protobuf cobre essa diferença e por que lá a compatibilidade no fio é definida por números de campo, não pela forma da URL.

O que uma mudança de versão deveria anunciar?

Uma mudança de versão deveria anunciar o que quebra, quem é afetado, como migrar, e por quanto tempo a versão anterior continua funcionando. A entrada tem a mesma forma de qualquer outra entrada de mudança que quebra algo, mais uma linha declarando a janela de suporte. Aqui está uma para uma API versionada por header:

A versão de API 2026-11-01 está disponível. A versão 2025-06-15 é suportada até 1º de novembro de 2027. Novidade em 2026-11-01: GET /invoices retorna amount em unidades mínimas como um número inteiro em vez de string decimal, e o campo depreciado customer_name é removido em favor do objeto customer. Afeta chamadores em 2025-06-15 que parseiam amount como string, que é o padrão para clientes não fixados criados antes de junho de 2025. Migração: parseie amount como inteiro e leia o nome de customer.name. Fixe X-Api-Version: 2026-11-01 quando estiver pronto. Nada muda para chamadores que não fixam versão.

A última frase é a que permite que a maioria das leitoras pare de ler, e pertence a todo anúncio de versão. A página exemplos de changelog inclui entradas de APIs que versionam assim, e a diferença entre as boas e o resto está principalmente nessa última frase.

Quem é informado quando uma versão muda?

Todo mundo na versão antiga, individualmente, e o changelog para todos os outros. Uma mudança de versão é o único caso em que “publicamos algo sobre isso” garantidamente perde exatamente os chamadores que importam: os que fixaram uma versão dois anos atrás e não leem uma nota de release desde então. Dados de uso respondem quem eles são; o aviso precisa alcançá-los onde está o código deles, nos headers de resposta e em uma mensagem para a dona da conta.

No ciclo que rodamos, a entrada que anuncia uma versão é redigida a partir do pull request que a lança, revisada por uma pessoa, e publicada em feed e widget, onde um cliente versionado pode lê-la como JSON. Quem pediu a mudança, ou relatou o bug que ela resolve, por um feedback no widget que virou uma issue do GitHub fechada pelo pull request, é informado nessa issue assim que a entrada entra no ar. O mecanismo é o mesmo de qualquer entrada; um salto de versão é só a entrada com a aposta mais alta.

FAQ

Toda mudança de API deveria receber uma nova versão? Não. Só as mudanças que quebram algo. Mudanças aditivas são lançadas sob a versão atual com uma entrada de changelog. Versionar mudanças aditivas treina chamadores a ignorar versões.

Versionamento por URL é melhor que por header? Versionamento por URL é mais fácil de ver para chamadores e mais difícil de evoluir aos poucos para vocês; versionamento por header é o contrário. Para uma API pública com muitos clientes pequenos, versionamento por URL falha menos. Para uma API grande com camada de tradução, a versão datada por header escala melhor.

Quantas versões deveriam ser suportadas ao mesmo tempo? As menos possíveis que sua janela de suporte permita, e nunca um número ilimitado. Duas ou três versões concorrentes é normal; mais que isso geralmente significa que versões não estão sendo aposentadas.

O que requisições sem versão deveriam receber? A versão suportada mais antiga, para que clientes existentes não fixados continuem funcionando, com um header de resposta dizendo qual versão eles receberam.


As afirmações técnicas deste artigo não foram revisadas de forma independente. Se algo estiver errado, avise a gente e vamos corrigir.

Relacionado na changeloop: Documentação para desenvolvedores, Exemplos de changelog

changeloop
O time por trás de um changelog que fecha o loop. Os usuários pedem algo, sua equipe entrega, quem pediu fica sabendo.