wandres.dev
NETWORK I · Leer el waterfall

Diagnosticar una carga lenta de principio a fin

El procedimiento completo aplicado a un caso real: de la queja vaga al dato concreto, con las cinco preguntas en el orden que las hace útiles.

⏱ 17 min

Las cuatro lecciones anteriores dan las piezas; esta las pone en secuencia. El procedimiento tiene cinco preguntas, cada una con su comprobación en el panel, y están ordenadas por poder de bisección: cada una descarta una parte del espacio de causas posibles, de forma que al llegar a la quinta ya sabes qué estás buscando. Aplicado con disciplina, un diagnóstico completo de una carga lenta cabe en diez minutos.

🎯 Al terminar esta lección sabrás
  • Ejecutar las cinco preguntas del diagnóstico en orden y saber qué descarta cada una.
  • Convertir una queja vaga en una medición reproducible antes de investigar.
  • Presentar el resultado con los datos que hacen accionable la conversación.
  • Reconocer cuándo el diagnóstico sale del panel de red y a dónde continúa.

Preparación: hacer la medición reproducible

Antes de la primera pregunta, tres ajustes sin los cuales el resto no significa nada.

Perfil limpio o incógnito, para que las extensiones no contaminen.

Condiciones fijadas: un perfil de throttling concreto, la caché en el estado que quieras medir, y el mismo tamaño de ventana. Sin condiciones fijadas, dos mediciones consecutivas no son comparables.

Decide qué caso mides: primera visita con caché vacía, o visita recurrente con caché caliente. Son dos escenarios con problemas distintos y hay que medirlos por separado. La mayoría de los equipos solo mide el primero, cuando el segundo es el que viven la mayoría de sus usuarios.

Y una recarga de las que graban desde el principio: el comando de recargar y grabar, o simplemente abrir el panel antes de navegar.

⚠️
Cuidado

Medir con la caché desactivada y sacar conclusiones para todos los usuarios es el error más común de este nivel. Con la caché desactivada estás midiendo el peor caso absoluto, que corresponde a la primera visita de un usuario nuevo. Es un caso legítimo y es uno de los dos, no el único.

Las cinco preguntas

Pregunta 1: ¿tarda el servidor en responder al documento?

Dónde se mira. La primera petición de la lista, su desglose, el tramo de espera.

Qué descarta. Si ese número es alto —por encima de unos pocos cientos de milisegundos— todo lo demás del waterfall está desplazado por esa cantidad, y ninguna optimización de frontend va a compensarlo. El diagnóstico termina aquí y continúa en el servidor.

El matiz. Compara con una petición a un recurso estático del mismo origen. Si el estático responde rápido y el documento no, el problema es el trabajo de generar esa página. Si los dos tardan, es infraestructura.

Pregunta 2: ¿qué forma tiene la cascada?

Dónde se mira. La silueta del conjunto, sin leer números.

Qué descarta. Cada forma manda a un sitio distinto, según la lección correspondiente: escalera manda a la cadena de dependencias, bloque largo con descargas grandes manda a los tamaños, muro de espera manda al backend, escalón de seis manda al protocolo, huecos mandan fuera del panel de red.

Por qué va tan pronto. Porque cuesta tres segundos y determina qué columna vas a leer después. Saltarse este paso es lo que hace que la gente lea columnas al azar.

Pregunta 3: ¿qué bloquea el primer píxel?

Dónde se mira. Los recursos que se piden antes de la línea del evento de contenido del DOM cargado, filtrando por hojas de estilo y scripts.

Qué buscas. Un script sin defer ni async en la cabecera, una hoja de estilos que importa otra, una fuente descubierta tarde, una cadena de peticiones antes de que aparezca nada.

La comprobación decisiva. Con el panel abierto y el throttling puesto, recarga y mira la pantalla, no el panel. El instante en que aparece contenido, comparado con el instante en que llegó cada recurso, dice cuál era el que bloqueaba.

Pregunta 4: ¿dónde está el peso?

Dónde se mira. Los filtros por tipo, uno por uno, anotando peticiones y bytes de cada categoría.

Qué te dice. El reparto del peso. Si el ochenta por ciento son imágenes, optimizar JavaScript es trabajo desperdiciado, y al revés.

El dato que suele sorprender. La diferencia entre transferido y descomprimido. Un recurso de texto donde ambos coinciden no está comprimido, y activar la compresión en el servidor suele ser una línea de configuración con un efecto de tres a uno.

