Knowledge

Idempotência como princípio de engenharia de sistemas

Por que repetir uma operação sem efeitos colaterais muda a confiabilidade, o custo operacional e a confiança em plataformas complexas.

23/02/202610 min de leituraKnowledge
Idempotência como princípio de engenharia de sistemas

Resumo executivo

Por que repetir uma operação sem efeitos colaterais muda a confiabilidade, o custo operacional e a confiança em plataformas complexas.

Ultima atualizacao: 23/02/2026

Introdução: A inevitabilidade da repetição

Em qualquer sistema distribuído, operações serão executadas mais de uma vez. Isso não é uma possibilidade; é uma certeza. Redes perdem pacotes. Servidores travam no meio de transações. Filas de mensagens entregam a mesma mensagem duas vezes. Clientes retentam requisições porque nunca receberam uma resposta.

Se um endpoint POST /pagamentos cria uma cobrança toda vez que é chamado, um simples timeout de rede se torna uma cobrança duplicada. Se um worker de fulfillment de pedidos processa a mesma mensagem duas vezes, o cliente recebe dois envios. Se um cron job de faturamento roda duas vezes por um erro do scheduler, faturas são duplicadas.

Idempotência é o princípio de engenharia que torna essas repetições inevitáveis inofensivas. Uma operação é idempotente se executá-la múltiplas vezes produz o mesmo resultado que executá-la uma vez. É a fronteira entre retries seguros e duplicação catastrófica.

Isso não é meramente uma preocupação de backend — é um princípio de engenharia de sistemas que afeta design de APIs, processamento de filas, operações de banco de dados e até submissões de formulários no frontend.

Idempotência no HTTP: O que a RFC já define

A RFC 9110 define formalmente quais métodos HTTP são idempotentes:

MétodoIdempotente?Seguro?Implicação
GETSimSimMúltiplas chamadas retornam o mesmo recurso. Sem efeitos colaterais.
HEADSimSimIgual ao GET mas retorna apenas headers.
PUTSimNãoSubstitui o recurso inteiro. Chamar duas vezes produz o mesmo estado final.
DELETESimNãoDeletar um recurso já deletado retorna 404 (ou 204). Mesmo estado final.
POSTNãoNãoCada chamada pode criar um novo recurso ou disparar um novo efeito colateral. É aqui que a engenharia de idempotência é crítica.
PATCHNãoNãoPode ou não ser idempotente dependendo da operação (ex: "setar campo X para 5" é idempotente; "incrementar campo X em 1" não é).

O desafio fundamental é que **a maioria das operações críticas de negócio usa POST** — criar pagamentos, registrar pedidos, enviar notificações — e POST é explicitamente não-idempotente por padrão. Você precisa projetar idempotência nessas operações.

O padrão Idempotency Key

O padrão consagrado pela indústria (popularizado pelo Stripe) é a Chave de Idempotência (Idempotency Key): um identificador único gerado pelo cliente enviado com a requisição, que o servidor usa para garantir execução at-most-once (no máximo uma vez).

Como funciona

POST /pagamentos
Idempotency-Key: pag_abc123_tentativa_1
Content-Type: application/json

{ "amount": 5000, "currency": "brl", "customer": "cli_xyz" }

Lógica do servidor:

typescriptasync function criarPagamento(req: Request) {
    const idempotencyKey = req.headers['idempotency-key'];

    // 1. Verificar se esta chave já foi processada
    const cached = await idempotencyStore.get(idempotencyKey);

    if (cached?.status === 'completed') {
        // Retornar a MESMA resposta da execução original
        return cached.response; // Exato mesmo status code, body, headers
    }

    if (cached?.status === 'processing') {
        // Outra requisição com esta chave está em andamento — retornar 409
        return new Response('Requisição em andamento', { status: 409 });
    }

    // 2. Travar a chave como "processing"
    await idempotencyStore.set(idempotencyKey, { status: 'processing' });

    // 3. Executar a lógica de negócio
    try {
        const pagamento = await stripe.paymentIntents.create(req.body);
        const response = { status: 201, body: pagamento };

        // 4. Armazenar a resposta para replays futuros
        await idempotencyStore.set(idempotencyKey, {
            status: 'completed',
            response,
            completedAt: new Date(),
        });

        return new Response(JSON.stringify(pagamento), { status: 201 });
    } catch (error) {
        await idempotencyStore.set(idempotencyKey, { status: 'failed', error });
        throw error;
    }
}

