
Sua API é um contrato entre você e seus clientes (aplicações mobile, SPA, parceiros). Um dia, você precisa adicionar um campo obrigatório, renomear uma propriedade ou remover um endpoint obsoleto. Como fazer isso sem quebrar todos os clientes existentes? A resposta é versionamento de API.
Versionar APIs é inevitável. O problema não é se versionar, mas como. Neste post, vamos explorar as principais estratégias de versionamento (URI, header, query parameter, content negotiation), quando usar cada uma, como comunicar deprecação e como manter compatibilidade retroativa – com exemplos em HTTP/REST, gRPC e práticas que a Jacobus aplica em projetos reais.
Por que versionar?
APIs evoluem. Bugs são corrigidos, novos campos são adicionados, requisitos mudam. Sem versionamento, você tem duas opções ruins:
- Quebrar clientes existentes – eles param de funcionar, você perde negócio.
- Nunca mudar a API – você fica preso a decisões antigas, não inova.
Versionamento permite que ambos coexistam: clientes antigos usam a versão antiga; clientes novos usam a nova.
Estratégias de versionamento para REST APIs
| Estratégia | Exemplo | Prós | Contras |
|---|---|---|---|
| URL path | /v1/users, /v2/users | Simples, visível, fácil cache | “Polui” a URL, difícil gerenciar múltiplas versões no código |
| Query parameter | /users?version=1 | Fácil de implementar | Cache pode ignorar query param; pouco idiomático |
| Custom header | Accept: application/vnd.myapi.v1+json | Limpo, sem poluir URL | Clientes precisam configurar header; menos visível |
| Content negotiation | Accept: application/json; version=1 | Padrão HTTP | Complexo, muitos clientes não suportam |
1. Versionamento por URL Path (recomendado pela Jacobus)
É a estratégia mais comum e intuitiva. A versão fica no path da URL.
GET /v1/users/123
GET /v2/users/123
Exemplo em Go (usando gorilla/mux ou net/http):
router := http.NewServeMux()
// v1 handler
router.HandleFunc("/v1/users/", handleGetUserV1)
// v2 handler (pode chamar a mesma lógica ou diferente)
router.HandleFunc("/v2/users/", handleGetUserV2)
func handleGetUserV1(w http.ResponseWriter, r *http.Request) {
// retorna User com campos antigos
}
func handleGetUserV2(w http.ResponseWriter, r *http.Request) {
// retorna User com novos campos
}
Vantagens: fácil de rotear (Kubernetes Ingress, API Gateway), cache funciona por URL, claro para clientes.
2. Versionamento por Custom Header
GET /users/123
X-API-Version: 1
Em Go, leia o header:
func handleGetUser(w http.ResponseWriter, r *http.Request) {
version := r.Header.Get("X-API-Version")
switch version {
case "1":
serveV1(w, r)
case "2":
serveV2(w, r)
default:
serveV1(w, r) // default
}
}
Quando usar: quando URLs limpas são importantes (ex: API pública de alta visibilidade) ou você quer forçar clientes a declarar versão explicitamente.
3. Versionamento por Accept Header (Content Negotiation)
GET /users/123
Accept: application/vnd.mycompany.user.v1+json
É o padrão RESTful mais “puro”, mas raramente usado por sua complexidade.
Como lidar com breaking changes sem quebrar clientes
Mesmo com versionamento, mudanças podem quebrar clientes se você não planejar. Boas práticas:
Mudanças compatíveis (não exigem nova versão)
| Mudança | Exemplo | É breaking? |
|---|---|---|
| Adicionar campo opcional | { "name": "João", "age": 30 } → { ..., "email": "joao@email.com" } | ❌ Não |
| Adicionar novo endpoint | GET /v1/users → também GET /v1/users/123/orders | ❌ Não |
| Ampliar valores de enum | status: "active" → "active", "suspended" (desde que clientes tratem desconhecidos) | ❌ Não |
| Mudar ordem de campos | Objeto JSON reordenado | ❌ Não (ordem não importa) |
Mudanças incompatíveis (exigem nova versão)
| Mudança | Exemplo | Solução |
|---|---|---|
| Remover campo | { "name": ... } → {} | Marcar como deprecated por uma versão, depois remover na v2 |
| Renomear campo | "user_id" → "userId" | Aceitar ambos por um período, depois remover o antigo |
| Mudar tipo de campo | "age": "30" (string) → "age": 30 (int) | Nova versão |
| Tornar campo obrigatório | email opcional → obrigatório | Nova versão |
Estratégia de deprecação (deprecation)
Uma versão não pode morrer do dia para a noite. Use um ciclo de deprecação:
- Anuncie com antecedência – informe que a versão V1 será descontinuada em 6 meses.
- Adicione headers de aviso – retorne
Warning: 299 - "Deprecated"ouDeprecation: true. - Monitore uso – veja quais clientes ainda usam a versão antiga.
- Após o prazo, retorne 410 Gone – mas considere manter em modo “read-only” para clientes teimosos.
Exemplo de headers de deprecação (RFC 8594):
HTTP/1.1 200 OK
Deprecation: true
Sunset: Sat, 31 Dec 2026 23:59:59 GMT
Link: <http://api.example.com/v2/docs>; rel="latest-version"
Em Go:
w.Header().Set("Deprecation", "true")
w.Header().Set("Sunset", "Sat, 31 Dec 2026 23:59:59 GMT")
w.Header().Set("Link", `<http://api.example.com/v2/docs>; rel="latest-version"`)
Versionamento em gRPC
gRPC tem um mecanismo diferente: protocol buffers (protobuf). Mudanças compatíveis são muito mais fáceis.
Regras de compatibilidade protobuf (mesma versão de serviço)
✅ Adicionar novo campo – desde que use números novos e não obrigatório (não use required).
✅ Renomear campo – o número do campo é o identificador, nome é apenas metadado.
✅ Remover campo – reserve o número (com reserved) para não reutilizar.
✅ Adicionar novo método na service.
❌ Mudar tipo de campo – incompatível.
❌ Mudar número de campo – incompatível.
❌ Remover método – quebra clientes.
Versionamento semântico para APIs gRPC
Use diferentes pacotes protobuf para versões diferentes:
// v1/api.proto
package api.v1;
service UserService {
rpc GetUser(GetUserRequest) returns (User);
}
// v2/api.proto
package api.v2;
service UserService {
rpc GetUser(GetUserRequestV2) returns (UserV2);
}
No servidor, registre ambas as implementações.
Versionamento em filas e eventos (mensageria)
Se você publica eventos em Kafka/RabbitMQ/NATS, versionamento também é essencial.
Boas práticas:
- Inclua
versionno envelope da mensagem. - Use esquemas compatíveis (Avro, Protobuf, JSON Schema) com evolução.
- Consumidores ignoram campos que não conhecem.
- Adicione
deprecatedem campos que serão removidos no futuro.
{
"version": 1,
"event_type": "order.created",
"data": {
"order_id": "123",
"total": 100.50
}
}
Estratégia de “mantenha duas versões simultâneas”
Na prática, você raramente quer manter mais de duas versões ativas (ex: v1 e v2). Crie uma política:
- Versão atual (última) – recebe novas funcionalidades.
- Versão anterior – apenas correções de segurança e bugs críticos.
- Versões mais antigas – descontinuadas (410 Gone).
Ferramentas para auxiliar versionamento
| Ferramenta | Função |
|---|---|
| OpenAPI (Swagger) | Documentar versões da API, gerar clientes, validar mudanças |
| Buf (para gRPC) | Checa compatibilidade protobuf em CI |
| GraphQL | Não tem versionamento explícito – use deprecação de campos |
| API Gateway | Pode rotear diferentes versões para diferentes backends |
Caso real: API de pagamentos com versionamento
Um cliente da Jacobus (fintech) tinha APIs não versionadas. Quando precisaram adicionar um novo campo installments no payload de pagamento, clientes antigos quebravam porque enviavam o payload sem o campo. Implementamos:
- Versionamento via URL path (
/v1/payments,/v2/payments). - Documentação OpenAPI para cada versão.
- Proxy no API Gateway roteando com base no path.
- Deprecação gradual da v1 com header
Deprecatione Sunset após 1 ano.
Resultado: zero downtime para clientes existentes; novos clientes usam v2; migração completa em 8 meses sem incidentes.
Conclusão
Versionamento de API não é opcional – é garantia de evolução sem destruir quem depende de você. Prefira URL path para REST (mais simples e cacheável) e protobuf com pacotes separados para gRPC. Anuncie deprecação com headers, mantenha no máximo duas versões ativas e documente tudo.
Na Jacobus Software, projetamos APIs que evoluem com o negócio, não contra ele. Se você ainda não versiona suas APIs, comece hoje – seus clientes (futuros) agradecerão.
🔄 Quer versionar suas APIs sem quebrar clientes?
Nossos especialistas ajudam a planejar e implementar estratégias de versionamento, documentação OpenAPI e deprecação segura.