// Reparto del peso por tipo, con aviso de lo que no viene comprimido
(() => {
  const r = performance.getEntriesByType('resource');
  const porTipo = {};
  const sinComprimir = [];
  for (const e of r) {
    const t = e.initiatorType || 'otro';
    porTipo[t] = porTipo[t] || { peticiones: 0, kbRed: 0, kbReal: 0 };
    porTipo[t].peticiones++;
    porTipo[t].kbRed += (e.transferSize || 0) / 1024;
    porTipo[t].kbReal += (e.decodedBodySize || 0) / 1024;
    if (e.decodedBodySize > 4096 && e.encodedBodySize >= e.decodedBodySize &&
        /\.(js|css|html|json|svg|txt|xml)(\?|$)/.test(e.name)) sinComprimir.push(e.name);
  }
  console.table(Object.entries(porTipo).map(([tipo, v]) => ({
    tipo, peticiones: v.peticiones, kbRed: Math.round(v.kbRed), kbReal: Math.round(v.kbReal),
    ratio: v.kbRed ? (v.kbReal / v.kbRed).toFixed(1) + 'x' : ''
  })).sort((a, b) => b.kbRed - a.kbRed));
  if (sinComprimir.length) console.warn('recursos de texto sin comprimir:', sinComprimir);
})();

Pregunta 5: ¿qué se pide que no hace falta?

Dónde se mira. La lista completa, ordenada por dominio.

Qué buscas. Peticiones duplicadas, recursos de terceros que nadie recuerda haber añadido, sondeos periódicos, imágenes descargadas a tamaño completo para mostrarse en miniatura, fuentes con pesos que no se usan, código de una funcionalidad que ya no existe.

Por qué va la última. Porque es la que más discusión genera y la menos técnica. Quitar un script de terceros es una conversación con quien lo puso, y conviene llegar a ella con los cuatro datos anteriores en la mano.

// Peticiones repetidas y reparto por dominio
(() => {
  const r = performance.getEntriesByType('resource');
  const cuenta = {}, porDominio = {};
  for (const e of r) {
    const limpia = e.name.split('?')[0];
    cuenta[limpia] = (cuenta[limpia] || 0) + 1;
    const d = new URL(e.name).host;
    porDominio[d] = porDominio[d] || { peticiones: 0, kb: 0 };
    porDominio[d].peticiones++;
    porDominio[d].kb += (e.transferSize || 0) / 1024;
  }
  const repetidas = Object.entries(cuenta).filter(([, n]) => n > 1).sort((a, b) => b[1] - a[1]);
  if (repetidas.length) console.table(repetidas.map(([url, n]) => ({ veces: n, url: url.slice(-70) })));
  console.table(Object.entries(porDominio)
    .map(([dominio, v]) => ({ dominio, peticiones: v.peticiones, kb: Math.round(v.kb) }))
    .sort((a, b) => b.kb - a.kb));
})();

Presentar el resultado

Un diagnóstico que no se puede accionar no sirve. La forma que funciona tiene tres frases.

El síntoma medido, no la queja. No “va lenta”, sino “con una red simulada de cuatro generación y caché vacía, el contenido principal tarda tres coma dos segundos en aparecer”.

La causa concreta, con el dato. No “hay demasiadas peticiones”, sino “el tiempo hasta el primer byte del documento son mil cien milisegundos, y la cadena crítica tiene cinco eslabones que suman otros ochocientos”.

La acción con su dueño. No “hay que optimizar”, sino “reducir el tiempo del servidor está en backend; romper la cadena crítica precargando la fuente y moviendo el CSS crítico al documento está en frontend y son dos cambios”.

La medición que no sabe qué escenario mide no mide nada

El error metodológico que invalida más diagnósticos de red no es técnico y no está en ninguna de las cinco preguntas: es medir un escenario y sacar conclusiones sobre otro. Una carga de página tiene al menos cinco escenarios distintos y sus waterfalls no se parecen en nada. Está la primera visita absoluta, con caché vacía, sin conexiones abiertas, sin nada resuelto: es el peor caso y es el que se mide siempre porque es el que sale al desactivar la caché. Está la visita recurrente con caché caliente, donde la mitad de las barras tienen tamaño transferido cero y el waterfall es un tercio de largo. Está la navegación interna, donde el documento ni se pide porque la aplicación cambia de vista en cliente y solo salen peticiones de datos. Está la vuelta atrás, que puede restaurar la página entera de la caché de retroceso sin ninguna petición, o no restaurarla y comportarse como una carga normal. Y está la recarga forzada, que revalida todo y que no es lo que hace ningún usuario nunca. Cinco escenarios, cinco diagnósticos, cinco conjuntos de acciones. Lo que ocurre en la práctica es que alguien mide el primero, encuentra que hay ochenta peticiones y dos megas, y dedica dos semanas a reducir el peso inicial, cuando resulta que el noventa por ciento de sus usuarios son recurrentes y la caché ya les estaba sirviendo casi todo. La disciplina que lo evita es enunciar el escenario antes de medir, en voz alta: “voy a medir la primera visita de un usuario nuevo en móvil”. Y la comprobación que decide en qué escenario invertir no está en las DevTools: está en los datos de tu producto, en la proporción entre visitantes nuevos y recurrentes. La herramienta te dice cómo se comporta un escenario; solo tus datos te dicen cuál de los cinco importa.