Como documentar APIs internas com decisões de design e trade-offs
1. Por que Documentar Decisões de Design em APIs Internas?
APIs internas são a espinha dorsal da comunicação entre serviços em qualquer organização de médio a grande porte. Diferentemente de APIs públicas, onde a documentação frequentemente se limita a contratos e exemplos de uso, APIs internas exigem um nível adicional de profundidade: o registro explícito das decisões de design e dos trade-offs envolvidos.
A ausência dessa documentação gera um fenômeno conhecido como "atrito cognitivo". Quando um novo desenvolvedor precisa alterar um endpoint, ele não sabe por que determinada escolha foi feita — se o design atual é fruto de uma restrição técnica, de uma preferência pessoal ou de um compromisso inevitável. Isso leva a retrabalho, regressões e, em casos extremos, à duplicação de funcionalidades inteiras.
Considere o seguinte cenário: um endpoint GET /orders retorna pedidos paginados com base em cursor. Sem a documentação adequada, um novo membro da equipe pode "corrigir" a implementação para usar paginação baseada em offset, sem saber que a escolha original foi motivada por requisitos de consistência em um banco de dados distribuído. Horas de debugging e um incidente em produção depois, a equipe descobre que a decisão havia sido cuidadosamente ponderada — mas o conhecimento estava apenas na mente de um desenvolvedor que saiu da empresa há seis meses.
A documentação de decisões de design também previne regressões arquiteturais. Quando um time decide, após análise, que determinado endpoint deve ser assíncrono para evitar acoplamento temporal, registrar essa decisão impede que uma futura refatoração "simplifique" o design reintroduzindo sincronicidade sem considerar os mesmos trade-offs.
2. Estrutura de um Documento de Decisão (ADR) para APIs
O formato mais eficaz para documentar decisões de design em APIs internas é o Architecture Decision Record (ADR). Sua estrutura clássica — Contexto, Decisão, Consequências — pode ser adaptada para endpoints e contratos específicos.
Um ADR mínimo para uma API REST interna segue este formato:
# ADR-004: Paginação baseada em cursor para GET /orders
## Contexto
O endpoint GET /orders precisa listar pedidos de forma eficiente.
O volume de dados é de aproximadamente 500 mil registros, com crescimento
de 10% ao mês. O banco de dados subjacente é PostgreSQL 14, e o serviço
opera em regime de alta disponibilidade com replicação assíncrona.
## Decisão
Utilizar paginação baseada em cursor (token) em vez de offset/limit.
O cursor será um ID base64 codificado do último registro retornado.
## Consequências
Positivas:
- Performance consistente independentemente da profundidade da página
- Menor probabilidade de resultados duplicados durante inserções concorrentes
- Compatibilidade com replicação assíncrona (não depende de ordem estável)
Negativas:
- Mais complexidade no cliente (navegação reversa não é trivial)
- Dificuldade em implementar "pular para página X"
- Necessidade de documentar o formato do cursor como opaco
Para adaptar ADRs a contratos específicos, inclua referências diretas ao schema OpenAPI ou ao arquivo de protobuf. Isso cria uma trilha auditável entre a decisão documentada e sua implementação concreta.
3. Mapeamento de Trade-offs Técnicos Comuns
Síncrono vs. assíncrono
A escolha entre comunicação síncrona e assíncrona é talvez o trade-off mais frequente em APIs internas. O ADR correspondente deve documentar:
## Contexto
O serviço de pagamentos precisa notificar o serviço de estoque sobre
pedidos confirmados. A latência aceitável é de 500ms.
## Decisão
Comunicação assíncrona via fila RabbitMQ com confirmação manual.
## Trade-offs
- Latência: aumenta em ~50ms, mas ainda dentro do SLA
- Consistência: eventual, com retry automático em caso de falha
- Acoplamento: reduzido (serviço de estoque pode ficar indisponível)
- Complexidade operacional: maior (monitoramento de filas, DLQ)
Paginação: cursor vs. offset
A paginação baseada em offset é intuitiva, mas sofre de degradação de performance em conjuntos grandes de dados. O cursor oferece performance consistente, mas sacrifica a navegação não sequencial.
## Alternativa Rejeitada: Paginação offset
- Performance O(n) para páginas profundas
- Resultados inconsistentes durante inserções/remoções concorrentes
- Problemas de replicação assíncrona (ordem de registros instável)
Versionamento de contrato
Três abordagens principais existem, cada uma com seus trade-offs:
## URI versioning (/v1/orders)
- Prós: explícito, fácil de rotear, cache-friendly
- Contras: poluição de URL, difícil manter múltiplas versões
## Header versioning (Accept: application/vnd.api+json;version=1)
- Prós: URL limpa, semântica HTTP pura
- Contras: invisível em logs, complexidade para clientes
## Content negotiation
- Prós: mais RESTful, separação de preocupações
- Contras: suporte inconsistente em frameworks, debugging difícil
4. Documentação de Restrições Não-Funcionais
Restrições não-funcionais frequentemente são esquecidas na documentação de APIs internas, mas são cruciais para o consumo correto pelos times clientes.
Rate limits e throttling
## Política de Rate Limiting
- Endpoint: POST /payments
- Limite: 100 requisições por minuto por cliente
- Resposta em caso de excesso: HTTP 429 com header Retry-After
- Justificativa: o gateway de pagamento externo impõe limite de 120/min
SLA e timeout
## SLA Documentado
- Tempo de resposta P95: < 200ms
- Timeout do cliente: 500ms (incluindo retry)
- Número de retries: 3 com backoff exponencial (100ms, 200ms, 400ms)
- Justificativa: o serviço depende de cache Redis com P99 de 50ms
Segurança interna
## Autenticação e Autorização
- Método: OAuth2 com client credentials flow
- Escopos: orders:read, orders:write, payments:process
- Token lifetime: 15 minutos (curto para reduzir risco de vazamento)
- Justificativa: ambiente interno com requisitos de auditoria PCI-DSS
5. Registro de Alternativas Rejeitadas e Justificativas
Documentar o que foi descartado é tão importante quanto registrar o que foi escolhido. Isso evita que a equipe reabra discussões já encerradas sem novos elementos.
## Alternativa Rejeitada: GraphQL
- Motivo: complexidade excessiva para o caso de uso
- O time cliente consome exatamente 3 endpoints com schemas estáveis
- GraphQL adicionaria overhead de query parsing, caching e segurança
- Custo estimado de manutenção: 2x o esforço atual
## Alternativa Rejeitada: Event Sourcing
- Motivo: trade-off de consistência eventual inaceitável
- O fluxo de pagamentos requer consistência forte em 99.9% dos casos
- A complexidade de reconciliar eventos em caso de falha é alta
- Custo estimado de migração: 6 meses de trabalho dedicado
6. Ferramentas e Formatos para Versionamento da Documentação
A documentação de decisões deve viver próxima ao código que a implementa. A abordagem mais eficaz é manter ADRs em Markdown dentro do repositório, versionados pelo mesmo Git que gerencia o código fonte.
/docs/adrs/
├── ADR-001-paginacao-cursor.md
├── ADR-002-comunicacao-assincrona.md
├── ADR-003-versionamento-header.md
└── ADR-004-rate-limit-pagamentos.md
Para integrar com OpenAPI, é possível usar extensões customizadas que referenciam ADRs específicos:
openapi: 3.0.0
info:
title: Orders API
version: 1.0.0
paths:
/orders:
get:
x-adr-reference: ADR-001-paginacao-cursor.md
parameters:
- name: cursor
in: query
schema:
type: string
O uso de Conventional Commits permite automatizar changelogs que referenciam decisões de design:
feat(orders): implementa paginação por cursor (refs ADR-001)
7. Revisão Periódica e Evolução das Decisões Documentadas
Decisões de design não são imutáveis. À medida que o sistema evolui, novas features, mudanças de stack ou incidentes podem invalidar decisões anteriores. Estabeleça gatilhos claros para revisão:
- Nova feature: quando um novo endpoint ou funcionalidade depende de uma decisão anterior
- Mudança de stack: migração de banco de dados, linguagem ou infraestrutura
- Incidente: quando uma decisão de design contribuiu para um incidente em produção
Para marcar decisões como obsoletas, utilize o campo "Status" no ADR:
# ADR-001: Paginação offset (OBSOLETO)
## Substituído por: ADR-004
## Motivo: degradação de performance identificada em pico de 50k req/s
Métricas de saúde da documentação podem ser definidas como:
## Métricas de Cobertura
- Endpoints documentados com ADR: 15/18 (83%)
- Endpoints com trade-offs registrados: 12/18 (66%)
- ADRs atualizados nos últimos 6 meses: 8/12 (66%)
Documentar decisões de design e trade-offs em APIs internas não é um exercício acadêmico — é uma prática de engenharia que reduz custos operacionais, acelera onboarding e preserva conhecimento crítico. Invista nela como investiria em testes ou monitoramento: o retorno aparece na forma de menos incidentes, menos retrabalho e times mais autônomos.
Referências
- Architecture Decision Records (ADRs) - Documentação Oficial — Guia completo sobre o formato ADR, incluindo templates e exemplos de aplicação em projetos de software
- OpenAPI Specification - Extensions — Documentação oficial sobre como adicionar extensões customizadas ao schema OpenAPI para referenciar decisões de design
- REST API Design: Pagination Strategies — Artigo técnico comparando paginação por cursor vs. offset com exemplos práticos de implementação
- Documenting API Design Decisions - Martin Fowler — Artigo seminal sobre a importância de documentar decisões de design em arquiteturas de software
- Conventional Commits Specification — Especificação oficial para mensagens de commit que permite automação de changelogs e rastreabilidade de decisões
- Rate Limiting Strategies for APIs — Guia da Google Cloud sobre estratégias de rate limiting, incluindo exemplos de documentação de políticas de throttling
- OAuth 2.0 for Internal APIs — Tutorial sobre implementação de OAuth2 com client credentials flow para APIs internas