Volver al blog

Debugging JavaScript: Técnicas Avanzadas con DevTools Que Necesitas Conocer

Hola HaWkers, debugging es una de las habilidades más importantes para cualquier desarrollador, pero muchos todavía se limitan al console.log(). Las herramientas de desarrollador de los navegadores modernos ofrecen recursos poderosos que pueden transformar completamente cómo encuentras y corriges bugs.

¿Vamos a explorar técnicas avanzadas que van más allá del básico?

Más Allá del console.log()

El console.log() es útil, pero el objeto console ofrece mucho más:

Console Methods Avanzados

// Agrupar logs relacionados
console.group('Procesando Usuario');
console.log('Nombre:', user.name);
console.log('Email:', user.email);
console.log('Permisos:', user.permissions);
console.groupEnd();

// Tablas para arrays y objetos
const users = [
  { name: 'Ana', age: 28, role: 'Dev' },
  { name: 'Carlos', age: 32, role: 'Lead' },
  { name: 'María', age: 25, role: 'Dev' }
];
console.table(users);

// Medir tiempo de ejecución
console.time('fetchUsers');
await fetchUsers();
console.timeEnd('fetchUsers'); // fetchUsers: 234.5ms

// Conteo de ejecuciones
function processItem(item) {
  console.count('processItem called');
  // lógica...
}

// Stack trace sin error
function deepFunction() {
  console.trace('¿Cómo llegamos aquí?');
}

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

Console Styling

// Logs coloridos y estilizados
console.log(
  '%c ERROR %c Falla en la autenticación',
  'background: #ff0000; color: white; padding: 2px 6px; border-radius: 3px;',
  'color: #ff0000;'
);

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

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

Breakpoints Condicionales

Breakpoints son más poderosos que simples pausas en el código.

Breakpoints con Condición

En Chrome DevTools, haz clic derecho en un breakpoint y selecciona "Edit breakpoint":

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

// Condición en el breakpoint: order.total > 1000
// Solo pausa cuando order.total sea mayor que 1000

Logpoints (Breakpoints que no pausan)

Agrega logs sin modificar el código:

// En DevTools, agrega un logpoint con:
// "Procesando order:", order.id, "total:", order.total

// Equivalente a agregar console.log() en el código,
// pero sin necesitar modificar y recargar

DOM Breakpoints

Pausa cuando el DOM cambie:

  1. Inspecciona el elemento en el panel Elements
  2. Haz clic derecho → Break on
  3. Elige: subtree modifications, attribute modifications, o node removal
// Útil para encontrar código que modifica elementos inesperadamente
// DevTools va a pausar y mostrar exactamente cuál código hizo la modificación

Debugging de Código Asíncrono

Código asíncrono puede ser difícil de debuggear. Aquí están técnicas específicas:

Async Stack Traces

Chrome DevTools muestra el stack trace completo incluyendo llamadas asíncronas:

async function fetchUserData(userId) {
  // Breakpoint aquí muestra todo el camino así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);
}

// En el stack trace verás:
// - fetchUserData (async)
// - loadDashboard (async)
// - buttonClickHandler
// - (click event)

Promise Rejections

// Activar "Pause on caught exceptions" para debuggear promises rechazadas

async function riskyOperation() {
  try {
    const result = await mightFail();
    return result;
  } catch (error) {
    // Con "Pause on caught exceptions" activo,
    // el debugger para aquí antes del catch ejecutar
    console.error('Operación falló:', error);
    throw error;
  }
}

Event Listener Breakpoints

En el panel Sources → Event Listener Breakpoints:

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

// Útil para entender el flujo de eventos
// sin necesitar agregar breakpoints manualmente

Performance Profiling

Identificar cuellos de botella de performance es esencial para aplicaciones responsivas.

Performance Panel

// Inicia una grabación en el panel Performance
// Ejecuta la acción que está lenta
// Para la grabación y analiza

// Qué buscar:
// - Long Tasks (tareas > 50ms)
// - Layout Thrashing (muchos reflows)
// - Scripting time alto
// - Blocking time

