wandres.dev
ESTRATEGIAS DE RENDER · SSR, SSG, ISR y CSR

El árbol de decisión de la estrategia de render

Las preguntas en el orden correcto, el árbol completo, cinco rutas reales resueltas paso a paso, y las trampas que llevan a la respuesta equivocada.

⏱ 18 min

Con las cuatro estrategias definidas, su aritmética hecha y el coste de la hidratación cuantificado, la elección deja de ser una cuestión de preferencia. Son tres preguntas encadenadas cuyas respuestas dependen del contenido y no de la tecnología, y casi siempre la primera se responde mal. Esta lección da el árbol, lo aplica a cinco rutas reales y enumera las trampas.

🎯 Al terminar esta lección sabrás
  • Responder, para una ruta cualquiera, las tres preguntas que determinan su estrategia.
  • Recorrer el árbol de decisión completo hasta una hoja concreta.
  • Aplicarlo a cinco rutas de un producto real y justificar cada resultado.
  • Reconocer las cuatro trampas que producen la respuesta equivocada.

Las preguntas, en orden

Primera: ¿el contenido depende de quién lo mira? No “¿hay algo personal en la página?” —siempre lo hay, aunque sea el nombre en la esquina— sino “¿el contenido principal, el que produce el LCP y el que se indexa, es distinto para cada usuario?”.

Segunda: ¿cada cuánto cambia? Nunca entre despliegues, cada hora, o en cada petición.

Tercera: ¿cuánto contenido viejo se tolera? Cero, unos minutos, o un día.

El orden importa. La primera determina si la respuesta puede cachearse en el borde, que es la decisión con mayor impacto de todas. La segunda determina si sirve la generación estática. La tercera decide entre estático con caducidad y servidor por petición.

Y hay una cuarta que no ramifica pero condiciona la hoja: ¿cuánta interactividad hay realmente? Una vez decidido cómo se produce el HTML, esta decide cuánto JavaScript acompaña.

El árbol

flowchart TD
A[Ruta nueva] --> B{El contenido principal depende del usuario}
B -->|No| C{Cambia entre despliegues}
B -->|Si| D{Que fraccion de la pagina es personal}
C -->|No| E[Generacion estatica]
C -->|Si| F{Se tolera contenido algo viejo}
F -->|Si| G[Regeneracion incremental]
F -->|No| H[Servidor por peticion con streaming]
D -->|Poca| I[Caparazon estatico y huecos por streaming]
D -->|Mucha| J{Hay que indexarla o compartirla}
J -->|Si| H
J -->|No| K[Cliente tras autenticacion]
E --> L{Hay partes interactivas}
L -->|No| N[Cero JavaScript]
L -->|Si| M[Islas solo en esas partes]
style E fill:#a6e3a1,color:#11111b
style G fill:#94e2d5,color:#11111b
style H fill:#89b4fa,color:#11111b
style I fill:#cba6f7,color:#11111b
style K fill:#f9e2af,color:#11111b
style M fill:#a6e3a1,color:#11111b
style N fill:#a6e3a1,color:#11111b

Las hojas, con su justificación:

Generación estática. Tiempo de respuesta mínimo, cacheable indefinidamente, cero cómputo por petición. Con islas si hay interactividad, sin nada de JavaScript si no la hay.

Regeneración incremental. El tiempo de respuesta del estático a cambio de aceptar una ventana de obsolescencia. La cifra que hay que vigilar es la tasa de aciertos de caché, no la latencia media.

Servidor por petición con streaming. Cuando el contenido tiene que estar al día o es personal e indexable. El streaming no es opcional aquí: es lo que evita que el tiempo del servidor se convierta íntegro en tiempo de LCP.

Caparazón estático con huecos por streaming. La hoja más infravalorada y a menudo la correcta. La página se cachea en el borde y las partes personales llegan aparte, por streaming en la misma respuesta o por una petición posterior.

Cliente tras autenticación. Legítima cuando no hay nada que indexar, la primera carga ocurre una vez por sesión larga y lo que importa es la latencia de las interacciones posteriores. Un editor, un panel de trabajo, una herramienta interna.

Cinco rutas reales

Una tienda con seis rutas principales. El ejercicio es el mismo que deberías hacer con las tuyas.

Portada. ¿Depende del usuario? No: el contenido principal es el mismo para todos, aunque la cabecera muestre el nombre. ¿Cambia entre despliegues? Sí, hay una sección de destacados que se edita. ¿Se tolera contenido viejo? Cinco minutos, sí. → Regeneración incremental con revalidación de cinco minutos, más una isla para el carrito de la cabecera.

Ficha de producto. ¿Depende del usuario? No. ¿Cambia? El precio y el stock, sí. ¿Se tolera viejo? El precio no. → Aquí el árbol lleva a servidor por petición, pero conviene mirar dos veces: la descripción, las fotos y las reseñas no cambian, y son el noventa y cinco por ciento del peso y del LCP. La respuesta buena es caparazón estático con hueco por streaming para precio y disponibilidad. La ruta se cachea, y lo volátil llega en su propio trozo.

