wandres.dev
PERFORMANCE III · Diagnóstico por síntoma

El perfil de un tercero pesado: medir lo que no controlas

Cómo aislar el coste real de un script externo, la cadena de cargas que esconde, el experimento de bloqueo que lo cuantifica, y cómo se presenta el dato a quien decide.

⏱ 18 min

Una parte considerable del tiempo de hilo principal de casi cualquier sitio comercial pertenece a código que nadie del equipo ha escrito, que nadie ha revisado, y que se actualiza sin avisar. Es también la parte más difícil de tocar, porque cada uno de esos scripts está ahí porque alguien lo pidió y lo defiende. El trabajo técnico aquí no es optimizar: es medir con precisión suficiente como para que la conversación deje de ser una opinión.

🎯 Al terminar esta lección sabrás
  • Aislar en el perfil el trabajo que pertenece a cada dominio de terceros.
  • Descubrir la cadena de cargas que un solo script provoca.
  • Cuantificar el coste real con el experimento de bloqueo.
  • Presentar el dato en términos que sirvan para tomar una decisión de producto.

La silueta

Un tercero pesado produce una forma con tres rasgos reconocibles.

Tareas largas con nombres de fichero de otro dominio. En el gráfico de llamas, los marcos superiores pertenecen a un dominio que no es el tuyo. Con la lista de ignorados bien configurada esto destaca todavía más, porque tu código queda colapsado y el suyo no.

Actividad en oleadas, no continua. Un script de terceros suele inicializarse, quedarse quieto, y volver a despertar cuando ocurre algo: un evento de usuario, un temporizador, la carga de otro módulo suyo. Esa periodicidad irregular es muy característica.

Peticiones a dominios que no reconoces, muchas veces en cadena: un script carga otro, que carga un tercero, que carga un píxel de seguimiento. La pista de red muestra la cadena con la relación temporal que la delata.

Medir el coste por dominio

El primer dato objetivo es cuánto tiempo de hilo principal consume cada origen. El panel lo calcula en su análisis automático; tenerlo también en un snippet permite medirlo en cualquier momento y en producción.

// Coste por dominio: bytes, peticiones y tiempo de fotograma atribuido
(() => {
  const propio = location.origin;
  const porDominio = {};
  const anota = (dominio, campo, valor) => {
    porDominio[dominio] = porDominio[dominio] ||
      { dominio, peticiones: 0, kb: 0, msRed: 0, msHilo: 0, scripts: 0 };
    porDominio[dominio][campo] += valor;
  };

  for (const e of performance.getEntriesByType('resource')) {
    let o; try { o = new URL(e.name).origin; } catch { continue; }
    anota(o, 'peticiones', 1);
    anota(o, 'kb', (e.encodedBodySize || 0) / 1024);
    anota(o, 'msRed', e.duration);
    if (e.initiatorType === 'script') anota(o, 'scripts', 1);
  }

  // Atribucion de tiempo de hilo principal via fotogramas largos
  if (PerformanceObserver.supportedEntryTypes?.includes('long-animation-frame')) {
    const obs = new PerformanceObserver(l => {
      for (const f of l.getEntries()) for (const s of f.scripts) {
        let o; try { o = new URL(s.sourceURL || location.href).origin; } catch { continue; }
        anota(o, 'msHilo', s.duration);
      }
    });
    obs.observe({ type: 'long-animation-frame', buffered: true });
  }

  window.costeTerceros = () => {
    const filas = Object.values(porDominio)
      .map(d => ({
        dominio: d.dominio === propio ? '(propio) ' + d.dominio : d.dominio,
        peticiones: d.peticiones,
        kb: Math.round(d.kb),
        msRed: Math.round(d.msRed),
        msHilo: Math.round(d.msHilo),
        scripts: d.scripts
      }))
      .sort((a, b) => (b.msHilo - a.msHilo) || (b.kb - a.kb));
    console.table(filas);
    const terceros = filas.filter(f => !f.dominio.startsWith('(propio)'));
    console.log('Dominios de terceros:', terceros.length,
      '| KB de terceros:', terceros.reduce((s, f) => s + f.kb, 0),
      '| ms de hilo de terceros:', terceros.reduce((s, f) => s + f.msHilo, 0));
    return filas;
  };

  console.log('Usa la pagina unos segundos y ejecuta costeTerceros()');
})();

La columna de milisegundos de hilo es la que importa y la que casi nunca se mide. Todo el mundo discute el peso en kilobytes de un script de terceros, que es la magnitud menos relevante: un fichero de treinta kilobytes que ejecuta cuatrocientos milisegundos de trabajo hace mucho más daño que uno de doscientos kilobytes que se limita a registrar un manejador.

La cadena que esconde

Un solo fragmento de código insertado en el HTML puede acabar cargando quince ficheros de ocho dominios. Ver la cadena completa es lo que convierte una discusión sobre “una etiqueta pequeña” en una sobre lo que esa etiqueta realmente trae.

En el panel de red, la columna de iniciador y su vista de cadena responden a esto directamente: para cada petición, quién la provocó. Ordenando por iniciador aparece el árbol completo.

