Saltar al contenido principal
Home
HexagonalGuru
Desarrollo Web

Rendimiento Web: Guía Práctica para Acelerar tu Aplicación

Avatar de HexagonalGuru

HexagonalGuru

Rendimiento Web: Guía Práctica para Acelerar tu Aplicación

Cada 100 ms de espera le cuesta conversiones a un eCommerce, posiciones en Google a un contenido y paciencia a tus usuarios. Los estudios de Google llevan años repitiéndolo, y las Core Web Vitals lo han convertido en factor de posicionamiento. El rendimiento no es una mejora estética: es negocio.

Y sin embargo, la mayoría de los equipos optimiza a ciegas: cambian cosas «que deberían ayudar», miran el tiempo de carga una vez al mes y nunca saben si movieron la aguja. Esta es la guía que usamos en HexagonalGuru para auditar y acelerar aplicaciones web — con ejemplos en Symfony/PHP, aunque los principios aplican a cualquier stack.

Regla Cero: Mide Antes de Tocar

Optimizar sin medir es tuning de pared: se nota en la factura, no en la experiencia. Antes de cambiar una línea, establece la línea base:

  • En el servidor: el profiler de Symfony en desarrollo, Blackfire o Tideways en producción, y el slow query log de la base de datos. Ahí es donde están el 80% de las sorpresas.
  • En el navegador: Lighthouse y PageSpeed Insights para lab, y RUM (Real User Monitoring) para saber qué viven tus usuarios de verdad, no tu MacBook con fibra.
  • Las métricas que importan: LCP (cuándo aparece el contenido principal), INP (cuándo responde a la interacción) y CLS (cuánto salta el layout). Son las Core Web Vitals que Google mide.

Con la línea base clara, ataca el cuello de botella real. El orden de los siguientes apartados es, por estadística de auditorías, donde suele estar el problema.

1. La Base de Datos: El Clásico N+1

El problema de rendimiento más común que encontramos no es «PHP lento» ni «el framework pesa»: es la base de datos recibiendo cientos de consultas por petición. El patrón N+1:

// N+1: una query para los pedidos... y una por cada pedido
$pedidos = $em->getRepository(Pedido::class)->findAll();      // 1 query
foreach ($pedidos as $pedido) {
    echo $pedido->getCliente()->getNombre();                 // N queries
}

La solución suele ser una línea: traer las relaciones en la misma consulta.

// 1 sola query con JOIN
$pedidos = $em->createQueryBuilder()
    ->select('p', 'c')
    ->from(Pedido::class, 'p')
    ->join('p.cliente', 'c')
    ->getQuery()
    ->getResult();

Y sus compañeros habituales:

  • Índices. Una columna en un WHERE o un JOIN sin índice convierte una consulta de milisegundos en un escaneo de millones de filas. EXPLAIN es tu amigo.
  • Paginación. Ninguna lista debería devolver 50.000 filas. LIMIT + cursor, siempre.
  • Proyecciones. Si solo necesitas tres columnas para un listado, no hidrates cuarenta entidades completas: usa DTOs o consultas parciales.

2. La Caché: La Optimización Que Sí Escala

La petición más rápida es la que nunca llega a tu aplicación. Piensa la caché como capas, de más cercana al usuario a más profunda:

Capas de caché: navegador, CDN, caché HTTP, caché de aplicación y base de datos, con los tiempos típicos de cada salto
  • Caché del navegador. Estáticos con huella en el nombre y Cache-Control: immutable por un año. Cambia el fichero, cambia la huella, caduca solo.
  • CDN. Imágenes, CSS, JS y — si el contenido es público — páginas completas servidas desde el punto más cercano al usuario.
  • Caché HTTP. Respuestas con ETag, Last-Modified y Cache-Control. En Symfony, un controlador puede declarar su caducidad en una línea y dejar que un reverse proxy (Varnish, Symfony HttpCache) sirva la página sin tocar PHP.
  • Caché de aplicación. Resultados caros (agregaciones, llamadas a terceros, el menú de categorías) en Redis o APCu, con TTL explícito.
  • Caché de base de datos. El query cache del propio motor y, en Doctrine, la caché de segundo nivel para entidades casi estáticas.
