O Print da Exceção Não é o Bug: Como Investigar Erros com IA de Forma Estruturada
São 16h37. Uma funcionalidade que funcionava desde a última sexta-feira parou de funcionar hoje de manhã. Não houve deploy novo, ninguém mexeu naquele módulo, e o log mostra apenas uma linha genérica: NullReferenceException em um arquivo com quatrocentas linhas. Você abre o arquivo, olha para o ponto indicado pela pilha de chamadas, não encontra nada óbvio, e começa a colocar print (ou console.log, ou ShowMessage, dependendo da linguagem) em cada linha suspeita. Quarenta minutos depois, você ainda não sabe a causa, só sabe que “o problema está em algum lugar ali”.
Essa cena se repete todos os dias em equipes de desenvolvimento, em qualquer linguagem — Python, Java, C#, JavaScript, Delphi, não importa. Depurar código (ou seja, investigar por que um programa não se comporta como esperado) é uma das tarefas que mais consome tempo no dia a dia de quem programa, e é também uma das que mais gera frustração, porque o tempo investido nem sempre é proporcional ao tamanho do problema: às vezes um bug gigante se resolve em cinco minutos, e um erro aparentemente bobo consome a tarde inteira.
Neste artigo você vai aprender, de forma simples e sem pressupor nenhuma ferramenta avançada instalada, como usar inteligência artificial (IA) para investigar erros de código de maneira estruturada — não como uma mágica que “conserta sozinho”, mas como um método que reduz o tempo gasto tateando no escuro. Vamos do conceito básico até exemplos práticos em código, para que você possa aplicar isso ainda hoje.
O problema real: por que depurar sozinho é lento
Antes de falar de IA, vale entender por que a depuração tradicional consome tanto tempo. Normalmente, o processo segue mais ou menos este roteiro:
- Você lê a mensagem de erro, mas ela raramente explica por que o erro aconteceu — só descreve o que aconteceu.
- Você tenta reproduzir o erro, o que já é difícil quando ele depende de uma condição específica de dados ou de ambiente.
- Você adiciona instruções de log ou usa um depurador (debugger — ferramenta que permite pausar a execução do programa e inspecionar valores de variáveis) para ir descobrindo o estado do programa passo a passo.
- Você formula hipóteses (“talvez seja isso”) e as testa uma a uma, muitas vezes sem um raciocínio explícito, só por instinto.
O problema não é que esse processo esteja errado — ele funciona. O problema é que ele é lento e artesanal: você guarda toda a hipótese e todo o histórico do problema na própria cabeça, e a cada nova pista precisa reconstruir o raciocínio. É exatamente aí que a IA pode ajudar, não substituindo seu raciocínio, mas servindo como um investigador auxiliar que lê rápido, lembra do que foi dito e ajuda a organizar hipóteses.
💡 Dica do Mestre: segundo a IBM, “a depuração com IA geralmente ocorre por meio de assistentes de programação incorporados ou construídos ao redor do ambiente de desenvolvimento, analisando código, mensagens de erro e contexto de execução para sugerir causas prováveis e correções”. Veja a explicação completa em Depuração com IA — IBM.
O que é, de fato, “debugging com IA”
Debugging com IA não é apenas colar a mensagem de erro em um chat e esperar a resposta. Isso até funciona para erros simples, mas para problemas reais — os que consomem sua tarde — funciona melhor quando você trata a IA como um assistente que precisa de contexto (as informações relevantes sobre o problema) para gerar hipóteses úteis.
Pense na analogia de um médico. Um bom diagnóstico não nasce de “estou com dor”, mas de sintomas descritos com precisão, exames e histórico. Da mesma forma, um bom diagnóstico de bug não nasce de “está dando erro”, mas de: mensagem de erro completa, trecho de código relevante, o que era esperado, o que de fato aconteceu, e o que já foi tentado.
Os quatro elementos de um bom pedido de investigação
Sempre que for pedir ajuda de uma IA para investigar um bug, tente reunir estas quatro informações antes de perguntar:
- Comportamento esperado: o que o código deveria fazer.
- Comportamento observado: o que ele está fazendo de fato, incluindo a mensagem de erro completa (não resumida).
- Código relevante: a função ou trecho onde o problema aparece, com contexto suficiente (não a linha isolada).
- O que já foi tentado: hipóteses descartadas, para a IA não repetir o mesmo caminho.
Vamos ver isso na prática.
Exemplo prático 1: um erro clássico em Python
Imagine a seguinte função, que deveria calcular a média de notas de uma lista de alunos:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
def calcular_media(notas): total = 0 for nota in notas: total += nota return total / len(notas) alunos = { "Ana": [8, 7, 9], "Bruno": [], "Carla": [6, 7, 5] } for nome, notas in alunos.items(): media = calcular_media(notas) print(f"{nome}: {media:.2f}") |
Ao rodar, o programa quebra com:
|
1 2 3 4 |
ZeroDivisionError: division by zero |
Um pedido malfeito para a IA seria: “meu código dá erro de divisão por zero, conserta”. Um pedido bem estruturado, seguindo os quatro elementos acima, seria assim:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Comportamento esperado: calcular a média de notas de cada aluno e imprimir o resultado. Comportamento observado: o programa quebra com "ZeroDivisionError: division by zero" ao processar o aluno "Bruno", que tem uma lista de notas vazia. Código relevante: def calcular_media(notas): total = 0 for nota in notas: total += nota return total / len(notas) O que já tentei: nada ainda, é a primeira vez que vejo esse erro. Pergunta: por que esse erro acontece e qual seria uma forma segura de tratar o caso de lista vazia sem quebrar o programa? |
Com esse contexto, a resposta da IA tende a ser precisa: o erro ocorre porque len(notas) é zero quando a lista está vazia, e a divisão por zero não é permitida. A correção sugerida costuma ser algo como:
|
1 2 3 4 5 6 7 8 |
def calcular_media(notas): if not notas: return 0.0 # ou None, dependendo da regra de negócio total = sum(notas) return total / len(notas) |
Note que o ganho de tempo não veio da IA “adivinhar” o problema — veio de você ter fornecido contexto suficiente para que a causa fosse óbvia na primeira tentativa, em vez de um ciclo de perguntas e respostas incompletas.
Exemplo prático 2: erro assíncrono em JavaScript
Erros que envolvem código assíncrono (código que não executa de forma imediata e sequencial, como chamadas de rede) costumam ser mais difíceis de depurar porque a pilha de erros muitas vezes não aponta diretamente para a causa. Veja este exemplo:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
async function buscarUsuario(id) { const resposta = await fetch(`https://api.exemplo.com/usuarios/${id}`); const dados = await resposta.json(); return dados; } async function exibirNomeUsuario(id) { const usuario = await buscarUsuario(id); console.log(usuario.nome.toUpperCase()); } exibirNomeUsuario(999); |
Se o usuário com id 999 não existir, a API pode responder com um corpo de erro (por exemplo, { "erro": "não encontrado" }), sem o campo nome. O programa então quebra com:
|
1 2 3 4 |
TypeError: Cannot read properties of undefined (reading 'toUpperCase') |
Um pedido estruturado para a IA, neste caso, incluiria também a resposta real da API, não só o código:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Comportamento esperado: buscar um usuário pelo id e exibir o nome em maiúsculas. Comportamento observado: erro "Cannot read properties of undefined (reading 'toUpperCase')" quando o id não existe na base. A resposta da API nesse caso é: { "erro": "não encontrado" } Código relevante: [colar as duas funções acima] O que já tentei: confirmei que o fetch está retornando status 404, mas não estou tratando esse caso. Pergunta: como devo validar a resposta antes de tentar acessar "nome", de forma que o erro fique claro em vez de quebrar o programa? |
A resposta tende a sugerir uma verificação explícita do formato de resposta antes de usar os dados:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
async function buscarUsuario(id) { const resposta = await fetch(`https://api.exemplo.com/usuarios/${id}`); if (!resposta.ok) { throw new Error(`Usuário ${id} não encontrado (status ${resposta.status})`); } return resposta.json(); } async function exibirNomeUsuario(id) { try { const usuario = await buscarUsuario(id); console.log(usuario.nome.toUpperCase()); } catch (erro) { console.error("Falha ao exibir usuário:", erro.message); } } exibirNomeUsuario(999); |
Esse tipo de correção — validar antes de assumir que os dados existem — é um padrão comum, e a IA costuma reconhecê-lo rapidamente quando tem a resposta real da API à disposição, não apenas o código.
Uma técnica avançada, mas simples: peça hipóteses, não soluções
Uma armadilha comum é pedir diretamente “conserta esse código” antes de entender a causa. Isso funciona para bugs triviais, mas em problemas mais complexos pode levar a uma correção que trata o sintoma, sem resolver a causa raiz. Uma técnica mais segura é pedir hipóteses antes de pedir a correção:
|
1 2 3 4 5 6 |
Antes de sugerir uma correção, liste de 3 a 5 hipóteses possíveis para esse comportamento, ordenadas da mais provável para a menos provável, explicando brevemente como eu poderia confirmar cada uma. |
Esse tipo de pedido transforma a IA em uma ferramenta de raciocínio, não apenas de geração de código. Você recebe uma lista de possibilidades, testa a mais provável primeiro (adicionando um log, checando um valor, revisando uma condição) e só então pede a correção depois de confirmar a causa real. Esse passo extra, que parece burocrático, é o que evita o ciclo de “tenta de novo, tenta de novo” sem entender o que está sendo mudado.
Usando IA integrada ao editor para ver mais contexto de uma vez
Ferramentas como o GitHub Copilot ou assistentes integrados a editores de código conseguem enxergar mais do que apenas o trecho colado em um chat — elas leem arquivos vizinhos, definições de tipos e até o histórico do projeto. Isso reduz a necessidade de você copiar manualmente cada pedaço de contexto. Ainda assim, mesmo usando esse tipo de ferramenta, os quatro elementos citados anteriormente (esperado, observado, código, tentativas) continuam sendo o que garante uma resposta precisa, porque a IA não sabe, sozinha, qual comportamento você considera “correto” para o seu sistema.
💡 Dica do Mestre: a Google descreve bem esse papel da IA no ciclo de desenvolvimento: “a IA para desenvolvedores pode automatizar tarefas, gerar códigos de qualidade, personalizar modelos e acelerar o desenvolvimento de softwares e aplicativos”. Vale conferir a visão geral em IA para desenvolvedores — Google Cloud.
Exemplo prático 3: um bug de lógica sem mensagem de erro
Nem todo bug gera uma exceção. Às vezes o programa roda sem travar, mas produz um resultado errado — o tipo mais difícil de investigar sozinho, porque não há uma pilha de erro apontando para o culpado. Veja este exemplo em Java:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class Estoque { public static double calcularDesconto(double preco, int quantidade) { double desconto = 0; if (quantidade > 10) { desconto = 0.1; } if (quantidade > 20) { desconto = 0.2; } return preco - (preco * desconto); } public static void main(String[] args) { System.out.println(calcularDesconto(100.0, 25)); // esperado: 80.0, mas retorna 90.0 } } |
Aqui não há erro nenhum na tela, apenas um valor incorreto. Um pedido bem feito para a IA precisa deixar claro o valor esperado versus o obtido:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Comportamento esperado: para quantidade 25, o desconto deveria ser 20% (resultado 80.0). Comportamento observado: o método retorna 90.0, como se o desconto aplicado fosse de apenas 10%. Código relevante: [colar o método calcularDesconto] O que já tentei: revisei a lógica dos "if" e não vejo o erro, os dois parecem cobrir os casos certos. Pergunta: existe algum problema na ordem ou nas condições desses "if" que explique esse comportamento? |
Nesse caso, a causa é simples de identificar quando bem explicada: como os dois blocos if são independentes (não são if / else if), e a condição quantidade > 10 é verdadeira ao mesmo tempo que quantidade > 20, o valor de desconto é sobrescrito corretamente para 0.2 — então o bug de fato não está aqui. Esse é exatamente o tipo de situação em que pedir hipóteses antes da correção evita que você (ou a IA) “conserte” algo que já estava certo, e direcione a investigação para o lugar certo — por exemplo, verificar se o valor de quantidade realmente chega como 25 até esse método, ou se há alguma conversão de tipo acontecendo antes.
Esse exemplo reforça um ponto importante: a IA é tão boa quanto o contexto que você fornece, e às vezes o maior ganho de produtividade é simplesmente ser forçado a descrever o problema com precisão — processo que, por si só, já revela parte da causa (esse efeito é conhecido informalmente como “explicar o problema para o pato de borracha”, uma técnica antiga de depuração).
Checklist rápido para aplicar hoje mesmo
- Antes de pedir ajuda, escreva a mensagem de erro completa, sem resumir.
- Descreva o comportamento esperado em uma frase clara.
- Cole o trecho de código com contexto suficiente (a função inteira, não só a linha).
- Liste o que você já tentou, para não receber a mesma sugestão de novo.
- Peça hipóteses antes de pedir a correção, principalmente em bugs sem erro explícito.
- Depois de aplicar a correção, teste o caso que falhava e também os casos que já funcionavam, para garantir que nada quebrou.
💡 Dica do Mestre: a Softdesign resume bem o cuidado necessário ao adotar IA no fluxo de desenvolvimento: a tecnologia agrega recursos e agilidade, mas o resultado depende de como ela é aplicada dentro do processo da equipe. Leia mais em IA para desenvolvimento de software — Softdesign.
Quer ir além do básico?
As técnicas apresentadas aqui são o ponto de partida para transformar a IA em uma parceira real de investigação de bugs, não apenas em um gerador de código. Se você quer trocar experiências com outros desenvolvedores que já aplicam esses métodos no dia a dia, discutir casos reais e aprender técnicas mais avançadas de depuração, automação e uso de agentes de IA no desenvolvimento, entre para a Comunidade Dev’s AI. É um espaço para quem quer aprender e evoluir junto, com gente que enfrenta os mesmos desafios que você enfrenta na sua rotina de código.
Conclusão
Debugging com IA não é sobre colar um erro em um chat e torcer para a resposta certa aparecer. É sobre transformar um processo que antes vivia só na sua cabeça — hipóteses, tentativas, descartes — em um processo estruturado e comunicável, onde a IA participa como um investigador auxiliar rápido e paciente. Quando você fornece o comportamento esperado, o comportamento observado, o código relevante e o que já tentou, a chance de resolver o problema na primeira ou na segunda tentativa aumenta consideravelmente, e as horas que antes se perdiam em tentativa e erro passam a ser horas de trabalho de fato produtivo. A ferramenta muda, a linguagem muda, mas o método — descrever o problema com precisão antes de buscar a solução — continua sendo a base de qualquer boa investigação, com ou sem inteligência artificial.