Aplicación creada por IA lenta: soluciona consultas N+1 e índices faltantes
¿Tu app generada por IA va lenta? Empieza por los problemas de BD más comunes: consultas N+1, índices faltantes, escaneos no acotados y ORMs chatty, con soluciones rápidas.

Qué suele significar “lento” (y por qué la base de datos es una causa común)
“Lento” raramente es un solo número. Para los usuarios se siente como una página que se queda en un spinner, un inicio de sesión que tarda una eternidad, una búsqueda que devuelve resultados tarde o un pago que caduca. Incluso cuando algo acaba cargando, la app puede parecer poco fiable porque nunca sabes si el siguiente clic se quedará colgado.
Una sorpresa común: la base de datos a menudo domina el tiempo total. Tu servidor puede renderizar una página en milisegundos, pero si espera datos, todo espera. Una consulta lenta puede bloquear toda la petición. Un montón de consultas medianamente lentas puede hacer lo mismo.
Si tu app generada por IA está lenta, vale la pena revisar la base de datos pronto porque el código generado por IA tiende a producir accesos a datos que “parecen razonables” pero son ineficientes en la práctica. Puede que recupere los mismos datos una y otra vez, obtenga muchas filas que no necesita, o ejecute consultas que funcionan con 50 registros pero se desploman con 50.000.
Por qué gana la base de datos (para bien o para mal)
La mayoría de las páginas se reducen a un patrón simple: leer datos, darles forma y enviarlos. Las lecturas son donde el tiempo desaparece.
Aquí están las maneras habituales en que la base de datos toma el control:
- Demasiadas consultas por carga de página (cada una añade tiempo de espera).
- Consultas que escanean muchas filas porque no pueden usar un índice.
- Consultas que devuelven resultados enormes y luego se filtran en el código.
- Bloqueos o transacciones largas que hacen que otras peticiones hagan cola.
- Límites del pool de conexiones que hacen esperar a las peticiones por una conexión libre.
El objetivo no es “arreglarlo todo”. Empieza por encontrar el mayor cuello de botella que puedas medir, arréglalo y vuelve a comprobar. Un buen cambio puede reducir el tiempo de carga a la mitad, mientras que perseguir diez problemas pequeños a menudo te hace perder un día.
En FixMyMess, este es un patrón común en prototipos generados por IA: la app parece bien durante una demo y luego se vuelve dolorosamente lenta cuando llegan usuarios y datos reales. El progreso más rápido suele venir de aislar la única consulta (o el pequeño conjunto de consultas) que está en la ruta crítica de tu página más lenta.
Señales rápidas de que la base de datos es el cuello de botella
Cuando una app creada por IA está lenta, la base de datos es a menudo el primer lugar a revisar porque es el recurso compartido del que depende cada petición. Una consulta lenta puede bloquear a muchos usuarios a la vez.
Síntomas comunes que apuntan a problemas en la base de datos:
- Endpoints específicos son lentos, mientras que otros se sienten normales (a menudo páginas de listas, búsquedas, dashboards).
- Ves timeouts o errores de “la petición tardó demasiado” bajo tráfico real.
- La CPU del servidor de la app parece bien, pero la CPU de la base de datos está alta o la base de datos hace muchas lecturas.
- El rendimiento empeora conforme crecen los datos (más usuarios, más filas, más joins).
- La misma página se hace más lenta semana a semana, sin nuevas funcionalidades.
Para separar lentitud del servidor de app de lentitud de la base de datos, piensa en dónde se gasta el tiempo. Los problemas del servidor suelen verse así: todas las rutas son lentas, la CPU del web server está alta o la memoria sube hasta que todo falla. Los problemas de base de datos suelen verse así: unas pocas rutas son dolorosamente lentas, la ralentización está ligada a ciertas tablas y el problema empeora mucho con más datos.
“Rápido en local, lento en producción” es una gran pista. Tu base de datos local es pequeña, cálida (en caché) y no tiene otros usuarios compitiendo. Producción tiene tamaño real de datos, concurrencia real y a menudo redes y reglas de seguridad más estrictas. Si un endpoint es instantáneo en local pero arrastra en producción, a menudo significa que el plan de consulta hace trabajo extra a escala, como índices faltantes, consultas N+1 o un escaneo completo de tabla.
Un chequeo rápido: abre la página lenta y luego prueba una versión con menos datos (rango de fechas más pequeño, menos filtros, menos filas). Si de repente va rápido, probablemente estás pagando una “tasa por tamaño de datos”. Los equipos que traen estos casos a FixMyMess suelen encontrar el mismo patrón: la app “funciona” como prototipo y luego se ralentiza cuando llegan usuarios y tablas reales.
Flujo simple paso a paso para solucionar problemas
Cuando una app creada por IA está lenta, es tentador tocar el código por todos lados. Resiste eso. Elige una acción de usuario y síguela de extremo a extremo para medir qué cambios realmente ayudan.
Empieza eligiendo una sola acción lenta que un usuario real haga, como “abrir Pedidos” o “buscar clientes”. Luego identifica el endpoint exacto detrás de ella (la ruta API o el handler del servidor). Esto mantiene tu prueba repetible.
Después, ejecuta esa acción mientras capturas la actividad de la base de datos. Usa lo que ya tengas: un log de consultas en desarrollo, un APM en producción o logging temporal alrededor de la petición. Buscas las consultas principales que ocurren durante esa acción, no una foto general de toda la app.
Mide dos cosas en lugar de adivinar: cuántas consultas se disparan y cuánto tiempo total pasa la base de datos en ellas. Una página que lanza 120 consultas pequeñas puede ser tan lenta como una página con 2 consultas enormes.
Un flujo práctico y enfocado:
- Reproduce la acción lenta y anota el endpoint que la maneja.
- Captura las consultas durante esa acción.
- Registra el conteo de consultas y el tiempo total en BD para la petición.
- Ordena por impacto: la consulta más lenta y la más repetida suelen ir primero.
- Arregla un problema y vuelve a ejecutar la misma acción para confirmar la mejora.
Ejemplo: si “abrir Pedidos” tarda 6 segundos y encuentras 80 consultas con 4.8 segundos de tiempo en BD, arregla al culpable (a menudo un filtro sin índice o un bucle N+1). Si el re-test baja a 2 segundos, vas por buen camino.
Si heredaste un código generado por IA y los trazados no tienen sentido, FixMyMess puede hacer una auditoría rápida y señalar los pocos problemas de BD que moverán la aguja más rápido.
5 chequeos rápidos antes de cambiar código
Si tu app generada por IA está lenta, no empieces por reescribir funciones. Primero, aclara qué hace la base de datos en una sola página o petición lenta.
Chequeos rápidos que suelen revelar el problema
Comienza con estos cinco chequeos en tus logs, APM o historial de consultas:
- Cuenta de consultas por petición. Si una página simple dispara 50, 100 o 500+ consultas, el rendimiento colapsará cuando crezca el tráfico.
- Busca repeticiones. Si ves la misma forma de consulta ejecutándose una y otra vez con solo el ID cambiando, probablemente hay un patrón N+1.
- Ordena por costo total, no solo por “la más lenta”. Una consulta de 30 ms ejecutada 1.000 veces duele más que una única consulta de 2 segundos. Mira duración x cantidad.
- Encuentra consultas que devuelven “demasiados datos” por accidente. Fíjate en SELECT * en tablas grandes, filtros WHERE que faltan, falta de LIMIT o cargar columnas BLOB/texto grandes que no necesitas.
- Verifica si los índices se usan realmente. Una consulta puede parecer bien, pero el plan puede mostrar un escaneo completo en lugar de una búsqueda por índice.
Un pequeño ejemplo: un dashboard carga una lista de 50 clientes y luego obtiene la “última factura” dentro de un bucle. Verás una consulta para la lista y 50 consultas casi idénticas para las facturas. Cada consulta es rápida sola, pero juntas convierten una petición en un embotellamiento.
Qué capturar antes de tocar nada
Apunta tres números para la petición lenta: conteo total de consultas, las 3 consultas principales por tiempo total y si esas consultas usan índices o escanean. Eso te da una línea base para confirmar que cada arreglo realmente ayuda.
En código generado por IA, estos problemas a menudo están ocultos bajo una capa ORM y logs ruidosos. En FixMyMess, estos son los datos que extraemos en una auditoría gratuita para que el primer cambio sea el correcto.
Consultas N+1: cómo detectarlas y pararlas
Una consulta N+1 ocurre cuando tu app ejecuta 1 consulta para cargar una lista y luego ejecuta 1 consulta más por cada fila de esa lista. Ejemplo: cargas 50 usuarios (1 consulta) y luego obtienes las órdenes de cada usuario una a una (50 consultas más).
Esto es común en ORMs porque la “carga perezosa” parece conveniente. Iteras sobre los usuarios, accedes a user.orders y el ORM toca la base de datos cada vez. El código generado por IA suele apoyarse en estos comportamientos por defecto, por eso “aplicación creada por IA lenta” a menudo significa “la página hace cientos de consultas pequeñas”.
Cómo detectarlo rápido
Busca un patrón repetido en tus logs o APM: la misma SQL una y otra vez, con solo el ID cambiando. Otro indicio es que la página se vuelve más lenta a medida que crecen los datos, aunque el código no cambie.
Si puedes, cuenta las consultas de una petición. Si el número sube con la cantidad de ítems en la página (20 ítems -> ~21 consultas, 100 ítems -> ~101 consultas), probablemente encontraste un N+1.
Arreglos rápidos que suelen funcionar
Elige la corrección más pequeña que elimine las búsquedas por fila:
- Cargar relaciones de forma eager (preload de usuarios con sus órdenes de una vez).
- Recuperar en lote por IDs (una consulta para todas las órdenes WHERE user_id IN (...)).
- Usar un join cuando realmente necesitas campos de ambas tablas.
- Devolver solo las columnas que necesitas (evita cargar blobs enormes).
Valida la corrección con dos comprobaciones: el conteo de consultas debería caer drásticamente y el tiempo de la página debería mejorar en una prueba repetible (mismos datos, misma petición).
Una precaución: arreglar N+1 uniendo todo puede salir mal. Sobre-joinear puede crear resultados grandes, filas duplicadas y más trabajo de memoria en tu app. Carga solo lo que la página muestra y mantén los resultados pequeños y focalizados.
Si heredaste un prototipo generado por IA, una auditoría de código suele encontrar puntos N+1 rápidamente, sobre todo en listas, dashboards y tablas de administración.
Índices faltantes: la ganancia más rápida para muchas apps lentas
Un índice es como el índice al final de un libro. Sin él, vas pasando páginas hasta encontrar el tema. Con él, saltas directamente a las páginas correctas. La base de datos funciona igual: sin índice, suele leer muchas filas para encontrar unas pocas coincidencias.
Los faltantes más comunes son aburridos, y eso es una buena noticia porque son fáciles de arreglar. Revisa las columnas que usas con frecuencia en filtros WHERE, condiciones JOIN y ordenaciones ORDER BY. Las claves foráneas son culpables frecuentes, especialmente en esquemas generados por IA donde las relaciones existen en el código pero no se añadieron las restricciones (y los índices) en la base.
Ejemplo simple: si tu app carga órdenes de un usuario con WHERE user_id = ? ORDER BY created_at DESC, normalmente quieres un índice que coincida con cómo buscas. Puede ser un índice de una sola columna en user_id, pero si filtras y ordenas juntos, un índice compuesto como (user_id, created_at) puede ser mucho más rápido.
Reglas prácticas al elegir un índice:
- Indexa columnas que aparezcan en
WHERE,JOINoORDER BYen consultas calientes. - Prefiere índices compuestos cuando filtras por varias columnas juntas con frecuencia.
- Asegúrate de que las columnas de claves foráneas estén indexadas cuando se usan para unir tablas.
- No asumas que el ORM agregó los índices correctos por ti.
Una trampa común es indexar columnas de baja cardinalidad (valores que se repiten mucho), como status con pocos estados. Esos índices suelen no ayudar porque la base de datos aún tiene que tocar una gran parte de la tabla. También pueden ralentizar escrituras porque cada inserción o actualización debe mantener el índice.
Para confirmar que arreglaste lo correcto, compara el plan de consulta antes y después. Buscas que el plan pase de un scan (leer muchas filas) a una búsqueda por índice (saltar a las filas coincidentes). Si el plan sigue escaneando, el índice puede estar en el orden de columnas equivocado, el filtro no es lo bastante selectivo o la consulta está escrita de forma que impide usar el índice.
Escaneos no acotados: cuando la BD lee mucho más de lo que piensas
Un escaneo no acotado ocurre cuando una consulta es tan amplia que la base de datos tiene que leer una porción enorme de la tabla (a veces toda) solo para devolver un pequeño resultado. Si tu app generada por IA es lenta y empeora con el crecimiento de datos, a menudo esta es la razón.
El patrón común es simple: la consulta no reduce la búsqueda lo suficiente temprano, así que la base de datos sigue leyendo filas hasta encontrar lo que necesita. Ese trabajo extra aparece como CPU alta en la BD, consultas largas y páginas que van bien en dev pero se arrastran en producción.
Señales en rojo a buscar
Unas cuantas señales aparecen en revisiones de código y logs de consultas:
- Consulta sin filtro WHERE en una tabla grande.
- Falta de LIMIT en endpoints que listan registros.
- Paginación por offset (page=2000) en una tabla que no para de crecer.
- Seleccionar muchas columnas (o SELECT *) cuando solo necesitas unas pocas.
- Consultas de “últimos items” que no filtran por tiempo o categoría.
Si ves esto en una ruta caliente como un dashboard o feed, trátalo como culpable probable.
Arreglos rápidos que suelen funcionar
Empieza haciendo que la consulta haga menos trabajo por petición. Cambios pequeños pueden reducir la carga de forma dramática:
- Añade un filtro real (status, user_id, tenant_id, created_at) y asegúrate de que coincida con cómo la gente usa la página.
- Cambia la paginación por offset a keyset pagination (usa el último id visto o un timestamp).
- Devuelve menos campos (solo las columnas que necesita la UI).
- Añade una ventana temporal para tablas de tipo log (últimos 7 días) y archiva datos antiguos.
Ten cuidado con funcionalidades de “buscar en todas partes”. Un LIKE '%termino%' ingenuo sobre campos de texto grandes suele forzar escaneos. Si la búsqueda importa, usa herramientas de búsqueda de texto que soporte tu BD o restringe la búsqueda a campos más pequeños e indexados.
Ejemplo realista: una tabla de actividad crece sin límite. La página principal pide “actividad reciente” pero no filtra por cuenta y usa paginación por offset. Funciona con 5.000 filas y luego se vuelve un escaneo con 5 millones. Añadir filtro por cuenta, paginación keyset y una ventana de 30 días suele convertir una consulta de 3 segundos en una de 50 ms.
Si heredaste código generado por IA, este problema es común porque los endpoints de lista se construyen rápido y quedan abiertos. FixMyMess suele encontrar unos pocos escaneos no acotados que representan la mayor parte de la lentitud cuando llegan usuarios reales.
ORMs chatty: muchas consultas pequeñas suman
Cuando una app creada por IA está lenta, la BD no siempre está “haciendo trabajo duro”. A veces hace mucho trabajo pequeño, una y otra vez. Un ORM chatty es cuando tu código hace muchas llamadas pequeñas en lugar de unas pocas útiles.
Esto suele pasar sin que lo notes porque cada consulta parece inofensiva. Pero 200 consultas “rápidas” pueden ser más lentas que 5 consultas bien construidas, sobre todo cuando entra en juego tiempo de red y pooling de conexiones.
Cómo se ve lo chatty
Verás patrones como:
- Una consulta para cargar una lista y luego otra por fila para obtener un detalle relacionado.
- Búsquedas por campo por campo (por ejemplo, cargar un usuario y luego volver a consultar su plan, equipo y configuración).
- Propiedades computadas o getters que ejecutan una consulta y se llaman dentro de un bucle.
- Cargas “incluye todo” que traen tablas relacionadas grandes que nunca muestras.
- Consultas repetidas por la misma fila en una petición (sin caché a nivel de petición).
Una trampa común en código generado por IA es un método bonito como user.displayName() que consulta tablas adicionales. Llamarlo 100 veces en una página crea 100 viajes extra a la BD.
Arreglos rápidos que funcionan
Empieza por hacer que la petición “cargue datos en una pasada”. Recupera la lista y los datos relacionados juntos, o haz una segunda consulta que cubra todas las filas (no una por fila). Luego mantén la carga pequeña: selecciona solo las columnas necesarias y evita cargar relaciones grandes a menos que la página las use.
Si la misma búsqueda ocurre varias veces en una petición (por ejemplo, “equipo actual”, “plan actual”, “feature flags”), añade una caché simple a nivel de petición para golpear la BD una sola vez.
Después de cualquier cambio, vuelve a medir. Cuenta consultas por petición y observa la latencia p95, no solo la media. Un buen resultado son menos consultas, menos viajes de ida y vuelta y una p95 que baje de forma perceptible.
Si trabajas con un código heredado generado por IA, FixMyMess suele encontrar hotspots chatty del ORM rápido en una auditoría porque aparecen como patrones repetidos de consultas ligados a un único endpoint.
Un ejemplo realista: la página que se hacía más lenta cada semana
Una historia común con un marketplace generado por IA es una página de administración de Orders que iba bien al principio y luego se volvió insoportable. Con 200 órdenes carga en 1 s. Un mes después, con 10.000 órdenes, tarda 12 a 20 s y a veces agota el tiempo. No cambió nada “grande”, pero la base de datos ahora hace mucho más trabajo.
Esto es lo que la app hace en esa página:
- Consulta 1: carga las últimas 50 órdenes para la tabla (a menudo con filtros como
statusy un rango de fechas). - Luego, para cada fila de orden, la UI muestra el nombre del cliente y una lista corta de items.
El problema oculto es N+1. Obtienes 1 consulta para la lista de órdenes y luego N consultas más por clientes y N más por items. Con 50 filas, pueden ser 101 consultas (o más). Cada consulta es “rápida” sola, pero el tiempo total se acumula y el pool de conexiones de BD se satura.
Al mismo tiempo, el filtrado empeora por un índice faltante. El código filtra por status y ordena/filtra por created_at, pero la base de datos no tiene un índice útil para esa combinación. A medida que la tabla crece, la BD empieza a escanear muchas más filas antes de devolver las 50 más nuevas.
Orden práctico de arreglos que mantiene los cambios pequeños:
- Mide primero: captura el conteo total de consultas y el tiempo total de BD en una carga de página.
- Arregla el índice: añade un índice compuesto que coincida con el filtro y la ordenación (por ejemplo,
status+created_at). Re-prueba. - Arregla el N+1: recupera clientes de una vez y items de una vez (o usa eager-load/include). Re-prueba.
- Añade límites y guardrails de paginación (tamaño máximo). Re-prueba.
Lo típico tras cada paso: el cambio de índice baja la consulta principal de segundos a decenas de milisegundos, pero la página aún puede sentirse lenta. Eliminar el N+1 suele reducir el conteo de consultas de ~100 a menos de 10 y la carga se vuelve predecible.
Mantén restricciones en mente: cambia una cosa a la vez, ejecuta la misma petición cada vez y verifica resultados con volúmenes de datos realistas. Con código generado por IA es fácil “arreglar” rendimiento cambiando accidentalmente comportamiento, así que pasos pequeños y verificaciones rápidas importan. Si la base es un desastre (llamadas chatty del ORM esparcidas en vistas), los equipos suelen usar una auditoría corta para mapear rutas de consulta antes de refactorizaciones mayores.
Errores comunes que hacen perder tiempo (y a veces empeoran)
Cuando una app creada por IA está lenta, da ganas de agarrar el arreglo que parece más rápido. El problema es que los arreglos rápidos a menudo ocultan la causa real, así que la ralentización vuelve (o se desplaza).
Arreglos que dan sensación de avance pero suelen fallar
Una trampa común es añadir caché antes de haber demostrado que la consulta es saludable. Si una página tarda 2 s porque ejecuta 120 consultas, cachear la respuesta puede enmascararlo un tiempo, pero cuando la caché expire (o los datos cambien), el pico sigue allí.
Otro tiempo perdido es añadir índices a ciegas. Un índice puede ser una gran victoria, pero el índice equivocado puede no hacer nada o incluso ralentizar escrituras y aumentar almacenamiento. Confirma siempre qué está haciendo la BD y elige el índice más pequeño que ayude al filtro u orden real.
El sobre-fetching está por todas partes en código generado por IA. Si la UI necesita un nombre y el plan, pero el ORM devuelve todo el registro del usuario más relaciones anidadas, pagas lecturas, memoria y transferencia de red extra en cada petición.
Finalmente, la estrategia de “mega-consulta” puede ser igual de mala. Unir todo para evitar N+1 puede producir una consulta difícil de cambiar, difícil de depurar y aún lenta porque devuelve demasiadas filas.
Saltarse la verificación es el mayor error
Si no fijas una línea base, no sabrás si mejoraste algo. Antes de cambiar código, captura una instantánea antes/después: tiempo de petición, número de consultas y la consulta más lenta.
Cinco señales de alarma que indican que estás adivinando en lugar de arreglar:
- Sin métricas de referencia (solo “se siente más rápido”).
- No revisar el plan de consulta antes de indexar.
- Endpoints que pueden crecer sin límite y sin paginación.
- Sin test de regresión para la ruta lenta (vuelve la próxima semana).
- No re-probar con volúmenes de datos en producción.
En FixMyMess vemos a menudo equipos pasar días en caches y refactors para luego descubrir que el problema real era un índice faltante y una llamada ORM dentro de un bucle. El camino más rápido es aburrido: mide, verifica, cambia una cosa, mide otra vez.
Próximos pasos: consolidar las mejoras y pedir ayuda si la necesitas
Una vez que encuentres los grandes problemas de consulta, no te quedes solo con el arreglo que hizo bajar la curva. El rendimiento tiende a desviarse otra vez conforme se añaden funcionalidades y crecen los datos. Trata tus primeras victorias como una nueva línea base que proteger.
Empieza por escribir una pequeña “lista de prioridades” que revises semanalmente. Que sea concreta, no teórica.
- Top 3 acciones lentas de usuarios (ejemplo: login, búsqueda, checkout) y sus consultas peor clasificadas.
- Para cada acción: tiempo medio, p95 y conteo de consultas.
- Los patrones exactos que lo causan (N+1, índice faltante, escaneo no acotado, demasiadas llamadas pequeñas del ORM).
Luego añade guardrails para detectar regresiones pronto. Son baratos y dan mucho rendimiento:
- Fija un presupuesto de conteo de consultas por petición para páginas clave.
- Activa alertas de consultas lentas en la base de datos y revísalas regularmente.
- Ejecuta un test básico de carga sobre las 3 acciones principales después de cada release.
- Añade una comprobación de rendimiento en code review ("¿añadimos consultas?").
- Mantén un repositorio compartido de helpers de consulta aprobados para que todos usen los mismos patrones.
Después planifica los refactors que eliminen la causa raíz. Comunes: limpiar patrones de acceso al ORM (cargar relaciones una vez, no en bucles), estandarizar paginación y crear helpers de consulta compartidos para que el mismo error no se repita en varios endpoints.
Si tu app generada por IA está lenta y fue creada por herramientas como Lovable, Bolt, v0, Cursor o Replit, considera pedir un diagnóstico profesional. Esos codebases a menudo esconden lógica repetida de consultas en muchos archivos; arreglas un endpoint y otros tres siguen causando problemas.
FixMyMess es una opción si quieres un siguiente paso claro: podemos ejecutar una auditoría gratuita del código para identificar problemas de BD y app, y aplicar arreglos focalizados con verificación humana para que las mejoras perduren.