Erros são informação, não veredito

Mensagens de erro e stack traces são telemetria do runtime. Desenvolvedores maduros leem, reproduzem, isolam e corrigem — em ciclo curto. Esta aula constrói autonomia diagnóstica; ferramentas auxiliares (incluindo assistentes de IA) só fazem sentido depois que você sabe o que procurar.

Anatomia de um stack trace

TypeError: Cannot read properties of undefined (reading 'total')
    at calcularDesconto (app.js:14:28)
    at processarPedido (app.js:42:10)
    at main (app.js:51:3)

Ordem de leitura: tipo → mensagem → função/linha onde ocorreu → cadeia de chamadas até entrypoint.

Diagrama PlantUML

Metodologia em quatro passos

  1. Reproduzir com entrada mínima.
  2. Isolar — comentar metade, usar console.log estratégico, debugger do VS Code (breakpoint).
  3. Corrigir causa raiz, não sintoma.
  4. Registrar teste ou checklist para não regressar.

Debugger prático no VS Code

Configure launch.json para Node: breakpoint na linha suspeita, inspecione variáveis no painel, avance step-by-step. Mais confiável que adicionar dezenas de console.log permanentes.

try / catch com responsabilidade

async function carregarConfig(url) {
  try {
    const res = await fetch(url);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } catch (erro) {
    console.error('Falha config', { url, message: erro.message });
    throw erro;
  }
}

Nunca catch vazio. Em produção, logue contexto (request id) sem dados sensíveis.

Erros comuns e tradução

MensagemSignificado provável
ReferenceErrorVariável não declarada ou fora de escopo
TypeErrorOperação em tipo errado (null, undefined)
SyntaxErrorErro de digitação antes da execução
RangeErrorValor fora do intervalo permitido

Construindo resiliência

  • Valide entradas na fronteira do sistema (função pública).
  • Prefira falhar cedo com mensagem clara.
  • Separe lógica pura (fácil de testar) de I/O (rede, arquivo).

Exercício prático: caça ao bug

Cole deliberatemente este código quebrado e pratique diagnóstico:

function resumoPedidos(pedidos) {
  let total = 0;
  for (let i = 0; i <= pedidos.length; i++) {
    total += pedidos[i].valor;
  }
  return total;
}

console.log(resumoPedidos([{ valor: 100 }, { valor: 50 }]));

Erros esperados: off-by-one no loop (<= em vez de <), possível TypeError no último índice. Corrija, adicione guard para array vazio e teste com três entradas diferentes.

Log estruturado (introdução)

function logErro(contexto, erro) {
  console.error(JSON.stringify({
    ts: new Date().toISOString(),
    contexto,
    message: erro.message,
    stack: erro.stack
  }));
}

JSON em logs facilita busca em ferramentas como Grafana ou CloudWatch — padrão em produção.

Técnica do “rubber duck” e hipóteses

Explique o bug em voz alta (ou por escrito) como se ensinasse um colega. Muitas vezes você encontra a inconsistência no meio da explicação — variável renomeada em um lugar só, condição invertida, objeto mutado inesperadamente. Registre hipóteses antes de testar: “Acho que pedido.cliente chega undefined porque o fetch retorna 204”. Valide ou descarte uma por vez, em vez de alterar dez linhas aleatoriamente.

Debugger no navegador (DevTools)

Na aba Sources, abra seu app.js, clique na margem para breakpoint, recarregue a página e use Step Over / Step Into. Inspecione escopo local e watch expressions. Para bugs de DOM, o painel Elements mostra HTML atual (não o font original) — compare com o que seu JavaScript gerou.

Erros assíncronos

Promises rejeitadas sem catch geram UnhandledPromiseRejection. Em async/await, envolva chamadas externas em try/catch ou use .catch() na cadeia. Erro de rede não é bug de lógica — trate mensagem amigável e retry com limite.

async function carregarPedido(id) {
  try {
    const res = await fetch(`/api/pedidos/${id}`);
    if (res.status === 404) return null;
    if (!res.ok) throw new Error(`Falha HTTP ${res.status}`);
    return await res.json();
  } catch (erro) {
    logErro('carregarPedido', erro);
    throw erro;
  }
}

Checklist de depuração para levar ao trabalho

  1. Qual é a mensagem exata e a primeira linha da stack?
  2. Consigo reproduzir com entrada mínima?
  3. O que mudou desde a última vez que funcionou (git diff)?
  4. Há diferença entre ambiente local e produção (variáveis, versão)?
  5. Corrigi a causa ou apenas o sintoma?

Nas próximas semanas, ao integrar web e projetos reais, você aplicará este fluxo diariamente. Só então faz sentido delegar interpretação inicial de traces a assistentes — sempre validando a hipótese no código.

Para aprofundar na web

Para entender melhor este tema, pesquise por:

  • "como ler stack trace JavaScript" — ordem das linhas e origem do erro
  • "debugger VS Code breakpoint tutorial" — inspecionar variáveis passo a passo
  • "try catch async await JavaScript" — erros em código assíncrono
  • "ReferenceError TypeError diferença" — tipos comuns e causas
  • "logging estruturado JSON aplicações" — padrão usado em produção

Quebre de propósito um script seu e documente no README: mensagem, causa, correção.

Atividades

  1. Onde está a origem imediata do TypeError no exemplo?

    • A) main:51
    • B) calcularDesconto:14
    • C) processarPedido:42
    • D) fetch
    Ver resposta

    Resposta correta: B) calcularDesconto:14

    Primeira linha da stack após a mensagem aponta função e linha do acesso inválido.

  2. Catch vazio é problemático porque:

    • A) Deixa código mais rápido
    • B) Oculta falhas e corrompe estado silenciosamente
    • C) É exigido pelo Node
    • D) Impede async
    Ver resposta

    Resposta correta: B) Oculta falhas e corrompe estado silenciosamente

    Erros engolidos geram incidentes difíceis de rastrear em produção.

  3. Descreva como reproduziria um bug intermitente de produção localmente.

    Ver resposta

    Coletar logs/stack, identificar entrada e condições (hora, usuário, payload), reduzir ao menor caso que falha, criar fixture de teste, usar debugger ou logs incrementais até achar linha exata.