Conceitos
Design de API: idempotência, paginação e evolução de contrato
Intuição
Uma API é um contrato entre quem presta um serviço e quem o usa, e a rede entre os dois falha: o pedido pode se perder, a resposta pode se perder, e quem pediu só sabe tentar de novo. Pense em apertar o botão do elevador: apertar duas vezes não chama dois elevadores, o resultado é o mesmo de apertar uma. Uma boa API é desenhada assim: repetir um pedido é seguro, ler uma lista longa em páginas não perde nem repete itens mesmo que a lista mude durante a leitura, e o serviço pode melhorar com o tempo sem quebrar quem já o usa.
Como funciona
A semântica dos métodos HTTP (RFC 9110) já carrega parte do contrato: GET, HEAD e OPTIONS são seguros (não alteram estado), e GET, HEAD, PUT e DELETE são idempotentes (executar N vezes tem o mesmo efeito de executar uma), enquanto POST e PATCH não o são por padrão. Para operações não idempotentes que não podem ser duplicadas, como cobrar um pagamento, o cliente gera uma chave de idempotência única por intenção e a envia num header; o servidor grava a chave junto com o resultado da primeira execução, de forma atômica com o efeito, e, ao receber a mesma chave de novo, devolve o resultado guardado em vez de executar outra vez (e rejeita a mesma chave com um corpo diferente). Isso é o que torna seguro o retry com entrega at-least-once: o cliente repete até obter resposta, e o servidor deduplica, o mesmo raciocínio que exige passos e compensações idempotentes numa saga. Na paginação, a de offset (página N = pular N itens) é simples, mas inserções ou remoções durante a leitura deslocam os itens e causam repetições ou omissões, além de custar mais em offsets grandes. A paginação por cursor devolve um token opaco que codifica a posição do último item visto, numa ordenação estável e única (por exemplo chave de ordenação mais id), e a próxima página começa logo após essa posição, então inserções antes dela não deslocam nada. Na evolução do contrato, mudanças compatíveis (adicionar campo opcional na resposta, novo endpoint, novo parâmetro opcional) não quebram clientes que ignoram campos desconhecidos; mudanças incompatíveis (remover ou renomear campo, mudar tipo ou semântica, tornar obrigatório um parâmetro) exigem uma nova versão, seja no caminho da URL, num header ou num media type, com período de depreciação. Por fim, o contrato inclui o comportamento sob falha e sobrecarga: um rate limit responde 429 com Retry-After para o cliente recuar, e erros seguem um formato consistente, com status HTTP correto, um código estável legível por máquina e uma mensagem para humanos, distinguindo erros que valem retry (5xx, 429) dos que não valem (4xx de validação).
Na prática
Em qualquer API pública ou entre serviços, as regras práticas são: tornar idempotentes as operações com efeito colateral caro (pagamentos, criação de pedidos, envio de mensagens) com chave de idempotência, usar cursor em coleções grandes ou que mudam o tempo todo, e tratar o schema publicado como promessa, evoluindo por adição e versionando só quando inevitável. Referências canônicas: a RFC 9110 para a semântica de métodos, status e cabeçalhos; a tese de REST de Fielding para a motivação de interface uniforme e sem estado; e a documentação de idempotency keys de uma API de pagamentos como modelo de como um servidor deduplica retries. Esse é o conceito a escolher quando o cliente é um terceiro que você não controla, onde cada quebra de contrato vira incidente do lado dele.
Use em um desenho
Pratique na escola de System Design os casos que dependem deste conceito.