Resultados de búsqueda. ¿Depende del usuario? El contenido depende de la consulta, no de la persona. ¿Cambia? Con el catálogo. ¿Se tolera viejo? Unos minutos, sí. → Regeneración incremental por combinación de parámetros para las consultas frecuentes y servidor por petición para la cola larga. Si tienes las diez mil consultas más habituales, las cacheas y cubres la mayoría del tráfico.

Carrito. ¿Depende del usuario? Totalmente. ¿Fracción personal? Toda. ¿Hay que indexarlo? No. → Cliente tras autenticación, o servidor por petición si prefieres no enviar JavaScript. Ninguna de las dos es mala aquí porque el volumen de tráfico es bajo y la expectativa del usuario ya es la de una aplicación.

Documentación y ayuda. ¿Depende del usuario? No. ¿Cambia entre despliegues? No, se despliega con el contenido. ¿Interactividad? Un buscador. → Generación estática con una isla para el buscador. Cero JavaScript en el resto.

Panel de pedidos del cliente. ¿Depende del usuario? Sí, entero. ¿Indexable? No. Pero la primera carga importa, porque el usuario llega desde un correo con un enlace directo. → Servidor por petición con streaming: el marco y la cabecera salen inmediatamente, la lista de pedidos llega cuando la consulta termina.

Fíjate en que de seis rutas salen cinco estrategias distintas y ninguna aplicación coherente de una sola. Eso es lo normal.

La primera pregunta se responde mal casi siempre, y por eso casi todo el mundo renderiza en servidor mucho más de lo que necesita

“¿El contenido depende del usuario?” parece trivial y se contesta con un sí automático en cuanto la página tiene una sesión. El nombre en la esquina, el número del carrito, el botón de favorito con su estado, un banner de bienvenida distinto para nuevos: hay elementos personales, luego la página es personal, luego hay que renderizarla en cada petición, luego no se puede cachear. Ese razonamiento, que suena impecable, es el responsable de la mayor parte del cómputo desperdiciado en la web. Porque la pregunta correcta no es si hay algo personal, sino qué fracción del peso, del tiempo de renderizado y del elemento que produce el LCP es personal. Y la respuesta, en la inmensa mayoría de las páginas de producto, catálogo, artículo o listado, está entre el uno y el cinco por ciento. Estás renunciando a cachear el noventa y siete por ciento de la respuesta por culpa del tres por ciento restante, y ese tres por ciento son cuatro cadenas de texto y dos estados booleanos. Hay cuatro formas de separarlos, todas viejas y todas probadas: traer lo personal en una petición aparte después del primer pintado; mandarlo en el mismo flujo pero en su propio trozo, al final; sustituirlo en el borde con una función que edite la respuesta cacheada; o dejar que el propio navegador lo rellene desde una cookie con una pizca de JavaScript. La cuarta es la más humilde y la más eficaz para el caso del nombre en la cabecera. Lo importante no es cuál eliges, sino que la decisión de arquitectura de la ruta entera deje de estar tomada por su elemento más pequeño. Cuando revises un producto, la pregunta que descubre este problema en dos minutos es: “enséñame la página y señálame con el dedo lo que cambia de un usuario a otro”. Si lo que señala cabe en la esquina superior derecha, tienes una ruta cacheable disfrazada de dinámica, y arreglarlo suele valer más milisegundos que un trimestre de optimizaciones.

Las trampas del árbol

Confundir personalización con segmentación. Mostrar precios en la moneda del país no es personalización por usuario: son cinco variantes. Eso se cachea perfectamente con una clave de caché que incluya el país, y son cinco entradas, no cinco millones. Lo mismo con el idioma y con el tema claro u oscuro. La regla: si el número de variantes es pequeño y conocido, no es dinámico, es una dimensión de la clave de caché.

Tomar la decisión por el caso peor. “Hay una ruta que necesita datos en tiempo real, así que la aplicación es de servidor.” La existencia de una ruta dinámica no obliga a nada sobre las demás.

Olvidar la tasa de aciertos. La regeneración incremental se evalúa por su porcentaje de aciertos, no por su tiempo de respuesta medio. Con muchas rutas y poco tráfico por ruta, la mayoría de las peticiones caen en caché fría y el comportamiento real es el de servidor por petición, con la latencia añadida de guardar. Antes de elegirla, estima cuántas peticiones por hora recibe la ruta mediana, no la más popular.

Decidir una vez y no volver. La estrategia correcta cambia cuando cambia el producto: una ruta que era estática deja de serlo al añadirle recomendaciones personalizadas, y una que era dinámica se vuelve cacheable el día que separas el bloque personal. Revisar la lista de rutas y su estrategia una vez al trimestre cuesta media hora y evita que la arquitectura se quede anclada a decisiones de hace dos años.

⚔️ Recorre el árbol con tus rutas
  1. Aplica las tres preguntas a tus diez rutas más visitadas y anota la hoja a la que llega cada una.
  2. Para las que salgan dinámicas, señala físicamente en la pantalla qué cambia de un usuario a otro y estima su porcentaje sobre el total.
  3. Identifica al menos una ruta que sea segmentación disfrazada de personalización y calcula cuántas variantes tendría su caché.
  4. Estima la tasa de aciertos de las rutas candidatas a regeneración incremental usando tu tráfico real por ruta.
  5. Escribe la tabla de rutas y estrategias en un documento del repositorio, con fecha, y ponle recordatorio trimestral.