Voltar para o Blog

Debugging JavaScript: Técnicas Avançadas com DevTools Que Você Precisa Conhecer

Olá HaWkers, debugging é uma das habilidades mais importantes para qualquer desenvolvedor, mas muitos ainda se limitam ao console.log(). As ferramentas de desenvolvedor dos navegadores modernos oferecem recursos poderosos que podem transformar completamente como você encontra e corrige bugs.

Vamos explorar técnicas avançadas que vão além do básico?

Além do console.log()

O console.log() é útil, mas o objeto console oferece muito mais:

Console Methods Avançados

// Agrupar logs relacionados
console.group('Processando Usuário');
console.log('Nome:', user.name);
console.log('Email:', user.email);
console.log('Permissões:', user.permissions);
console.groupEnd();

// Tabelas para arrays e objetos
const users = [
  { name: 'Ana', age: 28, role: 'Dev' },
  { name: 'Carlos', age: 32, role: 'Lead' },
  { name: 'Maria', age: 25, role: 'Dev' }
];
console.table(users);

// Medir tempo de execução
console.time('fetchUsers');
await fetchUsers();
console.timeEnd('fetchUsers'); // fetchUsers: 234.5ms

// Contagem de execuções
function processItem(item) {
  console.count('processItem called');
  // lógica...
}

// Stack trace sem erro
function deepFunction() {
  console.trace('Como chegamos aqui?');
}

// Assertions para debugging
console.assert(user.id > 0, 'User ID deve ser positivo', user);

Console Styling

// Logs coloridos e estilizados
console.log(
  '%c ERRO %c Falha na autenticação',
  'background: #ff0000; color: white; padding: 2px 6px; border-radius: 3px;',
  'color: #ff0000;'
);

console.log(
  '%c SUCCESS %c Usuário criado',
  'background: #00aa00; color: white; padding: 2px 6px; border-radius: 3px;',
  'color: #00aa00;'
);

// Múltiplos estilos
console.log(
  '%cVariável:%c user.name %c=%c "João"',
  'color: gray;',
  'color: purple;',
  'color: gray;',
  'color: green;'
);

Breakpoints Condicionais

Breakpoints são mais poderosos do que simples pausas no código.

Breakpoints com Condição

No Chrome DevTools, clique com botão direito em um breakpoint e selecione "Edit breakpoint":

// Código original
async function processOrders(orders) {
  for (const order of orders) {
    await processOrder(order);
  }
}

// Condição no breakpoint: order.total > 1000
// Só pausa quando order.total for maior que 1000

Logpoints (Breakpoints que não pausam)

Adicione logs sem modificar o código:

// No DevTools, adicione um logpoint com:
// "Processando order:", order.id, "total:", order.total

// Equivalente a adicionar console.log() no código,
// mas sem precisar modificar e recarregar

DOM Breakpoints

Pause quando o DOM mudar:

  1. Inspecione o elemento no painel Elements
  2. Clique com botão direito → Break on
  3. Escolha: subtree modifications, attribute modifications, ou node removal
// Útil para encontrar código que modifica elementos inesperadamente
// O DevTools vai pausar e mostrar exatamente qual código fez a modificação

Debugging de Código Assíncrono

Código assíncrono pode ser difícil de debugar. Aqui estão técnicas específicas:

Async Stack Traces

O Chrome DevTools mostra o stack trace completo incluindo chamadas assíncronas:

async function fetchUserData(userId) {
  // Breakpoint aqui mostra todo o caminho assíncrono
  const response = await fetch(`/api/users/${userId}`);
  const data = await response.json();
  return data;
}

async function loadDashboard() {
  const user = await fetchUserData(currentUserId);
  await loadUserPosts(user.id);
  await loadUserNotifications(user.id);
}

// No stack trace você verá:
// - fetchUserData (async)
// - loadDashboard (async)
// - buttonClickHandler
// - (click event)

Promise Rejections

// Ativar "Pause on caught exceptions" para debugar promises rejeitadas

async function riskyOperation() {
  try {
    const result = await mightFail();
    return result;
  } catch (error) {
    // Com "Pause on caught exceptions" ativo,
    // o debugger para aqui antes do catch executar
    console.error('Operação falhou:', error);
    throw error;
  }
}

Event Listener Breakpoints

No painel Sources → Event Listener Breakpoints:

// Marque eventos específicos para pausar:
// - Mouse → click
// - Keyboard → keydown
// - XHR/Fetch → Any XHR or fetch

// Útil para entender o fluxo de eventos
// sem precisar adicionar breakpoints manualmente

Performance Profiling

Identificar gargalos de performance é essencial para aplicações responsivas.

Performance Panel

// Inicie uma gravação no painel Performance
// Execute a ação que está lenta
// Pare a gravação e analise

// O que procurar:
// - Long Tasks (tarefas > 50ms)
// - Layout Thrashing (muitos reflows)
// - Scripting time alto
// - Blocking time

