Como projetar APIs públicas duráveis e evolutivas

1. Fundamentos da Durabilidade em APIs Públicas

Uma API pública é um contrato digital entre provedor e consumidor. Diferente de APIs internas, uma API pública estabelece compromissos de longo prazo: milhares de aplicações podem depender de cada endpoint, campo e comportamento. Quebrar esse contrato significa causar falhas em produção em escala global.

O ciclo de vida típico de uma API pública começa com a versão 1.0, passa por evoluções incrementais e termina com a descontinuação planejada. Cada etapa exige disciplina. O princípio fundamental é: nunca remova algo que os consumidores estão usando sem aviso prévio adequado.

Exemplo de contrato mínimo em OpenAPI 3.0:
openapi: 3.0.0
info:
  title: API de Usuários
  version: 1.0.0
paths:
  /users:
    get:
      summary: Lista usuários
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'
components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
      required:
        - id
        - name

2. Versionamento Semântico e Estratégias de Evolução

O versionamento semântico (MAJOR.MINOR.PATCH) é a espinha dorsal da evolução controlada. Uma mudança MAJOR quebra compatibilidade; MINOR adiciona funcionalidades sem quebrar; PATCH corrige bugs.

Existem três estratégias principais de versionamento:

  • URI: /v1/users, /v2/users — simples, mas polui a URL.
  • Cabeçalho: Accept: application/vnd.api+json;version=1 — mais limpo, mas menos descoberto.
  • Parâmetro: /users?version=1 — fácil de testar, mas pode ser ignorado.

A depreciação deve ser comunicada via cabeçalhos Sunset e Deprecation:

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

3. Projetando Contratos Flexíveis com Extensibilidade

A Lei de Postel (Postel’s Law) aplicada a APIs: "Seja conservador no que você envia, seja liberal no que você aceita." Isso significa que sua API deve ignorar campos desconhecidos em requisições e permitir que consumidores recebam campos extras sem quebrar.

Padrões como JSON:API, HAL e GraphQL oferecem extensibilidade nativa. Em REST tradicional, use envelopes flexíveis:

// Resposta flexível com campos opcionais
{
  "data": {
    "id": 123,
    "name": "João Silva",
    "email": "joao@exemplo.com",
    "phone": null,  // campo opcional, nunca removido
    "metadata": {}  // extensão futura
  },
  "meta": {
    "version": "1.2.0"
  }
}

Sempre adicione campos opcionais em vez de modificar os existentes. Se precisar renomear, mantenha o antigo como alias:

// Transição suave: campo antigo mantido, novo adicionado
{
  "full_name": "João Silva",    // será removido na v2
  "name": "João Silva",         // substituto
  "display_name": "João S."     // novo campo opcional
}

4. Gerenciamento de Mudanças com Backward Compatibility

Compatibilidade retroativa exige técnicas específicas:

  • Defaults: novos campos obrigatórios no backend, mas com valor padrão para consumidores antigos.
  • Aliases: endpoints antigos redirecionam para novos.
  • Adaptadores: camada intermediária que traduz versões.

Exemplo de adaptador em Node.js:

// Adaptador de versão
function adaptUserV1toV2(v1User) {
  return {
    id: v1User.id,
    name: v1User.full_name,  // mapeia campo renomeado
    email: v1User.email,
    created_at: v1User.created_at || new Date().toISOString()
  };
}

Testes de contrato automatizados (ex.: Pact, Spring Cloud Contract) garantem que mudanças não quebrem consumidores conhecidos.

5. Documentação, Descoberta e Automação

OpenAPI (antigo Swagger) é o padrão-ouro para documentar APIs públicas. Ele serve como fonte única da verdade, permitindo:

  • Geração automática de documentação interativa (Swagger UI)
  • Geração de SDKs em múltiplas linguagens (OpenAPI Generator)
  • Validação automática de requisições e respostas
# Exemplo de changelog em arquivo CHANGELOG.md
## [2.0.0] - 2025-03-15
### Breaking Changes
- Removido endpoint `/v1/users/:id/profile`
- Campo `full_name` renomeado para `name`
### Added
- Novo endpoint `/v2/users/:id/profile`
- Campo opcional `display_name` em respostas de usuário

Comunique breaking changes com pelo menos 6 meses de antecedência, usando changelogs, e-mails e cabeçalhos HTTP.

6. Segurança, Rate Limiting e Observabilidade

APIs públicas exigem segurança previsível. Prefira OAuth 2.0 para autorização e API keys para autenticação simples. Rate limiting deve ser claro e comunicado via cabeçalhos padrão:

HTTP/1.1 200 OK
RateLimit-Limit: 100
RateLimit-Remaining: 75
RateLimit-Reset: 1620000000
Retry-After: 120

Monitore métricas de depreciação: quantos consumidores ainda usam endpoints obsoletos? Ferramentas como Datadog, Prometheus ou logs estruturados ajudam a rastrear o uso.

7. Governança e Ciclo de Vida Completo

Estabeleça um comitê de revisão de mudanças na API. Toda proposta de breaking change deve passar por análise de impacto.

Estratégia de sunset em 4 fases:

  1. Anúncio: comunicado com data de descontinuação (mínimo 6 meses)
  2. Convivência: versão antiga e nova rodam em paralelo
  3. Degradação gradual: redução de rate limit na versão antiga
  4. Desligamento: retorno de erro 410 Gone

Casos reais de sucesso:

  • Stripe: versionamento por data (2023-08-01), changelogs detalhados, SDKs versionados
  • GitHub: API v3 estável por anos, migração gradual para GraphQL
Exemplo de resposta de erro para API descontinuada:
HTTP/1.1 410 Gone
Content-Type: application/json
{
  "error": {
    "code": "api_deprecated",
    "message": "Esta versão da API foi descontinuada. Use /v2/users.",
    "documentation_url": "https://docs.api.com/migration-v2"
  }
}

Referências