Um método prático para reproduzir falhas, testar hipóteses e corrigir a causa do problema com evidências.
Um formulário falha em alguns cadastros, mas funciona quando você tenta novamente. Você altera a validação, aumenta o tempo limite e modifica uma consulta. O erro desaparece. Qual dessas mudanças resolveu o problema? E como saber se ele voltará?
Aprender como depurar erros exige transformar esse tipo de situação em uma investigação verificável. O caminho é registrar o sintoma, reproduzir a falha, reunir evidências e testar explicações antes de corrigir a implementação.
Neste tutorial, vamos acompanhar um formulário que apresenta erro ao cadastrar determinados e-mails. O exemplo mostra um processo que também pode orientar a investigação de falhas em páginas, integrações e outras aplicações web.
Por que alterar código por tentativa e erro dificulta a depuração
Cada alteração modifica as condições da investigação. Quando você muda três partes do sistema de uma vez, perde a capacidade de associar o resultado a uma causa específica. Uma mudança pode resolver o problema, outra pode ser irrelevante e uma terceira pode introduzir um defeito.
Também é possível que nenhuma delas tenha corrigido a falha. Um cache expirou, os dados usados no teste mudaram ou a requisição terminou em outra ordem. O desaparecimento do sintoma, sozinho, não comprova a correção.
No formulário do exemplo, aumentar o tempo limite não explica por que apenas determinados cadastros falham. Antes dessa alteração, precisamos descobrir o que diferencia os envios bem-sucedidos dos demais.
Separe três elementos durante a depuração de código:
- Sintoma: o formulário apresenta uma mensagem de erro.
- Hipótese: a falha ocorre quando o e-mail já está cadastrado.
- Correção: tratar adequadamente o conflito de cadastro, se a hipótese for confirmada.
Registre a versão inicial e mantenha os experimentos rastreáveis no controle de versão. Logs temporários e pequenas alterações são úteis quando respondem a uma pergunta definida e podem ser removidos depois.
A organização da implementação também influencia a investigação. Os princípios SOLID com exemplos práticos ajudam a entender como responsabilidades bem delimitadas podem facilitar a manutenção e a análise de mudanças.
Registre o erro e construa uma reprodução confiável
Um relato como “o cadastro às vezes quebra” ainda não permite testar uma explicação. Comece descrevendo o comportamento esperado e o observado, com os passos necessários para chegar ao resultado.
Use uma ficha de reprodução:
- Resultado esperado: cadastrar um novo usuário ou informar uma condição prevista que impeça o cadastro.
- Resultado observado: mensagem genérica e resposta HTTP 500.
- Passos e entradas: abrir o formulário, preencher os campos e enviar.
- Contexto: versão da aplicação, navegador, ambiente e configuração relevante.
- Ocorrência: horário com fuso, mensagem exata e frequência nas tentativas realizadas.
Faça a reprodução em um ambiente de teste, com dados sintéticos ou anonimizados. Preserve as características necessárias ao problema: se o comportamento depende de um endereço duplicado, os dados de teste precisam manter essa duplicidade.
Compare um caso que falha com outro que funciona
No nosso exemplo, um e-mail novo funciona. Um endereço já cadastrado falha. A reprodução mínima passa a ser criar um cadastro e depois tentar outro com o mesmo e-mail, mantendo os demais campos válidos.
Reduza os elementos aos poucos. Remova campos opcionais, etapas e dependências dispensáveis, verificando a cada redução se o erro continua acontecendo. Esse é o princípio de um exemplo mínimo reproduzível.
Registre também quantas tentativas deram certo e quantas falharam. Uma ocorrência em dez envios exige uma investigação diferente de dez falhas consecutivas. Se a reprodução ainda for ocasional, documente essa limitação.
Use mensagens, stack traces e logs para localizar a falha
Antes de acrescentar instrumentos, examine as informações disponíveis. No navegador, a aba de rede permite observar a requisição enviada, o status HTTP e a resposta recebida. No servidor, procure os registros correspondentes à mesma operação.
Uma resposta HTTP 500 indica um erro no servidor, mas não identifica sua causa. No exemplo, o registro da requisição revela uma exceção associada à restrição de unicidade do e-mail. Essa informação direciona a investigação para a tentativa de cadastrar um endereço existente.
Leia a stack trace além da última linha
A stack trace apresenta os caminhos de chamada relacionados à exceção. Procure funções e arquivos da aplicação, leia a mensagem completa e examine exceções encadeadas quando existirem.
O ponto em que o erro aparece pode estar distante da decisão que criou a condição problemática. A gravação no banco pode lançar a exceção, enquanto a aplicação falha por não tratar um conflito esperado naquele fluxo.
Correlacione os registros por identificador de requisição e horário com fuso. Em sistemas com várias instâncias, só o horário pode não distinguir operações simultâneas.
Se faltar informação, adicione um log pontual que responda a uma pergunta. Por exemplo: qual operação estava em execução quando o conflito foi detectado? Prefira campos como identificador da requisição, operação, duração e categoria do erro. Evite registrar senhas, tokens e dados pessoais desnecessários.
A observabilidade com métricas, logs e rastreamento amplia essa visão ao conectar evidências de diferentes partes da aplicação. Na depuração com logs, cada registro precisa ajudar a reconstruir o fluxo investigado.
Formule hipóteses e teste uma variável por vez
Uma hipótese útil descreve uma condição e permite prever um resultado. “O banco está com problema” é amplo demais. “O envio falha quando o e-mail já existe” pode ser testado diretamente.
Escreva a previsão antes do experimento. Isso reduz a tendência de interpretar qualquer resultado como confirmação da sua ideia inicial.
| Elemento | Registro |
|---|---|
| Evidência | O servidor registrou uma violação de unicidade no cadastro. |
| Hipótese | A falha depende de o e-mail já estar cadastrado. |
| Experimento | Comparar envios equivalentes com um endereço existente e outro novo, controlando o estado inicial dos dados. |
| Resultado esperado | O endereço existente provoca o conflito; o novo permite o cadastro. |
| Resultado observado | Preencher depois da execução, incluindo qualquer divergência. |
Manter uma variável por vez exige controlar também o estado do sistema. Um envio bem-sucedido cria um cadastro: na próxima tentativa, aquele e-mail já não será novo. Prepare os dados antes de cada execução ou use endereços sintéticos distintos para os casos válidos.
Comece por hipóteses compatíveis com os registros e por experimentos simples que consigam descartá-las. Se um endereço novo também falhar, a hipótese inicial não explica todos os casos e precisa ser revista.
Registre os resultados negativos e reverta alterações experimentais descartadas. Esse histórico evita repetir tentativas e permite compartilhar a investigação sem depender da memória de quem a executou.
Isole a causa com breakpoints e redução do cenário
Quando os registros delimitarem o caminho suspeito, use um depurador para acompanhar a execução. Posicione breakpoints perto da primeira etapa em que o comportamento pode divergir do esperado.
No formulário, um ponto de parada antes da gravação permite inspecionar os dados recebidos. Outro no tratamento de exceções ajuda a verificar como a aplicação reage ao conflito retornado pelo banco.
Um breakpoint condicional pode interromper apenas a execução associada ao endereço sintético de teste. Assim, você acompanha o caso relevante sem parar em todos os cadastros.
- Confira os valores recebidos e as transformações aplicadas.
- Acompanhe a pilha de chamadas para entender de onde veio a operação.
- Avance até o primeiro estado diferente do previsto.
- Verifique qual camada deveria tratar essa condição.
Se a execução confirma uma tentativa de gravação duplicada e uma exceção sem tratamento adequado, a investigação fica mais precisa: a restrição do banco está protegendo a integridade dos dados, mas o fluxo não converte o conflito em uma resposta prevista.
Continue reduzindo o cenário se houver componentes demais envolvidos. Ao substituir uma dependência por uma simulação, confirme que a simulação preserva o comportamento relevante. Um substituto que sempre retorna sucesso elimina justamente a evidência que você precisa observar.
Em problemas de concorrência, as pausas do depurador podem mudar a ordem das operações e fazer o erro desaparecer. Nesses casos, combine a inspeção com registros e execuções controladas sem interrupções.
Investigue erros intermitentes e diferenças entre ambientes
Os erros intermitentes exigem observar as condições de cada ocorrência. Registre tentativas, falhas, horários, versão executada, carga e dados utilizados. Uma sequência sem falhas é uma evidência limitada, especialmente quando o problema aparece raramente.
Quando o defeito ocorre apenas em um ambiente, compare:
- Versões da aplicação, do runtime e das dependências.
- Configurações relevantes e recursos habilitados.
- Estrutura do banco, migrações aplicadas e características dos dados.
- Presença de cache, quantidade de instâncias e execução de tarefas em segundo plano.
- Limites de recursos e condições de acesso a serviços externos.
Priorize diferenças que tenham relação com as evidências. Se reiniciar o serviço ou limpar o cache melhora o comportamento, registre o que mudou e por quanto tempo. A melhora pode indicar estado acumulado ou dados desatualizados, mas ainda precisa de explicação.
Teste a concorrência quando houver sinais de disputa
No formulário, uma consulta prévia pode verificar que o e-mail está disponível. Porém, duas requisições simultâneas podem obter essa mesma resposta antes de qualquer uma concluir a gravação.
Reproduza esse cenário em teste. A restrição de unicidade deve continuar protegendo o banco, e o conflito precisa receber tratamento mesmo quando a verificação anterior não o detecta.
Use o histórico para encontrar regressões
Quando houver uma revisão comprovadamente funcional e outra com falha, o git bisect permite testar revisões intermediárias para localizar a mudança associada à regressão.
Esse processo depende de um critério confiável de classificação. Em uma falha ocasional, uma única execução bem-sucedida não basta para declarar uma revisão funcional. Também é necessário manter compatíveis o ambiente e os dados usados na comparação.
Corrija a causa e comprove o resultado com testes
Depois de confirmar a causa raiz de bugs, faça uma alteração focada no comportamento investigado. No nosso exemplo, remover a restrição de unicidade faria o erro desaparecer às custas de permitir cadastros inconsistentes.
A correção deve preservar essa proteção, reconhecer especificamente o conflito esperado e devolver a resposta definida pelo contrato da API. Um status como 409 pode representar esse conflito, conforme o desenho da API; a interface também precisa apresentar uma orientação adequada ao fluxo.
Evite capturar qualquer exceção do banco e classificá-la como duplicidade. Uma indisponibilidade de conexão ou outro erro inesperado precisa manter seu tratamento próprio.
Faça o teste demonstrar o defeito
Quando viável, escreva primeiro um teste que falhe pelo comportamento incorreto na versão anterior e passe após a mudança. Isso comprova que o teste consegue detectar a regressão investigada.
Para o formulário, verifique pelo menos:
- Cadastro novo: continua funcionando com dados válidos.
- Cadastro duplicado: recebe a resposta prevista, sem erro genérico inesperado.
- Envios simultâneos: preservam a unicidade e tratam o conflito.
- Outras falhas: não são apresentadas indevidamente como e-mail duplicado.
- Interface: interpreta a resposta e informa o usuário conforme o fluxo definido.
Escolha testes de regressão nos níveis que observam o problema. Uma simulação pode verificar o tratamento da exceção; um teste de integração com o banco ajuda a validar a restrição real e o reconhecimento do conflito.
Antes da publicação, revise logs temporários, pontos de depuração e alterações sem relação com a causa. Depois, acompanhe o comportamento na versão publicada e a ocorrência do erro investigado. Nos casos intermitentes, mantenha a observação por condições de uso relevantes antes de considerar a investigação encerrada.
Checklist para depurar o próximo erro difícil
Para lembrar como encontrar bugs sem perder o controle da investigação, use esta sequência:
- Descreva o sintoma e o resultado esperado.
- Registre versão, ambiente, entradas e frequência.
- Construa uma reprodução e compare casos que funcionam e falham.
- Reúna mensagens, respostas de rede, stack traces e logs relacionados.
- Formule uma hipótese com previsão verificável.
- Execute um experimento controlado e registre o resultado.
- Isole a causa e aplique uma correção focada.
- Valide o cenário original e os casos relacionados.
- Documente a conclusão e acompanhe a aplicação após a mudança.
Modelo de registro da investigação
Sintoma e comportamento esperado:
Versão e ambiente:
Passos e dados de reprodução:
Frequência e condições das ocorrências:
Evidências coletadas:
Hipótese e previsão:
Experimento e resultado observado:
Causa confirmada:
Correção aplicada:
Testes executados:
Limitações e acompanhamento:No caso do formulário, o registro final deve informar quais cenários foram reproduzidos, como a exceção de unicidade foi identificada, qual tratamento foi implementado e quais testes foram efetivamente executados. Se a concorrência ainda não foi validada, isso deve permanecer explícito.
Esse método de debugging ajuda equipes responsáveis por sites comerciais a investigar falhas em formulários, páginas e integrações com decisões rastreáveis. Para empresas em Recife, ele também facilita descrever o problema e compartilhar evidências com quem fará a manutenção.
Seu site apresenta falhas em formulários, páginas ou integrações? Entre em contato com a Codephix para conversar sobre o problema e avaliar os próximos passos para seu projeto em Recife.