Identificando Layout Thrashing

// ❌ RUIM - Causa múltiplos reflows
function updateElements(elements) {
  elements.forEach(el => {
    const height = el.offsetHeight; // Força reflow (leitura)
    el.style.height = height + 10 + 'px'; // Força reflow (escrita)
  });
}

// ✅ BOM - Agrupa leituras e escritas
function updateElementsOptimized(elements) {
  // Primeiro, todas as leituras
  const heights = elements.map(el => el.offsetHeight);

  // Depois, todas as escritas
  elements.forEach((el, i) => {
    el.style.height = heights[i] + 10 + 'px';
  });
}

Memory Profiling

// No painel Memory, tire snapshots para identificar memory leaks

// Padrão comum de memory leak:
class Component {
  constructor() {
    // Event listener que nunca é removido
    window.addEventListener('resize', this.handleResize);
  }

  handleResize = () => {
    // ...
  };

  // ❌ Sem cleanup, o listener mantém referência ao componente
}

// ✅ Correção:
class ComponentFixed {
  constructor() {
    window.addEventListener('resize', this.handleResize);
  }

  handleResize = () => {
    // ...
  };

  destroy() {
    window.removeEventListener('resize', this.handleResize);
  }
}

Source Maps e Debugging de Código Minificado

Configurando Source Maps

// webpack.config.js
module.exports = {
  devtool: 'source-map', // Produção
  // ou
  devtool: 'eval-source-map', // Desenvolvimento (mais rápido)
};

// vite.config.js
export default {
  build: {
    sourcemap: true,
  },
};

Debugging com Source Maps

Com source maps configurados, você pode:

  1. Ver código original no painel Sources
  2. Adicionar breakpoints no código TypeScript/ES6+
  3. Ver variáveis com nomes originais
  4. Stack traces apontam para arquivos originais
// Código TypeScript original
interface User {
  id: number;
  name: string;
}

async function getUser(id: number): Promise<User> {
  // Breakpoint aqui funciona normalmente
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

Snippets e Overrides

Snippets Reutilizáveis

Crie snippets no DevTools para tarefas repetitivas:

// Snippet: Limpar localStorage e recarregar
(function clearAndReload() {
  localStorage.clear();
  sessionStorage.clear();
  location.reload();
})();

// Snippet: Exportar dados do console
(function exportConsoleData() {
  const data = /* seus dados */;
  const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'export.json';
  a.click();
})();

// Snippet: Monitorar todas as chamadas de fetch
(function monitorFetch() {
  const originalFetch = window.fetch;
  window.fetch = async (...args) => {
    console.group('Fetch Request');
    console.log('URL:', args[0]);
    console.log('Options:', args[1]);

    const start = performance.now();
    const response = await originalFetch(...args);
    const duration = performance.now() - start;

    console.log('Status:', response.status);
    console.log('Duration:', duration.toFixed(2) + 'ms');
    console.groupEnd();

    return response;
  };
  console.log('Fetch monitoring enabled');
})();

Local Overrides

Modifique arquivos de produção localmente:

  1. No painel Sources, crie uma pasta de overrides
  2. Selecione um arquivo e edite
  3. As mudanças persistem entre recarregamentos
  4. Útil para testar fixes em produção sem deploy

Network Debugging

Filtros Avançados

// No painel Network, use filtros:

// Por tipo
// is:running - Requisições em andamento
// larger-than:1M - Maiores que 1MB
// status-code:500 - Apenas status 500

// Por domínio
// domain:api.example.com

// Expressões negativas
// -domain:analytics.com - Excluir domínio

// Múltiplos filtros
// method:POST status-code:400

Throttling e Offline

// Simular conexões lentas
// Network panel → No throttling → Selecione:
// - Slow 3G
// - Fast 3G
// - Offline

// Ou crie perfis customizados:
// - Download: 100kb/s
// - Upload: 50kb/s
// - Latency: 500ms

Conclusão

Dominar as ferramentas de debugging transforma sua produtividade como desenvolvedor. Em vez de ficar adivinhando onde está o problema, você pode ir diretamente à causa raiz.

Algumas práticas recomendadas:

  • Use breakpoints condicionais ao invés de múltiplos console.log()
  • Aprenda os atalhos de teclado do DevTools
  • Utilize logpoints quando não quiser modificar código
  • Profile regularmente para identificar problemas de performance antes que se tornem críticos

As ferramentas estão disponíveis gratuitamente em todos os navegadores modernos. Vale investir tempo para dominá-las.

Se você quer continuar aprofundando seus conhecimentos em JavaScript, recomendo conferir o artigo Web APIs Modernas do Navegador com JavaScript em 2025 onde exploramos APIs poderosas que podem melhorar suas aplicações.

Bora pra cima! 🦅

Comentários (0)

Esse artigo ainda não possui comentários 😢. Seja o primeiro! 🚀🦅

Adicionar comentário