Investigando erros em produção: o que fazer quando as coisas dão errado
Você recebe um alerta de erro às 3 da manhã. Nada de dramático, só um ticket aparecendo no sistema ou uma mensagem de stack trace num canal do Slack. O problema não é o erro em si — erro acontece todo dia. O problema é saber por que isso aconteceu de verdade, e não apenas aplicar um band-aid e torcer para não repetir.
A primeira coisa que ninguém faz: reconstruir o contexto
O erro é sempre um sintoma, nunca a causa raiz. Quando eu via uma exception genérica como "NullPointerException" ou "Timeout exceeded," meu instinto inicial era olhar pro log e tentar consertar o ponto de falha. Demorou uns seis meses pra eu perceber que isso era perda de tempo. A causa quase nunca estava no código que explodiu. Antes de mexer em qualquer linha de código, você precisa responder a três perguntas: o quê, quando e sob quais condições. Anote o horário exato do erro. Veja se coincidiu com deploy, com aumento de tráfego, com mudança de configuração. Eu já perdi meia manhã caçando um bug que na verdade era um timeout causado por uma query que foi reindexada no banco minutos antes. O código estava perfeito. O banco que mudou sem ninguém avisar.
Uma coisa que pouca gente leva a sério: cheque as dependências. Atualização automática de library, config de CDN que expirou, certificado TLS renovado mas com nome diferente. Pequenas coisas que quebram tudo de forma estranha.
Como funciona uma investigação prática
O método que funciona de verdade é mais chato do que heroico. Você pega o erro, traça o caminho que ele fez desde a entrada até o ponto de falha, e mapeia cada estado do sistema naquele momento. Isso significa variáveis de ambiente, carga do servidor, conexões ativas com o banco, filas de mensagem não processadas. No meu caso, a técnica que mais economiza tempo é o blast radius analysis. Você define exatamente o escopo do problema: quais usuários sentem, quais funcionalidades afetam, desde quando. Isso elimina 80% dos fatores de uma vez. Se só usuários do novo deploy sofrem, seu campo de busca tá reduzido drasticamente. Se afeta tudo igualmente, o problema é infraestrutura, não aplicação.
A ferramenta que eu uso pra isso é basicamente log aggregation com timestamps sincronizados — Kibana, Datadog, CloudWatch, o que sua stack tiver. A parte importante não é o tool em si, é ter logs estruturados com request ID que atravessa todos os serviços. Sem request ID correlacionado, você tá literalmente cego em sistemas distribuídos.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Um caso específico que aprendi na marra
Num sistema que eu maintainia, tínhamos um erro intermitente que aparecia umas duas vezes por dia. NUNCA no ambiente de staging. Sempre em produção, sempre no mesmo serviço, mas com comportamento diferente a cada vez: às vezes timeout, às vezes resultado corrompido, às vezes function abort silencioso. Passamos três semanas investigando. Código limpo. Testes passando. A causa raiz foi um race condition entre o garbage collector do JVM e um pool de conexões Redis que estávamos usando. O GC entrava em pause, o pool achava que as conexões estavam mortas e criava novas, mas o timeout de validação era menor que a latência normal de rede pro datacenter. O resultado era uma mistura aleatória de erros que pareciam não ter relação.
O workaround que funcionou foi duplo: aumentei o TTL das conexões no pool em 500ms e configurei o GC pra fazer pause menor com G1GC em vez do padrão. Aumentei também o timeout de validação de conexão. O erro parou de aparecer. Não foi uma mudança no código de negócio — foram ajustes de configuração e tuning de runtime.
O que iniciantes ignoram e profissionais não
Primeiro: erros intermitentes são mais caros que erros constantes. Um erro que reprodriz sempre você resolve uma vez e esquece. Um erro que aparece de três em três dias vai te assombrar por meses. Priorize a investigação assim que ele aparecer pela segunda vez, não quando virar um padrão. Segundo: a ausência de erro não prova nada. Muitos sistemas silently swallow exceptions. Se você vê zero erros num serviço mas o comportamento dos usuários tá errado, o problema é que seu error handling tá escondendo o defeito, não que não existe defeito. Cheque os métricas de negócio, não só os logs de exception.
Limitações honestas
Investigar erro não é ciência exata. Tem cenário onde simplesmente não dá pra saber o que aconteceu. Sistemas distribuídos com logging inconsistente, third-party services que não explicam o motivo do erro, dados corrompidos que já foram sobrescritos — nesses casos, a melhor resposta é "não conseguimos determinar a causa com os dados disponíveis." E você precisa ter coragem de dizer isso em vez de chutar uma causa raiz que não existe. Também é importante saber quando desistir. Se um erro custa menos pra contornar do que pra investigar profundamente — tipo, uma feature secundária que falha em condições extremas que nunca acontecem na prática — coloque um workaround, registre o problema e siga em frente. Engineering é sobre alocação de recursos, não sobre perfeição.
O que eu recomendo como alternativa quando a investigação tradicional falha: instrumentação. Não é sexy, mas monitoramento contínuo com alertas proativos resolve mais problemas antes que acontecem do que qualquer post-mortem. Você gasta horas adicionais de setup no começo, mas economiza dias de debugging depois. A regra prática que eu uso é: se um erro já aconteceu duas vezes, a terceira vez já devia ter alertado antes de explodir.