Identificando Layout Thrashing

// ❌ MALO - Causa múltiples reflows
function updateElements(elements) {
  elements.forEach(el => {
    const height = el.offsetHeight; // Fuerza reflow (lectura)
    el.style.height = height + 10 + 'px'; // Fuerza reflow (escritura)
  });
}

// ✅ BUENO - Agrupa lecturas y escrituras
function updateElementsOptimized(elements) {
  // Primero, todas las lecturas
  const heights = elements.map(el => el.offsetHeight);

  // Después, todas las escrituras
  elements.forEach((el, i) => {
    el.style.height = heights[i] + 10 + 'px';
  });
}

Memory Profiling

// En el panel Memory, toma snapshots para identificar memory leaks

// Patrón común de memory leak:
class Component {
  constructor() {
    // Event listener que nunca es removido
    window.addEventListener('resize', this.handleResize);
  }

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

  // ❌ Sin cleanup, el listener mantiene referencia al componente
}

// ✅ Corrección:
class ComponentFixed {
  constructor() {
    window.addEventListener('resize', this.handleResize);
  }

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

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

Source Maps y Debugging de Código Minificado

Configurando Source Maps

// webpack.config.js
module.exports = {
  devtool: 'source-map', // Producción
  // o
  devtool: 'eval-source-map', // Desarrollo (más rápido)
};

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

Debugging con Source Maps

Con source maps configurados, puedes:

  1. Ver código original en el panel Sources
  2. Agregar breakpoints en el código TypeScript/ES6+
  3. Ver variables con nombres originales
  4. Stack traces apuntan a archivos originales
// Código TypeScript original
interface User {
  id: number;
  name: string;
}

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

Snippets y Overrides

Snippets Reutilizables

Crea snippets en DevTools para tareas repetitivas:

// Snippet: Limpiar localStorage y recargar
(function clearAndReload() {
  localStorage.clear();
  sessionStorage.clear();
  location.reload();
})();

// Snippet: Exportar datos del console
(function exportConsoleData() {
  const data = /* tus datos */;
  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: Monitorear todas las llamadas 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

Modifica archivos de producción localmente:

  1. En el panel Sources, crea una carpeta de overrides
  2. Selecciona un archivo y edita
  3. Los cambios persisten entre recargas
  4. Útil para testar fixes en producción sin deploy

Network Debugging

Filtros Avanzados

// En el panel Network, usa filtros:

// Por tipo
// is:running - Requisiciones en andamiento
// larger-than:1M - Mayores que 1MB
// status-code:500 - Solo status 500

// Por dominio
// domain:api.example.com

// Expresiones negativas
// -domain:analytics.com - Excluir dominio

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

Throttling y Offline

// Simular conexiones lentas
// Network panel → No throttling → Selecciona:
// - Slow 3G
// - Fast 3G
// - Offline

// O crea perfiles customizados:
// - Download: 100kb/s
// - Upload: 50kb/s
// - Latency: 500ms

Conclusión

Dominar las herramientas de debugging transforma tu productividad como desarrollador. En vez de quedarte adivinando dónde está el problema, puedes ir directamente a la causa raíz.

Algunas prácticas recomendadas:

  • Usa breakpoints condicionales en vez de múltiples console.log()
  • Aprende los atajos de teclado de DevTools
  • Utiliza logpoints cuando no quieras modificar código
  • Profile regularmente para identificar problemas de performance antes que se vuelvan críticos

Las herramientas están disponibles gratuitamente en todos los navegadores modernos. Vale invertir tiempo para dominarlas.

Si quieres continuar profundizando tus conocimientos en JavaScript, recomiendo conferir el artículo Web APIs Modernas del Navegador con JavaScript en 2025 donde exploramos APIs poderosas que pueden mejorar tus aplicaciones.

¡Vamos a por ello! 🦅

Comentarios (0)

Este artículo aún no tiene comentarios 😢. ¡Sé el primero! 🚀🦅

Añadir comentarios