Se você trabalha com backend, APIs ou sistemas distribuídos, existe uma verdade inevitável que cedo ou tarde aparece em produção:
👉 Chamadas duplicadas vão acontecer.
Elas surgem por retry automático, timeout de rede, instabilidade de internet, falhas intermediárias, consumidores paralelos ou, simplesmente, pelo famoso “dedo nervoso” do usuário clicando várias vezes no mesmo botão.
E o problema não é a duplicação em si — o problema é o que o seu sistema faz quando ela acontece.
Neste artigo, vamos entender:
O que é idempotência (de verdade, sem buzzword)
Por que chamadas duplicadas são a regra, não a exceção
Exemplos reais de problemas causados por falta de idempotência
Estratégias para tratar duplicidade corretamente
Quando idempotência é obrigatória, desejável ou overengineering
O problema das chamadas duplicadas
Vamos começar com alguns exemplos comuns do mundo real.
- Retry e timeout no frontend
Imagine um endpoint POST /pagamentos.
O fluxo é simples:
O cliente chama a API
O backend processa o pagamento
O banco é atualizado
O cliente recebe a resposta
Agora adicione uma variável muito comum: internet instável.
O pagamento é processado com sucesso, mas o cliente não recebe a resposta por causa de um timeout. O frontend, bem programado, aplica uma política de retry e dispara a mesma requisição novamente.
Resultado:
O sistema “funcionou”
Não houve erro técnico
Mas o usuário pode ser cobrado duas vezes
Tecnicamente resiliente. Arquiteturalmente frágil.
- Confirmação de e-mail e tokens inválidos
Outro exemplo clássico: POST /usuarios/confirmar-email.
O usuário clica no botão para receber o token.
Clica de novo.
Clica de novo.
Clica de novo.
Cada clique gera um novo token, invalidando o anterior.
Resultado:
O usuário recebe vários e-mails
Nenhum token funciona
A experiência degrada
O suporte recebe chamados
Tudo isso porque o sistema assumiu que a chamada aconteceria uma única vez.
- Geração de relatórios pesados
Agora imagine um endpoint que gera um relatório complexo:
Várias queries
Processamento pesado
Geração de PDF
Se múltiplas requisições iguais chegam ao mesmo tempo, você pode:
Saturar o banco
Travar o sistema
Gerar o mesmo relatório várias vezes desnecessariamente
O erro conceitual por trás de tudo isso
Esses exemplos têm algo em comum:
Assumem que a chamada acontece uma única vez
Geram efeitos colaterais indesejados quando isso não acontece
Em ambientes distribuídos, isso nunca é uma premissa válida.
É aqui que entra o conceito de idempotência.
O que é idempotência?
O termo vem da matemática, mais especificamente da álgebra abstrata.
Uma operação idempotente é aquela que:
não importa quantas vezes seja aplicada com as mesmas entradas, o resultado final é sempre o mesmo.
Exemplos do mundo real:
Botão de elevador
Botão de emergência
Botão de “parar” de uma máquina
A expectativa é simples:
👉 apertar uma vez ou dez vezes produz o mesmo efeito.
Em APIs e backends, deveria ser exatamente assim.
Idempotência em sistemas
Aplicar idempotência significa garantir que:
Chamadas duplicadas não quebram o sistema
Efeitos colaterais não se repetem
O estado só muda quando realmente precisa mudar
Gerar um token?
➡️ Apenas uma vez dentro da regra definida.
Criar um pagamento?
➡️ Apenas uma vez para aquela transação.
Gerar um relatório?
➡️ Apenas uma vez para aquele conjunto de parâmetros.
Como identificar chamadas duplicadas?
Antes de tratar duplicidade, precisamos responder uma pergunta fundamental:
Como saber que duas chamadas são, de fato, a mesma operação?
Existem várias estratégias, mas duas são as mais comuns.
- Identidade lógica (baseada no domínio)
Aqui, usamos dados do próprio negócio para garantir unicidade.
Exemplos:
CPF
Data/hora
Tipo de operação
Estado atual
Exemplo prático:
Um token de confirmação só pode ser gerado a cada 5 minutos para o mesmo e-mail
Se uma nova requisição chega dentro desse intervalo, reutilizamos o token existente
Vantagens:
Simples de entender
Alinhada ao domínio
Pode usar índices únicos no banco
Pouca ou nenhuma mudança no payload
Desvantagens:
Regras precisam estar muito bem alinhadas entre sistemas
Mudanças exigem coordenação entre times
Acoplamento forte ao domínio
- Chave de idempotência
Aqui, o cliente gera um identificador único e envia junto com a requisição.
Essa chave:
Identifica a operação
Independe dos dados de negócio
É armazenada no backend junto com o estado da operação
Fluxo típico:
O cliente gera uma chave
Envia a requisição com essa chave
O backend verifica se ela já existe
Decide se processa, reutiliza ou ignora
Vantagens:
Menos acoplamento ao domínio
Muito comum em APIs públicas e pagamentos
Excelente para retrys automáticos
Cuidados:
A chave precisa ser persistida
O cliente precisa armazená-la corretamente
Exige storage, locks e controle de estado
Quando idempotência é obrigatória?
Idempotência não é “nice to have” em alguns cenários — é requisito de negócio.
Ela é obrigatória quando:
Há efeitos colaterais relevantes
Envolve dinheiro, contratos ou confiança
O sistema é distribuído
Existe mensageria, filas, eventos e consumidores paralelos
Se repetir a operação causa prejuízo ou caos, idempotência é obrigatória.
Quando idempotência é desejável?
Ela é altamente recomendada quando:
Existe retry automático
O sistema lida com redes instáveis
O estado final não pode ser repetido
A operação representa uma transição de estado
Exemplos:
Confirmação de e-mail
Ativação de conta
Finalização de pedido
Cancelamento de assinatura
Quando idempotência pode ser overengineering?
Nem tudo precisa ser idempotente.
Alguns exemplos:
Operações somente leitura
Geração de logs
Métricas
Auditoria
Eventos que devem ser repetidos
Se:
O impacto da duplicação é baixo
O custo de implementação é alto
Não há identidade clara da operação
Talvez o risco seja aceitável.
Arquitetura também é saber onde não colocar complexidade.
E quanto ao REST?
De forma geral:
GET → naturalmente idempotente
PUT → geralmente idempotente
POST → geralmente não idempotente
Mas isso não é regra escrita em pedra.
Tudo depende:
Do domínio
Dos efeitos colaterais
Das garantias que seu sistema precisa oferecer
Conclusão
Chamadas duplicadas não são exceção.
Elas são a regra em sistemas reais.
Retry, timeout, instabilidade, falhas intermediárias e comportamento humano fazem parte do jogo.
Nosso papel como arquitetos e desenvolvedores é:
Antecipar esse cenário
Tratar duplicidade corretamente
Proteger o estado do sistema
Evitar efeitos colaterais indesejados
Idempotência é uma decisão de arquitetura, não um detalhe de implementação.
Ela tem custo.
Ela tem complexidade.
E exatamente por isso não deve ser aplicada cegamente.
Garantir idempotência é garantir que o estado do sistema só muda quando realmente precisa mudar.