El caso más frecuente y más caro es el gestor de etiquetas: un solo fragmento en el HTML que carga un contenedor que a su vez ejecuta una configuración que carga todo lo que el equipo de marketing haya añadido, sin que nadie de ingeniería lo revise ni se entere. Eso significa que la superficie de rendimiento del sitio cambia sin ningún despliegue, y que un perfil de la semana pasada puede no describir la situación de hoy.

⚠️
Cuidado

Un script de terceros cargado de forma diferida sigue costando; solo cuesta más tarde. Diferirlo mejora las métricas de carga y no reduce ni un milisegundo el tiempo total de hilo principal. Si el script se inicializa justo cuando el usuario empieza a interactuar, diferirlo puede incluso empeorar la respuesta mientras mejora los números de carga. Es una de las formas más limpias de mejorar un informe sin mejorar el producto.

El experimento que cuantifica

Medir el coste dentro del perfil da una cifra discutible. El experimento de bloqueo da una cifra que no admite discusión, y consiste en medir la misma página dos veces: con el dominio bloqueado y sin él. La capacidad de bloquear peticiones por patrón de dominio es la herramienta.

El procedimiento tiene que ser riguroso para que el número valga algo. Misma ralentización de CPU y de red en las dos medidas. Mismo estado de caché. Al menos cinco repeticiones de cada configuración, y comparar medianas, no la mejor ni la media, porque una sola ejecución anómala mueve la media y no la mediana. Y medir las tres cosas: la métrica de pintura mayor, el tiempo total de bloqueo, y el tiempo total de hilo principal.

El resultado es una frase con la forma “bloquear este dominio mejora la pintura de contenido mayor en ochocientos milisegundos y reduce el tiempo de bloqueo en cuatrocientos”. Esa frase es el producto entregable de todo este trabajo.

Merece la pena comprobar además si el sitio funciona con el dominio bloqueado. Muchas veces sí, completamente, y eso convierte al script en un candidato claro. Otras veces se rompe algo visible, y entonces el bloqueo deja de ser una opción y pasa a ser una negociación sobre cómo cargarlo.

Las cuatro salidas

Cuantificado el coste, hay exactamente cuatro caminos y conviene presentarlos juntos, con su coste y su efecto.

Quitarlo. Es sorprendentemente viable más veces de lo que parece. Muchos scripts llevan años cargándose porque nadie se atreve a quitarlos, y su panel de control lleva meses sin que nadie lo abra. La comprobación previa es preguntar quién ha mirado los datos de esa herramienta en los últimos noventa días.

Sustituirlo por una fachada. Para cualquier cosa que solo hace falta tras una interacción —un vídeo incrustado, un chat de soporte, un mapa— se muestra una imagen estática que parece el componente y solo se carga el script de verdad cuando el usuario lo pide. Es la técnica con mejor relación entre esfuerzo y ganancia de toda la categoría, porque la mayoría de los usuarios nunca dispara la carga.

Moverlo fuera del hilo principal. Existen soluciones que ejecutan scripts de terceros en un worker interceptando su acceso al DOM. Funcionan para una parte de los casos y no para todos, y añaden una capa que hay que mantener.

Cargarlo mejor. Diferirlo, retrasarlo hasta la inactividad, o condicionarlo a que el usuario llegue a cierto punto. Es la salida menos efectiva y la más fácil de acordar, y por eso es la que suele elegirse.

El dato que cambia la conversación no está en milisegundos sino en la unidad que usa quien decide

Todo el trabajo técnico de esta lección se puede tirar a la basura en la reunión donde se presenta, y se tira casi siempre por el mismo motivo: se presenta en la unidad equivocada. Decir que un script cuesta cuatrocientos milisegundos de hilo principal es información perfecta para un ingeniero y ruido para todos los demás. Quien tiene que decidir si esa herramienta se queda no sabe si cuatrocientos milisegundos son muchos, y su intuición le dice que menos de medio segundo no puede importar. Para que el dato pese hay que traducirlo, y hay tres traducciones que funcionan. La primera es al número que esa persona ya vigila: si el equipo tiene un objetivo de rendimiento, di qué fracción del presupuesto se lleva ese script. “El presupuesto entero de esta página son mil quinientos milisegundos y esta herramienta se lleva cuatrocientos” es una frase que se entiende sin saber nada de navegadores. La segunda es a la tasa de abandono o de conversión, si tienes datos propios que las relacionen con la velocidad; y si no los tienes, conseguirlos con un experimento controlado en una fracción del tráfico es la inversión con más retorno que puede hacer un equipo de rendimiento, porque convierte todas las conversaciones futuras en aritmética. La tercera, y la más eficaz cuando no hay datos, es a comparación explícita entre alternativas: no “este script es caro”, sino “esta herramienta cuesta lo mismo que toda nuestra página de producto; si la mantenemos, no podemos cumplir el objetivo; estas son las tres opciones y esto cuesta cada una”. Un ingeniero que llega con esa tabla toma decisiones; uno que llega con un perfil, informa. Y una advertencia final sobre el tono, porque es donde se pierden estas negociaciones: el script está ahí porque resuelve un problema real de alguien. Presentar el dato como una acusación garantiza una posición defensiva y ningún cambio. Presentarlo como un coste que hay que decidir si compensa, con las alternativas ya estudiadas, funciona muchas más veces de las que la experiencia colectiva de la profesión sugiere.