Los errores que ya no deberías cometer
El catálogo de lo que el track ha ido desmontando, cada error con la intuición que lo hace atractivo, lo que cuesta de verdad en milisegundos, la señal que delata que lo estás cometiendo y qué hacer en su lugar.
Ninguno de estos errores lo comete gente descuidada. Todos son intuiciones razonables, la mayoría fueron ciertas en algún momento, y varios aparecen todavía en listas de buenas prácticas publicadas. Esa es exactamente la razón por la que hay que catalogarlos: un error que se reconoce como error se evita solo, y uno que sigue pareciendo una optimización se comete indefinidamente. Nueve, con su intuición, su coste real en milisegundos, la señal que los delata y el remedio.
- Reconocer los nueve errores por la señal que dejan, no por la intención de quien los comete.
- Cuantificar el coste real de cada uno en la métrica que afecta.
- Sustituir cada uno por la práctica correspondiente sin perder lo que el error pretendía conseguir.
- Detectar la estructura común a los nueve, que es lo que permite reconocer el décimo.
Errores de método
Uno: optimizar sin medir. La intuición es impecable: sabes que ese bucle es caro, que esa librería pesa, que ese componente se renderiza de más. Y probablemente tienes razón en las tres cosas. El problema no es que la optimización sea falsa, es que no hay ninguna razón para que la cosa cara que has encontrado sea la cosa cara que sufren tus usuarios. En cualquier página, mirando lo suficiente, se encuentra algo caro; esa es una propiedad del software, no un diagnóstico.
La señal: no puedes decir, antes de hacer el cambio, cuánto va a bajar la métrica ni en qué percentil. Si tu predicción es “esto debería ayudar”, no has medido.
En su lugar: los nueve pasos de el método completo de diagnóstico, y en particular el paso seis, que es el que convierte una métrica en una lista ordenada de sumandos. Ninguna optimización se empieza sin saber qué fracción del problema representa.
Dos: medir en el portátil de desarrollo. La intuición es que un ordenador es un ordenador y el navegador es el mismo. Los números dicen otra cosa. La herramienta de auditoría aplica por defecto un multiplicador de CPU de 4x y limita la red a 150 milisegundos de latencia con 1,6 Mbps de bajada precisamente porque medir sin eso no representa a casi nadie. Y 4x sigue siendo optimista frente a los aparatos que están en el percentil 75 real de muchos productos.
La señal: tu tiempo de carga local es de 0,8 segundos y el percentil 75 de campo es de 4,2. No hay ninguna optimización que explique un factor cinco; lo que hay es un entorno de medida distinto.
En su lugar: calibra la limitación contra un aparato físico, siguiendo throttling de CPU y de red, y ten uno en el cajón: probar en gama baja. El factor exacto que separa tu máquina de la de tus usuarios está en el móvil frente al portátil de desarrollo.
Tres: mirar la media y el agregado. La intuición viene de la estadística de instituto: la media resume. Para una distribución de tiempos de carga, que es asimétrica y con cola larga, la media resume mal y además no le corresponde a ningún usuario. Y el agregado de todas las páginas y todos los dispositivos oculta exactamente el segmento que sufre, porque un problema concentrado se diluye al promediarlo con el noventa por ciento que va bien.
La señal: tu número global mejora un ocho por ciento y las quejas siguen llegando de la misma gente.
En su lugar: percentil 75 y no media, y cortar siempre por las cinco dimensiones de segmentación antes de sacar ninguna conclusión.
El error de objetivo
Cuatro: perseguir la puntuación en vez de la experiencia. Este merece tratamiento aparte porque no es un error técnico sino de sustitución de objetivo, y por eso sobrevive a gente que domina todo lo anterior. La puntuación de rendimiento de la auditoría es una media ponderada de métricas de laboratorio: el tiempo total de bloqueo pesa un 30 %, el pintado del elemento más grande un 25 %, el desplazamiento acumulado un 25 %, y el primer pintado con contenido y el índice de velocidad un 10 % cada uno.
Tres consecuencias que hay que tener presentes a la vez:
- La interacción hasta el siguiente pintado no está en la puntuación, porque en una ejecución de laboratorio no hay interacciones. Un sitio puede puntuar 95 y tener un INP de campo desastroso.
- La curva no es lineal. Ganar 200 milisegundos cuando estás mal casi no mueve la puntuación; ganarlos cerca del umbral la mueve mucho. La relación entre “he mejorado” y “ha subido” es errática por diseño.
- La puntuación es optimizable sin tocar la experiencia. Retrasar la aparición del contenido grande para que el elemento del LCP sea uno más pequeño funciona: baja el LCP y empeora la página.
La señal: alguien pregunta por qué no estáis en verde antes de preguntar a qué usuarios les va mal.
En su lugar: la puntuación es un indicador de diagnóstico, y muy bueno, mientras no sea el objetivo. La regla operativa está en cuál manda para decidir y el desglose de qué le puedes creer, en Lighthouse y su simulación: cree lo que observa, desconfía de lo que estima.
Errores de remedio
Cinco: el preload de más. La intuición es que si precargar un recurso lo adelanta, precargar cinco adelanta cinco. El ancho de banda no funciona así: una precarga no crea capacidad, la redistribuye. Cinco recursos declarados críticos equivalen a ninguno, y el que de verdad importaba ahora compite con cuatro que no lo eran. El caso documentado en el track cuesta 500 milisegundos de regresión en el LCP por una etiqueta que pasó la revisión de código como mejora, y encima adelantaba una fuente que no cambiaba nada visible.
La señal: hay más de dos o tres etiquetas de precarga en la cabecera y nadie recuerda quién puso cada una ni contra qué se midió.
En su lugar: precarga solo lo que el navegador no puede descubrir por sí mismo, y comprueba primero si el escáner de precarga ya lo ve. Los criterios exactos están en preload: cuándo y cómo y los modos de fallo en cuándo las pistas hacen daño.
Seis: will-change preventivo. La intuición es que avisar al navegador de que algo va a moverse solo puede ayudar. will-change no es un acelerador: es una reserva de capa que se paga desde que la declaración se aplica y mientras siga aplicándose. En una hoja de estilos, sobre un selector que empareja con muchos elementos, eso es memoria de vídeo reservada permanentemente para animaciones que quizá nunca ocurran. Un panel a pantalla completa promocionado en un móvil de densidad tres cuesta 11,3 MB; treinta tarjetas de 300 por 200 en el mismo aparato, unos 60 MB.
Y cuando la memoria de vídeo se agota el navegador no falla: degrada, deja de promocionar y regenera texturas. El síntoma es una página que va bien un rato y luego da tirones sin razón aparente, con el perfil del hilo principal limpio, que es de los diagnósticos más difíciles que hay.
La señal: hay apariciones de will-change o de translateZ(0) en la hoja de estilos que nadie puede justificar con una medida.
En su lugar: aplicarlo con ciclo de vida —ponerlo cuando la animación es inminente, quitarlo al terminar— según capas y composición. Y no confundirlo con contain, que es otra cosa distinta: contain, los cuatro tipos.
Siete: la carga diferida en el elemento del LCP. La intuición es que diferir imágenes es bueno, luego diferirlas todas es mejor. Pero una imagen diferida pierde el escaneo de precarga, tiene que esperar al layout para saber si es visible y además arranca en la cola de prioridad baja. El daño es acumulativo: entre 300 y 500 milisegundos en un caso normal, y entre 500 y 1.500 en un móvil con red mediocre. Nueve caracteres, medio segundo o más, sobre la única métrica de carga que se usa para clasificar.
La señal: tu imagen principal tiene loading="lazy" porque el componente de imagen lo pone por defecto. Es el origen más frecuente de este error: nadie lo decidió.
En su lugar: la regla es que el elemento del LCP nunca se difiere y además se le sube la prioridad. Los detalles, incluido el margen de anticipación real de cada navegador, están en loading=lazy y sus trampas y en decoding y fetchpriority.
Ocho: confundir el peso del bundle con su coste. La intuición es la más arraigada del oficio: los kilobytes son la unidad, luego menos kilobytes es mejor y todos los kilobytes valen igual. No valen igual. Un kilobyte de imagen se descarga y se decodifica, buena parte de eso fuera del hilo principal. Un kilobyte de JavaScript se descarga, se parsea, se compila y se ejecuta, y las tres últimas fases ocurren en el hilo principal. En un aparato de gama media, el procesamiento cuesta del orden de 2 a 4 milisegundos por kilobyte comprimido: 150 KB de JavaScript son unos 750 milisegundos de descarga a 1,6 Mbps más otros 300 a 600 de procesamiento.
De ahí se derivan dos correcciones incómodas. Que un presupuesto solo de bytes no protege del coste de ejecución, y por eso hay que presupuestar también el tiempo de bloqueo. Y que 40 KB de una librería de fechas y 40 KB de un icono en línea no son comparables, aunque el diferencial del paquete los muestre iguales.
La señal: vuestro informe de rendimiento tiene una sola cifra por ruta y esa cifra está en kilobytes.
En su lugar: las cuatro fases de un byte de JavaScript para el modelo, un byte de JavaScript frente a un byte de imagen para la comparación, y las cuatro magnitudes que hay que presupuestar a la vez en fijar un presupuesto y defenderlo.
El error de infraestructura
Nueve: el service worker sin vía de escape. La intuición aquí no es de rendimiento sino de despliegue: si algo sale mal, se despliega el arreglo. Con un service worker eso deja de ser cierto, y es de los pocos errores de frontend que no se arreglan con otro despliegue, porque el código está en el disco del usuario, decide qué se descarga, sobrevive al cierre del navegador y sigue ahí cuando tú ya corregiste el fallo en el servidor.
Los cuatro modos de fallo comparten una propiedad que los hace especialmente peligrosos: ninguno produce un error visible en tu servidor. Tu monitorización de origen está verde, tus métricas de laboratorio están perfectas, y una parte de tus usuarios no puede usar el sitio. Si el manejador responde a las navegaciones desde caché, el usuario recarga y recibe otra vez la copia rota; si el install de la versión nueva falla, el worker roto sigue activo y tu corrección no llega a nadie; si la CDN cachea el propio script del worker, la comparación byte a byte dice que no hay cambios; y si el manejador lanza una excepción, todas las peticiones del sitio se convierten en errores de red, incluida la que enviaba tus datos de campo.
La señal: tenéis un service worker en producción y nadie ha probado nunca el procedimiento de baja.
En su lugar: escribir y probar el mecanismo de emergencia antes de necesitarlo, y desplegar de forma progresiva vigilando la versión en campo: el despliegue seguro y la vía de escape, con las consecuencias exactas de forzar la activación en skipWaiting y clients.claim.
La tabla de repaso
| Error | La señal que lo delata | Coste típico |
|---|---|---|
| Optimizar sin medir | No puedes predecir la mejora antes de hacerla | Semanas de trabajo correcto en el sitio equivocado |
| Medir en el portátil | Local 0,8 s, campo 4,2 s | Un factor de 4 a 6 en el hilo principal |
| Media y agregado | El número global mejora y las quejas siguen | El segmento afectado no aparece |
| Perseguir la puntuación | Se pregunta por el verde antes que por el usuario | Optimizaciones que mueven el número y no la experiencia |
| Preload de más | Más de dos o tres precargas sin dueño | Hasta 500 ms de regresión en LCP |
will-change preventivo |
Está en la hoja de estilos, sin medida detrás | 11 MB por capa a pantalla completa, y tirones sin causa |
| Diferir el elemento LCP | Lo puso el componente por defecto | De 300 a 1.500 ms de LCP |
| Bytes como única unidad | Un solo número por ruta, en kilobytes | De 2 a 4 ms por KB de JavaScript, invisibles |
| Service worker sin salida | Nadie ha probado el procedimiento de baja | Usuarios atrapados, sin señal en tu monitorización |
Merece la pena mirar la lista entera de golpe, porque de cerca parecen nueve problemas distintos y de lejos son una sola operación mental repetida. En cada uno hay una cosa que importa —el tiempo que un usuario real espera, en su aparato, en su red— y un sustituto que es incomparablemente más cómodo de observar: una puntuación, un número de kilobytes, una media, un perfil grabado aquí, una etiqueta que “debería ayudar”. El sustituto no se elige por pereza; se elige porque es lo único que se puede mirar sin montar nada, y esa comodidad es precisamente lo que lo vuelve peligroso, porque lo que se puede mirar barato se acaba mirando siempre y termina siendo lo único que se mira. A partir de ahí el mecanismo se despliega solo y no requiere que nadie haga nada mal: se toman decisiones sobre el sustituto, se celebra cuando el sustituto mejora, y llega el día en que aparece un cambio que mejora el sustituto y empeora la cosa. Nadie lo detecta, porque el instrumento que lo detectaría es justo el que se dejó de mirar. Retrasar el contenido grande para que el elemento del LCP sea uno más pequeño es el caso de laboratorio de esta patología, pero el mismo movimiento está en precargar cinco cosas —el indicador es “he priorizado”, la cosa es “el recurso crítico llega antes”—, en poner will-change por si acaso —el indicador es “he pedido aceleración”, la cosa es “el fotograma cabe en 16 milisegundos”— y en presupuestar solo bytes —el indicador es el peso, la cosa es el tiempo de hilo principal—. La defensa no es desconfiar de los indicadores, que sería paralizante y además injusto: son útiles, son baratos y sin ellos no se trabaja. La defensa tiene dos piezas concretas. La primera es mantener siempre viva al menos una medida de la cosa misma, aunque sea cara e incómoda: el percentil 75 de campo del segmento que sufre, o directamente abrir la página en un aparato de gama baja una vez por semana; una sola observación de la realidad ancla todos los sustitutos. Y la segunda es más fina y más útil: cada vez que uses un indicador, ten escrita la frase que dice cómo podría separarse de la cosa. Si sabes decir “esta puntuación subiría sin que la experiencia mejorase si retrasáramos el bloque grande”, ya no puedes engañarte con ella, porque tienes el modelo de su fallo. Un indicador cuyo modo de fallo conoces es una herramienta excelente. Uno cuyo modo de fallo no has pensado nunca es una trampa esperando el momento, y el momento siempre llega, casi siempre con una fecha de entrega cerca y con alguien de buena fe presentando una mejora que no lo es.
- Recorre la tabla de repaso y busca en tu proyecto la señal de cada uno, en ese orden. Anota solo los que encuentres, sin arreglar nada todavía.
- Cuenta las etiquetas de precarga y las apariciones de
will-changeytranslateZde tu hoja de estilos, y comprueba una por una si hay una medida detrás. - Comprueba si el elemento del LCP de tus tres rutas principales tiene carga diferida heredada del componente de imagen.
- Si tenéis service worker, ejecuta hoy el procedimiento de baja en un entorno de pruebas y cronométralo.
- Elige el error de la lista que más caro te salga y escribe la frase que separa su indicador de la cosa que mide. Guárdala donde la vea el equipo.