// Symfony: página pública cacheable 1 hora, con ETag automático
$response = new Response($contenido);
$response->setPublic();
$response->setMaxAge(3600);
$response->setEtag(md5($contenido));

3. El Runtime de PHP: Rendimiento Gratis

  • PHP actualizado. Cada versión mayor trae mejoras medibles de velocidad y memoria. Ejecutar una versión antigua es pagar por rendimiento que no usas.
  • OPcache bien configurado. En producción: opcache.validate_timestamps=0, memoria suficiente y preloading si tu aplicación lo soporta. Es literalmente rendimiento gratis.
  • Composer optimizado. composer dump-autoload --optimize --classmap-authoritative en el despliegue.
  • Sin trabajo pesado en la petición. Enviar emails, generar PDFs, llamar a APIs lentas: todo eso va a una cola (Symfony Messenger) y la respuesta sale en milisegundos. El usuario no tiene por qué esperar a tu SMTP.

4. El Frontend: Donde Viven las Core Web Vitals

El backend puede responder en 50 ms y aun así tener un LCP de 6 segundos. Los sospechosos habituales:

  • Imágenes. Son el peso número uno de casi toda la web. WebP/AVIF, tamaños adaptados al dispositivo con srcset y loading="lazy" en todo lo que no esté en el primer viewport. La imagen hero, al contrario: fetchpriority="high" y precargada.
  • JavaScript a dieta. Audita tu bundle: librerías enteras para usar dos funciones, scripts de terceros sin defer, código muerto. Cada KB de JS bloquea la interacción (INP).
  • CSS crítico. Inline del CSS del primer viewport y carga diferida del resto.
  • Fuentes. font-display: swap, subconjuntos y preconnect al origen de las fuentes. Las fuentes son una causa clásica de CLS.

Errores Comunes

  1. Optimizar sin perfilar. Cachear agresivamente lo que ya era rápido mientras una query de 2 segundos sigue intacta. Primero el profiler, luego la cirugía.
  2. Cachear páginas personalizadas en el CDN. Servir el carrito de un usuario a otro es el incidente que nadie quiere explicar. Lo privado se cachea por fragmentos (ESI) o no se cachea.
  3. Microoptimizar PHP. Discutir si foreach o array_map mientras la página hace 300 queries. El orden de magnitud importa: red > base de datos > caché > código.
  4. Medir solo en el portátil del desarrollador. Tu fibra y tu M4 no son el móvil gama media de tu cliente con 4G. RUM o no sabes nada.
  5. Hacerlo una vez y olvidarse. El rendimiento se degrada solo: cada feature nueva añade queries y kilobytes. Presupuestos de rendimiento (LCP < 2,5 s, bundle < X KB) en el CI o no hay garantías.

En Resumen

El rendimiento web no es magia ni una opinión: es medir, atacar el cuello de botella real en el orden correcto (base de datos, caché, runtime, frontend) y poner presupuestos para que no se degrade. Las mejoras suelen ser menos glamurosas y mucho más baratas de lo que parece: un índice, un JOIN, un Cache-Control bien puesto.

Si tu aplicación va lenta y no sabes por dónde empezar, es exactamente lo que hacemos en una auditoría de rendimiento: medimos todo el stack y te entregamos un informe priorizado por impacto. Solicita una consultoría gratuita →

Sigue leyendo: REST vs GraphQL: cómo elegir la API correcta para tu proyecto.

  • #rendimiento
  • #symfony
Artículos relacionados
¿Empezamos?

¿Listo para construir algo que crezca con tu negocio?

Cuéntanos tus objetivos y trazaremos juntos la ruta de diseño, desarrollo y tecnología que te lleve de la visión a resultados medibles.