Debugar software é uma habilidade essencial para desenvolvedores, especialmente aqueles com alguns anos de experiência. Ao longo de 15 anos de experiência com software, vejo profissionais cada mais mais preocupados em “resolver o problema” do que entender o problema, e isso resulta em uma perda de tempo absurda, principalmente quando se está com algum problema em ambiente produtivo ou algum bug chato na etapa de desenvolvimento.
Este artigo serve apenas como rascunho de ideias para que você possa organizar suas ideias e também como proceder após a etapa de debugging. Portanto, aqui estão algumas dicas estruturadas para ajudar você a melhorar sua abordagem ao depurar código.
1) Defina o Problema
O primeiro passo no processo de debugging é definir claramente o problema. Isso envolve:
- Identificar os sintomas: Descreva o que está acontecendo de errado.
- Comparar os resultados esperados com os reais: Destaque as discrepâncias entre o comportamento esperado e o comportamento observado.
- Determinar a extensão do problema: Avalie se o problema afeta uma parte específica do sistema ou se é mais abrangente.
- Avaliar a gravidade e o impacto: Entenda a urgência e a importância de corrigir o problema.
- Notar os passos para reproduzir o problema: Documente o caminho que leva ao bug para facilitar sua investigação.
- Verifique se a versão correta do aplicativo/versão correta foi implantada ou não: Nossa, essa é mais uma causa popular de bugs. Sempre verifique a versão do aplicativo em que você está verificando o bug para garantir que ele esteja funcionando bem. Sempre verifique se as mudanças foram implementadas corretamente em todos os pipelines.
Essa clareza inicial ajuda a direcionar os esforços de troubleshooting de maneira mais eficaz. Saber a prioridade e urgência do problema que está tentando resolver, ajuda a colocar o foco onde importa naquele momento. Procure ter o máximo de evidências, por imagens, textos ou qualquer material que lhe ajude a imergir em um lugar mais certeiro antes de ir direto para o código, isso vai te salvar muito tempo!
Fale Sobre o Problema
Discutir o problema com um colega ou até mesmo explicar a situação para um objeto inanimado pode ajudar a organizar seus pensamentos e encontrar uma solução. Muitas vezes, a simples ação de verbalizar o problema leva à descoberta de insights valiosos.
2) Reproduza o Problema
Reproduzir o bug é muitas vezes a maneira mais eficaz de identificar sua causa. Por isso aqui vale o alerta para um assunto que já é bem conhecido. O seu ambiente produtivo, precisa ser “reproduzível” localmente, ou o mais próximo dele. Ambientes de desenvolvimento muito diferentes do ambiente de produção ocasionam em uma série de problemas, que vão desde estimativas de entregas, até troubleshooting, que é o nosso caso aqui. Porém, se não for possível reproduzi-lo diretamente, considere:
- Verificar o ambiente onde ocorreu: Analise se há algo específico no ambiente que pode ter causado o problema.
- Buscar a mensagem de erro online: Muitas vezes, outros desenvolvedores já enfrentaram e resolveram problemas semelhantes.
- Avaliar o estado do sistema no momento do bug: Entenda o contexto em que o problema apareceu.
- Anotar a frequência do problema: Determine se é um problema intermitente ou constante.
- Identificar padrões recorrentes: Padrões podem fornecer pistas importantes sobre a origem do bug.
Para os profissionais “desesperados”, existe uma ferramenta chamada TTD, Time Travel Debug, você pode encontrar mais detalhes neste artigo aqui: Depurando aplicações .NET com Time Travel Debugging
3) Identifique a Causa
Os logs são uma ferramenta fundamental no processo de debugging. Se os logs existentes não forem suficientes, adicione mais pontos de log e tente reproduzir o problema novamente. Outras estratégias incluem:
- Utilizar ferramentas de debugging: Ferramentas como o Visual Studio Debugger podem fornecer insights valiosos. Às vezes, um trecho de código pode ser executado várias vezes antes de você encontrar as condições que procura. Por exemplo, você pode estar trabalhando com uma coleção e o 93º elemento é aquele que você precisa depurar. Você pode sentar e pressionar F5 92 vezes para chegar ao registro no qual está interessado ou pode usar um ponto de interrupção condicional. Os pontos de interrupção condicionais só farão com que o processo seja interrompido quando uma condição for atendida. As condições podem ser muito simples, como contar o número de vezes que uma linha foi atingida. Eles também podem ser mais complexos, como verificar se algum valor é verdadeiro.
- Testar componentes em partes menores: Quebre o código em partes menores e teste individualmente para identificar a origem do problema.
- Comentar seções do código: Comente partes do código para isolar e identificar a área problemática.
- Ajuda do Git: Determinar os commits ocorridos desde a última vez que o código funcionou bem nos ajudará a restringir o espaço do problema. Ao vasculhar o git diff, podemos determinar as alterações que poderiam ter causado o bug. Ferramentas Git como git bisect e git log são suas amigas para verificar regressões, possíveis pull requests que podem ser a causa do bug.
Dica adicional: Use e abuse de sistemas de rastreamento, APM’s e logging. É nessa hora que você pode encontrar mensagens de erro ou inconformidades importantes. Essas ações podem fornecer pistas valiosas para resolver o problema.
4) Utilize Breakpoints
Os breakpoints são essenciais para interromper a execução do código em pontos estratégicos e analisar o estado da aplicação. Use breakpoints condicionais para interromper a execução apenas quando determinadas condições forem atendidas, economizando tempo e focando em cenários específicos. Aqui você acessa a documentação completa: Use breakpoints in the Visual Studio debugger
Durante o debugging, você pode visualizar os valores das variáveis ao passar o mouse sobre elas ou usando a janela de Locals. Além disso, você pode editar esses valores para testar diferentes cenários e ver como a aplicação reage, permitindo simular condições difíceis de reproduzir.
5) Adicione Testes Unitários
Adicionar testes unitários pode ser uma maneira eficiente de depurar. Esses testes permitem isolar funções e testar com diferentes entradas de forma automatizada, facilitando a identificação de problemas. Ferramentas como NUnit ou xUnit podem ajudar a criar uma suíte de testes robusta.
6) Forneça um Postmortem
Uma vez que a causa do bug seja identificada e resolvida, é crucial documentar o problema, a correção e as maneiras de prevenir ocorrências futuras. Compartilhe esse conhecimento com sua equipe para garantir que todos estejam informados e possam se beneficiar das lições aprendidas. Isso promove uma abordagem proativa para desafios futuros.
Reforço para o postmortem: Esta etapa costuma ser a mais negligenciada e a meu ver é a mais importante. Ter uma documentação com uma base de conhecimento (não só sobre bugs) é o que faz um time ser robusto, forte e ao mesmo tempo rotativo. Porque rotativo? A rotatividade em um time a meu ver é algo que precisa ser incentivado, obviamente que seja de uma forma saudável, mas que permita pessoas novas chegando e saindo para espalhar mais conhecimento pela empresa.
7) Faça uma pausa e volte ❤️
Sério, isso pode fazer maravilhas. Muitas vezes esqueço essa regra e resolvo um problema por horas, forçando meus olhos e minha mente. Fazer uma pausa e analisar o problema com a mente descansada é sempre útil.
Conclusão
Debugar de maneira eficaz é um processo sistemático que envolve definir claramente o problema, reproduzi-lo, identificar a causa e documentar as lições aprendidas. Com essas dicas, você pode aprimorar suas habilidades de debugging e ajudar sua equipe a ser mais eficiente na resolução de problemas.