Las DevTools del navegador no son una bolsa de trucos escondidos. Son el camino más corto entre «la página está rota» y evidencia real sobre el DOM, el CSS, las peticiones, el JavaScript y el renderizado. Mi regla: no empieces rociando console.log por todo el proyecto. Primero averigua qué capa mintió.
🎙️ Publicado y grabado: ·
Abre las DevTools con F12, Control Shift I o Command Option I. Después clasifica el síntoma. Una forma o un color equivocados son cosa de Elements. Los datos que no llegan son cosa de Network. Una interacción muerta es cosa de Console y Sources. Una página lenta es cosa de los tiempos de Network o de Performance. El estado que sobrevive a un refresco es cosa de Storage. Esa clasificación toma diez segundos y te ahorra media hora de ediciones al azar.
# Symptom → first panel
button is hidden → Elements / Styles
API data is absent → Network / Fetch-XHR
click does nothing → Console, then Sources
works only after hard refresh → Network cache + Application storage
page freezes while scrolling → Performance
keyboard cannot reach control → Elements / AccessibilityNo agregues logs primero. Un log cambia los tiempos, no ve nada de lo que falló antes de él y solo te cuenta lo que adivinaste imprimir. Conserva la página que falla, revisa la consola y el registro de peticiones que ya tienes, y pon un breakpoint donde la ejecución se desvía. Agrega un solo log dirigido cuando un breakpoint no pueda capturar el entorno.
El árbol de Elements es el DOM vivo, no necesariamente el archivo HTML que escribiste. Usa el selector de elementos, elige el nodo roto y revisa las reglas que coinciden, las declaraciones tachadas, los valores heredados y la pestaña Computed. Desactiva una declaración en lugar de borrarla. Agrega una propiedad temporal en DevTools, comprueba que el arreglo funciona y luego haz el cambio de verdad en el código fuente. Al refrescar, el experimento se pierde.
/* The declaration is crossed out because specificity wins */
.card button { color: white; }
#checkout .card button { color: black; }
/* Inspect Computed → color, then follow the winning rule. */
/* Fix the selector design; do not reach for !important by reflex. */
.checkout-button { color: white; }Uno: en Computed, revisa display, visibility, opacity, dimensiones, overflow y transform. Dos: revisa los ancestros por recortes y contextos de apilamiento. Tres: desactiva la regla sospechosa. Cuatro: copia la declaración corregida de vuelta a la hoja de estilos. Si el nodo del DOM ni siquiera existe, deja de depurar CSS y revisa la condición de renderizado o los datos que llegaron.
Activa Preserve log solo cuando la navegación sea parte del bug, limpia la consola, reproduce una vez y lee desde la primera línea roja hacia abajo. Los errores posteriores suelen ser consecuencia. Haz clic en el archivo y la línea para abrir Sources. Expande los objetos con cuidado: un objeto registrado puede mostrar su estado ya mutado, así que guarda una copia cuando el historial importe.
Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
at app.js:18:9
// Step 1: click app.js:18 and inspect the selector result.
const button = document.querySelector("#save"); // null
// Step 2: verify the live DOM and script timing.
// Step 3: fix the ID, or load code after the DOM exists.
document.addEventListener("DOMContentLoaded", init);No «arregles» esto con optional chaining si el botón es obligatorio. Eso solo convierte un error de cableado bien visible en un botón muerto. Averigua si el selector está mal, si el componente no se renderizó o si el script corrió demasiado pronto. Repara el contrato real.
Abre Network antes de reproducir, porque solo graba mientras está abierto. Filtra por Fetch o XHR, selecciona la petición y revisa status, URL final, método, request headers, payload, response headers, cuerpo de la respuesta, initiator y tiempos. Usa Copy as cURL cuando necesites separar el comportamiento del navegador del comportamiento del servidor, pero borra cookies y authorization antes de compartirlo.
GET https://example.com/api/users 404 (Not Found)
Access to fetch at 'https://api.example.net/users' from origin
'https://example.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present.Para el 404, revisa la Request URL final, compárala con la ruta que está desplegada en el servidor, verifica base paths y mayúsculas, y luego llama a esa URL exacta de forma directa. Para CORS blocked, busca la petición fallida o el preflight OPTIONS, revisa sus response headers y configura el servidor de la API para permitir el origen, el método y los headers exactos. No instales una extensión del navegador y no agregues el response header desde el JavaScript del frontend: ese permiso lo da el servidor.
Una petición lenta no es un solo número. Queueing puede indicar límites de conexión o presión en el main thread. DNS, conexión y TLS apuntan al costo de establecer la conexión. Waiting, lo que muchos llaman time to first byte, apunta a latencia del servidor o de la red. Content download apunta al tamaño del payload o a un stream lento. Lee la pestaña Timing antes de decidir de qué equipo es el retraso.
# Example timing interpretation
Queueing 2 ms
DNS 0 ms # reused connection or cached lookup
Initial conn 0 ms
Waiting 842 ms # investigate server work / upstream latency
Download 18 ms # payload transfer is not the problem
Cache-Control: public, max-age=31536000, immutable
Cache-Control: no-cache # may store; must revalidate
Cache-Control: no-store # must not storeUsa Disable cache solo mientras DevTools está abierto y solo para una prueba controlada. Primero reproduce con la caché normal, luego compara con la caché desactivada y después mira la columna Size para distinguir memory cache, disk cache o bytes transferidos. Si los usuarios ven assets viejos con hash, revisa la caché del HTML y el service worker antes de culpar a la caché de assets.
El panel Application expone cookies, local storage, session storage, IndexedDB, cache storage y service workers. Revisa la clave exacta antes de borrarla. «Clear site data» destruye la evidencia y puede hacer que el bug desaparezca sin enseñarte por qué. Para las cookies, verifica domain, path, expiración, SameSite, Secure, HttpOnly y si la petición realmente incluyó la cookie.
# Cookie exists in Storage but is absent from the request?
Set-Cookie: session=abc; Path=/; Secure; HttpOnly; SameSite=Lax
# Check in order:
1. Request URL matches Domain and Path
2. HTTPS is used when Secure is set
3. SameSite permits this request context
4. Fetch uses credentials when cross-origin
5. Third-party-cookie policy did not block itSi un service worker viejo sirve una app vieja, abre Application, revisa el worker activo y los nombres de caché, activa update on reload para una sola prueba y elimina el registro recién cuando ya documentaste el estado. El arreglo en producción es una caché versionada y una estrategia de activación pensada, no un aviso pidiéndole a cada usuario que vacíe su navegador.
Un breakpoint congela el programa con las variables locales, el scope, la pila de llamadas y el evento que lo disparó intactos. Pon un breakpoint de línea justo antes de la rama defectuosa. Usa un breakpoint condicional cuando falla un solo registro, un breakpoint de XHR para peticiones que contienen un fragmento de URL, un breakpoint de event listener para clics misteriosos, y pause on caught exceptions cuando una librería se traga el error útil.
// Conditional breakpoint expression:
order.id === "ord_1842" && order.total < 0
// Then inspect, without editing production code:
order
order.items
subtotal
new Error().stack
DevTools failed to load source map:
Could not load content for /assets/app.js.map: HTTP error: status code 404Uno: mira el final del JavaScript compilado y busca su sourceMappingURL. Dos: abre esa URL de mapa en Network y confirma el 404. Tres: sube el mapa a esa ruta exacta o cambia la referencia que escribe el build. Cuatro: si los mapas deben quedar privados, quita la referencia pública y súbelos solo a tu servicio de errores. La aplicación puede seguir funcionando, pero depurar código minificado será un dolor innecesario.
Graba la interacción lenta más pequeña que puedas: inicia, haz una acción, detén. Busca una tarea larga en el main thread y ábrela en scripting, recálculo de estilos, layout, paint y árbol de llamadas. El amarillo indica trabajo de JavaScript, el violeta apunta a estilos y layout, y el verde a paint. El color es una pista, no un veredicto: selecciona el evento y mira quién lo originó.
# A common layout-thrashing pattern
for (const row of rows) {
row.style.width = container.offsetWidth + "px"; // read/write repeated
}
# Batch the read, then the writes
const width = container.offsetWidth;
for (const row of rows) row.style.width = width + "px";Usa CPU throttling para sacar a la luz interacciones frágiles, pero etiqueta el resultado como simulado. Para la carga, graba un reload y conecta los recursos grandes o las tareas largas con un retraso que el usuario nota. No optimices el bloque más bonito del flame chart. Arregla el trabajo que bloquea la interacción real.
El modo dispositivo es excelente para el ancho del viewport, los breakpoints responsivos, la emulación de eventos táctiles, la orientación y el throttling de red. No es un iPhone real, ni una GPU de Android, ni la interfaz de un navegador móvil, ni un teclado en pantalla. Úsalo para encontrar fallos probables y después prueba los caminos importantes en dispositivos físicos.
# Quick responsive checks
320 CSS px wide · 200% zoom · landscape · slow network
# Accessibility checks in Elements
computed role · accessible name · keyboard focus · contrast
# Bad: clickable div with no keyboard semantics
<div onclick="save()">Save</div>
# Good: native behavior and accessible name
<button type="button">Save</button>Revisa el panel Accessibility para ver el rol y el nombre accesible, y después recorre la página con Tab, Shift Tab, Enter, Espacio y Escape. Los paneles automáticos de issues detectan etiquetas faltantes y fallos de contraste, pero no te dicen si el orden de foco tiene sentido ni si lo que anuncia un lector de pantalla sirve de algo. El HTML nativo le gana a un div reparado.
Conserva el fallo, reprodúcelo una vez y sigue la evidencia desde el síntoma que ve el usuario hasta la capa responsable. Cambia una sola variable, repite los mismos pasos y anota qué refutó tu primera teoría. Eso es más rápido que juntar veinte causas plausibles.
# Triage order
1. Reproduce with exact URL, account, viewport, and action
2. Console: first red error, file, line, call stack
3. Network: URL, status, payload, response, initiator, timing
4. Elements: live DOM, winning CSS, box and accessibility tree
5. Storage: exact cookie/key/worker involved
6. Sources: breakpoint before divergence; inspect call stack
7. Performance: record one slow action
8. Fix source, reload cleanly, repeat original steps
# Error shorthand
404 → verify final URL and deployed route
CORS blocked → inspect preflight; fix API response headers
Uncaught TypeError → click line; inspect value and lifecycle
source map 404 → fix map URL/deployment or remove reference
stale page → compare cache, service worker, and storage
# Useful shortcuts
F12 / Ctrl+Shift+I / Cmd+Option+I open DevTools
Ctrl+Shift+P / Cmd+Shift+P command menu
Ctrl+Shift+C / Cmd+Shift+C pick an elementEl navegador ya grabó casi toda la escena del crimen. Mi orden preferido es Network antes que los logs, breakpoints antes que más logs, y una grabación de performance bien acotada antes de cualquier optimización. Las DevTools se vuelven poderosas cuando reemplazan suposiciones, no cuando memorizas todos los paneles.