Debug de automações intermitentes: como caçar o bug que só aparece quando você vai dormir
Debug de automações intermitentes não é luxo de arquiteto que gosta de desenhar caixinhas no Miro. É o tipo de cuidado que separa uma automação útil de uma bomba-relógio educada. A bomba-relógio, claro, ainda manda notificação bonitinha antes de explodir. Ela só não entrega o trabalho.
Eu aprendi isso do jeito menos elegante: integração rodando linda por semanas, até uma API devolver 429, um webhook atrasar, o token expirar e a planilha receber três linhas duplicadas como se estivesse fazendo arte contemporânea. A primeira reação foi culpar a API. A segunda, mais honesta e bem mais irritante, foi admitir que a automação tinha sido desenhada como se o mundo fosse estável. O mundo não é estável. O mundo é uma tomada frouxa.
Neste guia, vou mostrar uma arquitetura prática de debug de automações intermitentes para automações pessoais, operações internas e pequenos produtos digitais. A ideia é simples: todo evento importante entra numa fila local, ganha estado, tentativa, log e critério de reprocessamento. Se o serviço externo falhar, o processo não se perde. Ele espera, tenta de novo, registra o motivo e permite intervenção humana sem humilhação pública.

1. O problema real: automação sem memória vira teatro
A maioria das automações começa como um fluxo inocente: formulário recebido, chamada para API, resposta formatada, mensagem enviada. Em diagrama, parece uma esteira limpa. Na vida real, cada etapa tem o charme de um elevador antigo: funciona até o dia em que decide parar entre andares.
O erro comum é tratar cada execução como um evento descartável. Se deu certo, ótimo. Se deu errado, alguém olha o log bruto, xinga baixo e tenta apertar “run again”. Isso até funciona quando há cinco eventos por semana. Com cinquenta por dia, vira trabalho manual fantasiado de automação.
A automação robusta precisa responder quatro perguntas:
- O que chegou?
- Em que estado está?
- Quantas vezes foi tentado?
- Qual foi o último erro útil?
Sem essas respostas, você não tem sistema. Tem esperança com webhook.
2. A peça central: uma fila local pequena e honesta
Quando falo em fila local, não estou exigindo Kafka, Kubernetes e uma reunião semanal só para discutir nomes de tópicos. Para muita automação, SQLite, Postgres, Redis ou até uma tabela no banco da aplicação resolvem muito bem. O ponto não é ostentar infraestrutura. O ponto é persistir intenção.
Em vez de processar tudo imediatamente dentro do webhook, você registra um item:
{
"id": "evt_20260530_001",
"type": "lead.created",
"payload": { "email": "cliente@exemplo.com", "source": "landing-page" },
"status": "pending",
"attempts": 0,
"next_run_at": "2026-05-30T21:05:00Z",
"last_error": null
}
Depois, um worker separado pega itens pendentes, processa e atualiza o estado. Essa separação muda tudo. O webhook só precisa receber e salvar. O processamento pesado acontece fora da linha de tiro.
3. Estados mínimos para não enlouquecer
Você não precisa de uma máquina de estados barroca. Precisa de nomes que digam a verdade. Eu costumo começar com cinco:
- pending: recebido, ainda não processado.
- running: está em execução agora.
- done: concluído com sucesso.
- retry: falhou, mas pode tentar novamente.
- dead: falhou além do limite ou exige ação humana.
Esse vocabulário já evita uma categoria inteira de sofrimento. Quando alguém pergunta “o que aconteceu com aquele pedido?”, você não precisa consultar três dashboards, uma thread de Slack e a intuição. Você consulta a fila.
Box perrengue: o bug mais caro não é o que falha sempre. É o que falha uma vez a cada 73 execuções, apenas quando a API demora mais de 12 segundos e o usuário aperta enviar duas vezes. Se você não registra estado e idempotência, esse bug vira folclore corporativo.
4. Idempotência: a palavra feia que salva seu sábado
Idempotência significa poder repetir uma operação sem criar efeito duplicado. Em automações, isso é fundamental porque retentativas são parte do jogo. Se você vai tentar de novo, precisa garantir que a segunda tentativa não cobre o cliente duas vezes, não envie três e-mails e não crie clones do mesmo registro.
O jeito prático é definir uma chave única de operação. Pode ser o ID do evento recebido, o e-mail combinado com timestamp arredondado, o identificador do pedido ou um hash do payload.
CREATE TABLE automation_jobs (
id TEXT PRIMARY KEY,
idempotency_key TEXT UNIQUE NOT NULL,
type TEXT NOT NULL,
payload JSONB NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
attempts INTEGER NOT NULL DEFAULT 0,
next_run_at TIMESTAMP NOT NULL DEFAULT NOW(),
last_error TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
Com essa restrição, a fila recusa duplicatas. A aplicação pode responder “já recebi isso” e seguir a vida. Elegante? Sim. Glamoroso? Não. Funciona? Aí está a parte importante.
5. Retentativas com backoff, não com desespero
Quando uma chamada externa falha, muita gente tenta novamente imediatamente. Isso parece diligência, mas às vezes é só bater na porta de alguém que acabou de dizer que está ocupado. Se a API está em rate limit, insistir em milissegundos só piora.
Use backoff exponencial com limite:
function nextDelayMinutes(attempts) {
const base = Math.pow(2, attempts); // 1, 2, 4, 8...
const jitter = Math.floor(Math.random() * 3);
return Math.min(base + jitter, 60);
}
O jitter impede que todos os jobs acordem ao mesmo tempo e ataquem a API como uma liquidação de madrugada. O limite máximo evita que um erro temporário vire esquecimento eterno.
6. O worker: pequeno, previsível e sem heroísmo
O worker deve fazer poucas coisas e fazer bem:
- Buscar jobs vencidos com status
pendingouretry. - Marcar como
runningantes de processar. - Executar uma unidade de trabalho.
- Salvar resultado ou erro.
- Calcular próxima tentativa quando fizer sentido.
def process_job(job):
try:
mark_running(job.id)
handler = handlers[job.type]
handler(job.payload, idempotency_key=job.idempotency_key)
mark_done(job.id)
except TemporaryError as err:
schedule_retry(job.id, str(err))
except Exception as err:
mark_dead(job.id, str(err))
Observe a divisão entre erro temporário e erro fatal. Falha de rede, 429 e timeout costumam merecer nova tentativa. Payload inválido, credencial ausente e regra de negócio quebrada geralmente precisam parar. Tentar de novo um CPF inválido não o torna mais válido. Eu gostaria que tornasse; facilitaria bastante a vida.

7. Observabilidade que serve para gente cansada
Logs não são um depósito de frases crípticas para impressionar dev sênior. Logs são ferramentas de recuperação. Quando algo falha, a pessoa que está olhando precisa entender o que aconteceu, com qual entrada, em qual tentativa e qual ação tomar.
Registre pelo menos:
- ID do job.
- Tipo do evento.
- Chave de idempotência.
- Status anterior e novo.
- Tempo de execução.
- Erro normalizado.
- Resposta externa relevante, sem vazar segredo.
O cuidado com segredo é importante. Não jogue token, app password, CPF, cartão ou conteúdo sensível em log. O log precisa explicar o problema, não virar um vazamento com timestamps.
8. Dead letter queue: o cemitério que precisa de porteiro
Todo sistema sério precisa de uma área para itens mortos. Não porque gostamos do nome dramático, mas porque alguns jobs não devem continuar tentando para sempre. Depois de cinco, sete ou dez tentativas, talvez seja hora de admitir que a automação precisa de ajuda.
Uma boa tela ou consulta de dead letter mostra:
- Quando o job entrou.
- Quantas tentativas ocorreram.
- Último erro.
- Payload resumido.
- Botões ou comandos para reprocessar, editar payload ou arquivar.
Esse ponto muda a relação da equipe com automação. Em vez de “sumiu”, você tem “parou aqui por esse motivo”. Parece pouco, mas é a diferença entre operar e adivinhar.
9. Webhooks: receba rápido, processe depois
Webhooks devem ser tratados como entregadores apressados. Receba, valide assinatura, grave o evento e responda logo. Não faça chamada para cinco serviços dentro da requisição original. Essa é uma receita clássica para timeout, duplicidade e lágrimas discretas.
Fluxo recomendado:
- Validar assinatura ou token do webhook.
- Gerar ou extrair chave de idempotência.
- Persistir payload bruto e normalizado.
- Responder
202 Accepted. - Deixar o worker processar.
Se você publica ou opera WordPress, vale cruzar isso com práticas de API e segurança. A categoria Log de Erros tem espaço para esse tipo de engenharia prática, e textos como Como criar um watchdog de automações que detecta falhas antes do cliente reclamar, Backup imutável para pequenos negócios: o plano anti-ransomware que cabe no bolso e Painel pessoal de automações: como saber o que seus robôs fizeram sem virar babá de script ajudam a montar o repertório.
10. Como começar sem transformar tudo em plataforma
Comece com uma automação dolorida, não com uma arquitetura abstrata. Escolha um fluxo que já falhou ou que causaria prejuízo se falhasse: leads, cobrança, publicação, onboarding, suporte, backup, relatório financeiro. Depois implemente a fila mínima.
Checklist de implementação
- Crie tabela de jobs com status, tentativas e erro.
- Adicione chave de idempotência única.
- Faça o webhook apenas validar e salvar.
- Rode um worker a cada minuto via cron, systemd timer ou processo dedicado.
- Implemente backoff exponencial com jitter.
- Separe erro temporário de erro fatal.
- Crie uma consulta para jobs mortos.
- Documente como reprocessar com segurança.
Não precisa nascer perfeito. Precisa nascer observável. Depois você melhora painel, métricas, alertas e ergonomia. Antes disso, garanta que o evento não desaparece.
11. Métricas que realmente importam
Métrica boa muda comportamento. Para esse tipo de automação, eu acompanharia:
- Jobs criados por hora.
- Tempo médio até conclusão.
- Taxa de retry.
- Quantidade em dead letter.
- Erros por serviço externo.
- Jobs mais antigos ainda pendentes.
Esses números dizem se a automação está saudável ou só está quieta. Silêncio não é estabilidade. Às vezes é só o alarme quebrado, o que é uma forma bem passivo-agressiva de infraestrutura.
12. O ganho invisível: confiança operacional
O maior benefício de debug de automações intermitentes não é técnico. É psicológico. Quando você sabe que cada evento tem estado, histórico e reprocessamento, para de operar no modo susto. Dá para mexer, evoluir, trocar APIs e dormir com menos abas abertas na cabeça.
Também fica mais fácil delegar. Uma automação sem rastreabilidade depende do criador original como sacerdote do caos. Uma automação com fila, logs e estados pode ser entendida por outra pessoa. Isso é maturidade, mesmo quando o sistema ainda é pequeno.
Conclusão: automação boa sabe falhar
Automação boa não é a que nunca falha. Essa não existe, ou existe apenas no slide de venda. Automação boa é a que falha de forma legível, segura e recuperável. Ela registra o que aconteceu, tenta novamente quando faz sentido e chama um humano quando a situação pede.
Se você está construindo fluxos com APIs, webhooks, agentes de IA ou integrações entre ferramentas, coloque uma fila local antes que a falta dela cobre juros. Comece pequeno: uma tabela, cinco estados, backoff e idempotência. O resto vem depois.
Agora me diga: qual automação você quer ver desmontada e reconstruída aqui na AutoMente? Pode ser triagem de e-mail, publicação automática, CRM pessoal, agente de pesquisa, backup, financeiro ou aquela gambiarra que está funcionando há meses e por isso mesmo já merece respeito e medo.