Decisões críticas de design

  1. O cliente gera a chave. O servidor não pode gerar chaves de idempotência porque o objetivo inteiro é deduplicar requisições que o servidor pode ver como distintas (dois POST com corpos idênticos mas conexões TCP diferentes).
  1. A resposta deve ser armazenada e replayada. Quando uma chave duplicada chega, o servidor deve retornar a _resposta exata_ (mesmo status code, mesmo body) da execução original. Retornar uma resposta diferente (como 409 Conflict) quebraria as expectativas do cliente.
  1. O modelo de três estados previne race conditions:
  • processing → Outra instância está executando. Rejeitar ou enfileirar.
  • completed → Retornar a resposta cacheada.
  • failed → Permitir retry (a chave pode ser reutilizada).
  1. O TTL deve cobrir a janela de reconciliação. Se você expira chaves de idempotência após 1 hora mas um processador de pagamento retenta após 2 horas, a deduplicação falha. O Stripe mantém registros de idempotência por 24 horas. A maioria dos sistemas financeiros deveria reter por 48-72 horas no mínimo.

Além de APIs: Idempotência em filas e cron jobs

Idempotência não se limita a APIs HTTP. Todo sistema que processa mensagens ou executa tarefas agendadas precisa de handlers idempotentes:

SistemaDesafio de IdempotênciaPadrão de Solução
Filas de Mensagens (SQS, Kafka)Garantias de entrega at-least-once significam que mensagens duplicadas são esperadas.Deduplicar por messageId ou por uma chave de nível de negócio (ex: orderId).
Cron JobsErros do scheduler, restarts de máquina ou sobreposições de deployment podem disparar execução dupla.Usar lock distribuído (ex: Redlock) e persistir registros de execução com timestamps.
Migrações de Banco de DadosUma migração aplicada duas vezes pode corromper dados.Usar versionamento de migrações (ex: Prisma, Flyway) com checksums.
Envio de Email/NotificaçãoEnviar o mesmo email de boas-vindas duas vezes é irritante; enviar uma fatura duplicada é um problema de compliance.Rastrear status sent por tupla (tipoNotificacao, recipientId, eventId).

Quando idempotência acelera a entrega

Tratar idempotência como princípio de engenharia de primeira classe reduz o custo de falha em todo o sistema:

  • Custo de reconciliação cai. Quando retries são seguros, não há necessidade de investigação manual cara após cada soluço de rede.
  • Confiança em retries aumenta. Desenvolvedores e sistemas consumidores podem retentar livremente (com backoff) sabendo que efeitos colaterais duplicados são impossíveis.
  • Incidentes financeiros diminuem. A classe mais cara de bugs — cobranças duplicadas, envios duplos, faturas fantasma — é estruturalmente prevenida.

Perguntas de decisão para o seu contexto de engenharia:

  • Quais operações com efeitos colaterais (pagamentos, envios, notificações) exigem chaves de idempotência obrigatórias?
  • Como você separa "em processamento" de "concluído" sob retries concorrentes?
  • Qual janela de retenção cobre atrasos realistas de rede e SLAs de integração com parceiros?

Roteiro de otimização contínua

  1. Mapeie todas as operações com efeitos colaterais críticos por nível de risco financeiro e reputacional. Priorize processamento de pagamentos, alterações de inventário e envio de notificações.
  2. Padronize o formato da chave de idempotência por domínio. Use uma estrutura previsível como {dominio}_{entityId}_{acao}_{timestamp}.
  3. Implemente persistência e replay de resposta por chave. Requisições deduplicadas devem retornar a _resposta exata_ da original.
  4. Defina TTL de retenção baseado em janelas reais de reconciliação. No mínimo 24 horas; 72 horas para operações financeiras.
  5. Alinhe estratégia de retry/backoff com garantias de idempotência. Clientes devem implementar backoff exponencial com jitter, sabendo que idempotência torna retries seguros.
  6. Monitore a taxa de duplicidade prevenida por operação. Essa métrica quantifica diretamente o valor do investimento em idempotência.

Como validar evolução em produção

Meça a eficácia da idempotência monitorando:

  • Efeitos colaterais duplicados prevenidos por operação crítica: Quantas cobranças, envios ou notificações duplas foram bloqueados?
  • Esforço de reconciliação manual após falhas: O tempo gasto investigando "isso realmente aconteceu duas vezes?" diminuiu?
  • Incidentes financeiros ligados a retries não-idempotentes: Zero é a meta. Qualquer número diferente de zero justifica investimento adicional.

Quer transformar esse plano em execução com previsibilidade técnica e impacto no negócio? Falar com especialista em arquitetura com a Imperialis para desenhar, implementar e operar essa evolução.

Fontes

Leituras relacionadas