Projete para variância, não para a média
Meça DNS, conexão, TLS, upload, processamento, first byte, download, perda, reconexões e transições de rádio. Segmente por país, operadora/ASN, rede, app, dispositivo, endpoint, payload e tentativa. p50 esconde quem cruza o timeout repetidamente.
RTT altoCada handshake e dependência sequencial custa caro. Reuse conexão, reduza chatty calls e faça batch com limite.
Perda e troca de rotaA conexão trava ou reseta e o cliente não sabe se o write foi confirmado.
Banda variávelResposta pequena funciona; mídia ou lista ilimitada falha no meio.
Disponibilidade intermitenteO app alterna entre online, captive, degradado, background e offline; a intenção sobrevive ao processo.
Dê um deadline ponta a ponta
A operação precisa de um orçamento, não de 30 segundos em cada hop. Reserve DNS/connect/TLS, primeira tentativa, retry opcional, parsing e feedback. Propague o saldo para downstream e cancele trabalho abandonado. Separe timeout de conexão de request/idle timeout.
DNS
Conectar
TLS
Try 1
Jitter
Try 2
Limite
Parse
UI
Retry depende de método, falha e orçamento
Retry GET/HEAD em falha transitória. PUT/DELETE podem ser idempotentes por contrato, mas a implementação ainda pode disparar efeitos externos. Não repita POST/PATCH automaticamente sem contrato de idempotência. Validação, autenticação, autorização, conflito e not-found permanente não são perda de pacote.
Backoff exponencial limitado com full jitterT+0Tentativa 1Envie request ID, deadline e idempotency key quando necessária.
falha transitória → aleatório 0..base
T+Δ1Tentativa 2Reuse o ID lógico e pare se orçamento/política não permitirem.
aleatório 0..min(cap, base × 2²)
T+Δ2Tentativa finalRetorne estado recuperável, enfileire ou mostre falha. Nunca repita indefinidamente.
Coloque retry em uma camada
SDK 3x, gateway 3x e serviço 3x transformam uma ação em 27 chamadas. Escolha a camada que conhece semântica e budget. Use retry quota/token bucket, limite de tentativas/atraso, full jitter, Retry-After e desligamento em falha sustentada.
| Resultado | Interpretação | Política | Contrato do servidor | Evitar |
|---|
| Conexão perdida sem resposta | Outcome do write desconhecido. | Retry só se seguro/idempotente ou keyed. | Status consultável pela key/ID. | Criar compra duas vezes. |
| 408 / timeout | Não concluiu no contexto. | Retry limitado se houver budget. | Parar trabalho abandonado e deduplicar. | Reiniciar operação longa. |
| 429 / quota | Chamador excedeu política. | Respeitar tempo e modelar tráfego. | Publicar política legível por máquina. | Todos repetirem juntos. |
| 502/503/504 | Upstream, overload ou gateway transitório. | Retry pequeno com jitter; fallback de leitura. | Load shed cedo e Retry-After. | Retry storm. |
| 409 conflict | Estado atual rejeita mudança. | Buscar e reconciliar. | Problem details e revisão atual. | Sobrescrever concorrência. |
| 4xx validação/auth | Request ou autoridade deve mudar. | Sem retry de transporte. | Tipo, campos, correção e request ID. | Gastar bateria repetindo. |
Idempotência é máquina de estados no servidor
O cliente cria key de alta entropia vinculada a principal e rota. O servidor guarda key, fingerprint, estado, ID da operação/recurso, resultado, timestamps e expiração. Mesma key/payload retorna o outcome original; payload diferente é rejeitado. Duplicatas concorrentes aguardam ou recebem “em andamento”. Persista registro e efeito atomicamente.
Trabalho longo é assíncrono
Retorne 202 Accepted com URL de operação. Exponha pending/running/succeeded/failed/cancelled, timestamps, resultado e problem detail. Faça polling com backoff e ETag ou entregue evento assinado. Não mantenha conexão móvel aberta durante export de minutos.
Paginação começa com ordem estável
Todo list nasce paginado. O cursor vincula query, filtros, sort, snapshot/boundary, direção e última tupla estável, como (updated_at, id). Tokens são opacos e protegidos. Offset serve para conjuntos pequenos/estáticos; coleções mutáveis geram duplicatas e lacunas.
Permita buscar menos
Use page size limitado, field masks, recursos resumidos, filtros, delta sync, updated_since com semântica estável e mídia separada. O cliente precisa carregar a próxima tela acionável, não todo o grafo de domínio.
Compressão é negociada e medida
Suporte Accept-Encoding e Vary: Accept-Encoding. Comprima JSON, texto, protobuf e SVG acima de threshold medido. Não recomprima JPEG/AVIF/vídeo. Compare Brotli, gzip e Zstandard por tamanho, CPU, bateria e latência. Streaming não deve bufferizar tudo.
Cache e revalidação evitam download
Use Cache-Control, ETag, If-None-Match e Last-Modified. Separe conteúdo público e personalizado, configure Vary e mostre freshness. Dados stale só quando regra permitir, com as_of e ações restritas. Cache-Status ajuda no diagnóstico.
Transferências grandes são retomáveis
Upload em partes usa ID, offset/part, checksum, expiração, metadados e finalize explícito. Download usa ranges e validators. Repita apenas partes ausentes. tus é uma opção estabelecida. Não conclua antes de validar tamanho e digest.
Degradação é contrato de produto
Sirva menosPágina menor, summary, sem thumbnail, precisão reduzida, enriquecimento adiado ou cache.
Enfileire intençãoComando local com operation ID, key, versão, expiração, estado e política de conflito.
Bloqueie ação arriscadaSe auth, preço, estoque, saldo ou segurança estão stale, permita leitura e bloqueie write irreversível.
Priorize tráfego centralAuth, segurança, confirmação financeira, metadados de sync e texto antes de analytics e imagem.
Faça load sheddingRejeite trabalho opcional cedo e preserve capacidade crítica.
Mostre estado verdadeiroOffline, sincronizando, pendente, stale, falhou, conflito e confirmado são diferentes.
Use erros legíveis por máquina
RFC 9457 define type, title, status, detail e instance. Adicione código estável, violações, retryability, correlation ID e remediação segura. Não exponha stack nem obrigue parsing de texto traduzido.
Transporte não substitui semântica
HTTP/3 sobre QUIC pode melhorar perda e mudança de conexão; prioridade HTTP sinaliza urgência. Reuso, TLS resumption, edge e menos chamadas sequenciais também ajudam. Nada disso evita duplicidade, lista ilimitada, erro ambíguo ou write stale.
Observe tentativas, não só requests
Trace uma operação lógica por operation ID e request ID por tentativa. Registre timings, bytes, compressão, cache, rede, fase do timeout, retry, idempotência, cursor, stale mode, fila e outcome. Meça amplificação, supressão de duplicatas, conclusão por rede, dados/bateria e p95/p99.
O que eu construiria
SDK compartilhado com deadline, timeouts, classificação, backoff full-jitter, quota, keys, polling, cursor, cache condicional, upload retomável, outbox local, conflitos e telemetria. No servidor: storage de idempotência, problem details, load shedding, paginação, field masks, operações assíncronas, ETags, cancelamento e traces por tentativa.
O princípio
API confiável não finge que a rede é confiável. Torna outcome incerto recuperável, retry limitado, write deduplicado, transferência retomável e estado degradado visível para a próxima decisão correta.