Resumen. Hacemos muchas auditorías de apps construidas con Cursor, Claude Code, Bolt, Lovable y sesiones largas de ChatGPT. Las bases de código son tremendamente distintas, pero los hallazgos casi nunca lo son. Once problemas aparecen una y otra vez, más o menos en el orden en que suelen doler: secretos y credenciales incrustados en el código, sin validación de entrada (la superficie de inyección), autenticación que marca la casilla pero no verifica la petición, cero cobertura de tests, sin gestión de errores en el camino infeliz, consultas N+1 y rendimiento dejado al azar, dependencias obsoletas con CVEs conocidas sin parchear, variables y funciones duplicadas, sin arquitectura coherente, estado asíncrono complejo colapsado en un infierno de callbacks en lugar de streams, y ninguna conciencia del entorno de despliegue. Ninguno es exótico. Todos son predecibles, y todos se pueden arreglar sin reescribir. Este es el complemento de ingeniería a nuestra guía para fundadores sobre publicar un prototipo de IA; si quieres que nos encarguemos, eso es lo que hace una auditoría de código IA.
Qué Es una Auditoría — y Qué No Es
Una auditoría no es una reescritura, ni un veredicto sobre si deberías haber usado IA para construir la app. Es una lectura estructurada de una base de código en funcionamiento que responde a una pregunta: ¿qué pasa la primera vez que esto se encuentra con un atacante real, un pico de tráfico real o un cambio real dentro de seis meses? Los hallazgos de abajo no son categorías hipotéticas de una lista de comprobación: cada uno es algo que hemos encontrado, redactado y arreglado en un encargo real. Hacemos referencia a nuestro informe de auditoría de muestra y a la auditoría de código IA a lo largo de todo el artículo, porque ahí es donde ocurre realmente la corrección; este artículo es la prueba de por qué hace falta.
Por qué se repiten los hallazgos
Las herramientas de programación con IA optimizan una sola cosa: el camino más corto hasta código que se ejecute. Eso es genuinamente útil: llevas una idea a un estado funcional en horas. Pero «se ejecuta en la demo» y «es mantenible y segura en producción» son objetivos distintos, y la brecha entre ambos es notablemente consistente entre proyectos.
La razón es estructural. Un modelo generando código tiene una ventana de contexto estrecha y ninguna memoria de las decisiones que tomó tres archivos atrás. No puede probar lo que acaba de escribir, no tiene ningún sentido de tu financiación ni de tu postura de seguridad y no tiene incentivo para mantener coherente la base de código con el tiempo. Así que toma la decisión localmente óptima cada vez, y la suma de decisiones localmente óptimas es una base de código que funciona hoy y se resiste a todo cambio mañana.
Tras suficientes auditorías, los fallos se agrupan en las mismas once categorías. Aquí están.
1. Secretos y Credenciales Incrustados en el Código
Toda auditoría encuentra esto, y es justo el olor que cuesta dinero de verdad: archivos .env subidos al repositorio, claves de API y credenciales de base de datos incrustadas directamente en el código fuente, tokens de terceros horneados en bundles de cliente que llegan a cada navegador que carga la app. Una sola clave filtrada de OpenAI o Stripe puede acumular miles de dólares en cargos no autorizados en cuestión de horas, o entregarle a un atacante tu almacén de datos directamente.
Por qué lo hace la IA. Incrustar una clave funciona de inmediato; montar un gestor de secretos o inyección de variables de entorno no, y el modelo no tiene ningún motivo para preferir el camino más lento cuando el más rápido también «se ejecuta». El camino más corto hasta una funcionalidad que funcione casi nunca es el seguro.
Cómo lo arreglamos. Los secretos salen del código y pasan a una gestión de entorno en condiciones. Cualquier cosa que se haya subido alguna vez a git se trata como ya comprometida y se rota, no solo se elimina — un git rm sin rotación deja la clave antigua válida en cada clon y en todo el historial de commits. Esta es la parte no negociable de cualquier auditoría: una clave filtrada es una emergencia, y se arregla primero.
2. Sin Validación de Entrada — la Superficie de Inyección
La inyección SQL, el XSS y la inyección de prompts están por todas partes en el código construido con IA, y la causa raíz es siempre la misma: la entrada del usuario va directa a una consulta, una plantilla o un prompt de LLM sin ninguna validación o saneamiento en medio. Un único endpoint vulnerable puede comprometer toda tu base de datos o dejar que un atacante manipule lo que hacen tus propias funcionalidades de IA.
Por qué lo hace la IA. La validación es una segunda petición que nunca se le pidió al modelo. Genera el código que satisface el prompt del camino feliz —«coge el mensaje del usuario y guárdalo»— y el camino feliz nunca menciona qué hay que rechazar.
Cómo lo arreglamos. Cada frontera donde entra información no confiable recibe validación explícita y consultas o plantillas parametrizadas, no concatenación de cadenas. Para las funcionalidades de IA en concreto, eso significa tratar la propia construcción del prompt como una frontera propensa a inyección, no solo la capa de base de datos.
3. Autenticación Que Marca la Casilla, No Que Verifica la Petición
Las apps generadas por IA suelen implementar la autenticación de forma superficial —existe una pantalla de login, y funciona— pero el backend nunca verifica realmente los permisos por petición. Los endpoints de API aceptan cualquier cosa que les llegue. Las rutas de administración son accesibles sin comprobación de rol. El usuario A puede ver los datos del usuario B cambiando un ID en la URL: el ejemplo de libro de texto de una referencia directa a objeto insegura, en producción.
Por qué lo hace la IA. «Añade una pantalla de login» y «comprueba que esta petición concreta puede tocar este registro concreto» son problemas distintos, y el modelo resuelve el que se le preguntó. La autenticación es visible en una demo; los huecos de autorización son invisibles hasta que alguien los explota.
Cómo lo arreglamos. Auditamos cada endpoint frente a quién está realmente autorizado a llamarlo, no a quién sugiere la pantalla de login, y añadimos las comprobaciones de autorización por petición que faltaban: comprobaciones de propiedad sobre los registros, comprobaciones de rol en las rutas de administración y límites de tasa en cualquier cosa que un script pudiera machacar.
4. Sin Tests — Cada Despliegue Es una Apuesta
El hallazgo más común con diferencia: no hay ningún test. Ni una suite fina, ni tests inestables: cero. La app se validó haciendo clics por ella, y esa es toda la red de seguridad.
Esto es invisible hasta justo el momento en que es catastrófico. Sin tests no hay forma de saber si un cambio ha roto algo salvo publicándolo y esperando a que un usuario se queje. Cada despliegue se convierte en una pasada de regresión manual que nadie llega a hacer, así que refactorizar da pánico, las actualizaciones de dependencias se saltan y la base de código se calcifica, no porque el código sea malo, sino porque nadie se atreve a tocarlo.
Por qué lo hace la IA. Generar una funcionalidad y generar tests para ella son dos peticiones separadas, y nadie hizo la segunda. El modelo escribirá tests encantado si se los pides, pero por su cuenta entrega el camino feliz y para.
Cómo lo arreglamos. No buscamos un 100 % de cobertura el primer día. Añadimos una capa fina donde más rentabiliza: un test de humo de que la app arranca, tests alrededor de la lógica de dinero y de autenticación, y un test de regresión por cada bug que arreglamos durante la auditoría. Solo eso convierte los despliegues de apuesta en rutina.
5. Sin Gestión de Errores en el Camino Infeliz
La app funciona exactamente como en la demo, siempre que la red no falle nunca, la API de terceros nunca haga timeout y el usuario nunca haga nada inesperado. En el momento en que ocurre alguna de esas cosas, los síntomas son feos: un rechazo de promesa sin gestionar tumba toda la petición, una llamada a la API fallida deja la interfaz atascada en un spinner para siempre, una excepción muestra al usuario una traza de pila en bruto en lugar de un mensaje que signifique algo para él.
Por qué lo hace la IA. El camino feliz es lo que describió el prompt y lo que ejercitó la demo. La gestión de errores es código defensivo escrito para situaciones que nunca se le pidió al modelo que imaginara, y añade líneas sin hacer que la demo se vea más impresionante, así que es lo primero que se salta bajo un presupuesto de tiempo implícito.
Cómo lo arreglamos. Recorremos cada llamada externa —API, base de datos, sistema de archivos— y añadimos la rama de fallo: reintentos con backoff donde reintentar ayuda, un fallback o un estado de error claro donde no ayuda, y logging que cuenta lo que realmente pasó en vez de un genérico «algo salió mal».
6. Consultas N+1 y Rendimiento Dejado al Azar
La pantalla de lista que carga al instante con diez filas en desarrollo se arrastra con diez mil en producción. La causa clásica es un patrón de consulta N+1: una consulta para traer una lista, luego una consulta separada por fila para sus datos relacionados, así que una pantalla que debería costar un viaje a la base de datos cuesta cientos. Los índices que faltan, los conjuntos de resultados sin límite ni paginación, y cargar objetos enteros cuando solo se muestran uno o dos campos son los compañeros habituales.
Por qué lo hace la IA. El patrón N+1 es la forma más obvia de escribir el bucle, y produce un resultado correcto: el modelo no tiene ningún bucle de retroalimentación que le diga que el número de consultas importa hasta que alguien lo mide con un volumen de datos real, cosa que una demo con un puñado de filas nunca hace.
Cómo lo arreglamos. Perfilamos los patrones de consulta reales bajo un volumen de datos realista, colapsamos las cadenas N+1 en joins o cargas por lotes, añadimos los índices que faltan y ponemos paginación o límites en cualquier cosa que devuelva un conjunto sin acotar. Nuestra guía de bases de datos e índices cubre la mecánica de por qué esto es lento y qué aspecto tiene un plan de consulta sano.
7. Dependencias Obsoletas y Deriva de CVEs
npm audit o su equivalente saca a la luz un muro de vulnerabilidades conocidas en el momento en que alguien lo ejecuta, porque nadie lo había hecho antes. Los paquetes están anclados a la versión que era actual cuando la herramienta de IA generó el proyecto, las dependencias transitivas que nadie eligió directamente cargan con sus propias CVEs, y no hay ningún proceso para enterarse de cuándo sale un parche.
Por qué lo hace la IA. El modelo elige un paquete que resuelve el problema inmediato y sigue adelante; no tiene ninguna relación continuada con tu proyecto que le hiciera revisar esa elección más tarde. La higiene de dependencias es una actividad de mantenimiento, y nada en generar una funcionalidad dispara el mantenimiento.
Cómo lo arreglamos. Ejecutamos un escaneo de vulnerabilidades de dependencias, parcheamos o sustituimos cualquier cosa con un exploit conocido, y montamos un proceso —aunque sea un escaneo programado simple— para que esto no vuelva a derivar en silencio en cuanto termine la auditoría.
8. Variables y Funciones Duplicadas
Abre una base de código construida con IA y busca el mismo helper de formato de fechas. A menudo lo encontrarás tres o cuatro veces, ligeramente distinto cada vez, porque cada uno se generó de forma aislada para la pantalla que lo necesitaba. Lo mismo pasa con las reglas de validación, los clientes de API, la aritmética de moneda y las constantes de configuración.
La duplicación no es solo fea: es una bomba de relojería de corrección. Cuando la lógica tiene que cambiar —una nueva regla fiscal, un bug de redondeo arreglado, un endpoint actualizado— tienes que encontrar todas las copias. Se te escapará una. Y entonces dos partes de la app discrepan sobre algo en lo que deberían coincidir, y esa discrepancia es el siguiente incidente en producción.
Por qué lo hace la IA. El modelo rara vez busca en la base de código existente un helper que podría reutilizar. Desde su perspectiva es más barato regenerar la función en línea que descubrir e importar la que ya existe. Cada generación es razonable localmente; el agregado es deriva.
Cómo lo arreglamos. Encontramos los grupos de código casi idéntico, extraemos una única fuente de verdad y hacemos pasar por ella todas las llamadas. Es una de las limpiezas de mayor impacto en casi todas las auditorías: encoge la base de código y elimina categorías enteras de bugs de «arreglado aquí pero no allí».
9. Sin Arquitectura Coherente
Este choca la primera vez que lo ves. Dos pantallas del mismo proyecto estarán escritas como si las hubieran hecho dos equipos distintos: una pide datos desde el componente, la otra a través de una capa de servicio; una guarda el estado de una forma y la siguiente de otra completamente distinta; los nombres, la estructura de carpetas y la gestión de errores cambian de funcionalidad en funcionalidad. No hay espina dorsal.
Una base de código sin arquitectura coherente es una en la que cada archivo que abres es una sorpresa. Incorporar a un desarrollador lleva semanas porque no hay ningún patrón que aprender, solo cien casos especiales que memorizar. Peor aún, cuando los patrones chocan, las costuras entre ellos son justo donde se crían los bugs.
Por qué lo hace la IA. El modelo no tiene una imagen persistente de «cómo está construida esta app». Cada prompt es un comienzo desde cero, así que recurre al patrón que encaje con esa petición concreta. A lo largo de la vida de un proyecto eso produce un mosaico: cada pieza sensata por sí sola, el conjunto incoherente.
Cómo lo arreglamos. Elegimos una arquitectura que encaje con el proyecto —no una dogmática, una que encaje— y hacemos converger la base de código hacia ella de forma incremental, para que un desarrollador que aprenda una funcionalidad pueda predecir cómo funciona la siguiente.
10. Se Evita el Estado Basado en Streams — Directos al Infierno de Callbacks
Este es el fallo técnicamente más interesante, y el que rompe en silencio las funcionalidades más difíciles. El código generado por IA tiende a evitar los modelos de estado reactivos y basados en streams en favor de callbacks imperativos. En lugar de modelar «este valor cambia con el tiempo y la interfaz reacciona», conecta un callback, que dispara otro callback, que activa una bandera, que dispara un tercero, y el resultado es el infierno de callbacks.
En pantallas simples apenas se nota. Pero en el momento en que el estado es genuinamente complejo —un formulario de varios pasos con validación cruzada entre campos, un cuadro de mando que se actualiza en vivo, cualquier cosa con debouncing, reintentos o actualizaciones optimistas— el enfoque de callbacks se desmorona. El síntoma clásico es el formulario que casi funciona: valida, pero el error se limpia en el momento equivocado; envía, pero un doble toque lo dispara dos veces.
Por qué lo hace la IA. Los callbacks imperativos son el patrón más común en sus datos de entrenamiento y el más fácil de generar pieza a pieza. Los modelos reactivos y basados en streams exigen sostener toda la máquina de estados en la cabeza a la vez, que es exactamente aquello en lo que un generador con contexto limitado es peor.
Cómo lo arreglamos. Identificamos las funcionalidades con estado complejo y reconstruimos su capa de estado en condiciones —como streams o como un modelo reactivo apropiado para el stack— para que la interfaz sea una función del estado en vez de un montón de callbacks compitiendo entre sí.
11. Ninguna Conciencia del Entorno de Despliegue
El modelo escribe código como si fuera a ejecutarse como un único proceso en una sola máquina, porque desde dentro del prompt ese es el único entorno que puede ver. No tiene ni idea de cuántas instancias van a correr, qué servicios gestionados ya existen ni cómo se enruta el tráfico. Así que recurre a la topología más simple posible, y ese valor por defecto se rompe en silencio en cuanto la app se despliega de verdad.
Los síntomas son siempre los mismos. El estado que vive en la memoria del proceso —una caché, las sesiones, contadores de límite de tasa— funciona perfectamente en una instancia y diverge en silencio en cuanto una segunda réplica aparece tras el balanceador de carga. Los trabajos en segundo plano se disparan en todas las instancias en vez de una sola vez, así que el correo sale tres veces.
Por qué lo hace la IA. No tiene ninguna imagen de tu infraestructura. No sabe que ya tienes Redis, una cola de mensajes y almacenamiento de objetos, así que los reimplementa en memoria. La topología de despliegue es exactamente el contexto que un prompt no puede contener.
Cómo lo arreglamos. Mapeamos el despliegue real y movemos el estado compartido a donde pertenece: la caché, las sesiones y los bloqueos a Redis o a la base de datos, los archivos al almacenamiento de objetos, el trabajo recurrente a un planificador o cola de verdad. El resultado es código que escala horizontalmente.
Cómo los Encontramos
Cada hallazgo de arriba empieza con una pasada automatizada y termina con una persona leyendo el código. La automatización —análisis estático, escaneo de dependencias y secretos, perfilado de costes de API— es lo que hace rápida una auditoría acelerada por IA: despeja las categorías que son mecánicas de detectar (los hallazgos 1, 4, 6 y 7 de arriba salen a la luz casi de inmediato), de modo que el tiempo de nuestros ingenieros se concentra en las categorías que exigen criterio: la lógica de autorización, la arquitectura y si una determinada ruta de error importa realmente para tu producto. Ninguna de las dos mitades funciona sola: la automatización sola se pierde todo lo que requiere entender para qué sirve el código, y la revisión manual sola no escala a una base de código real en una semana. El desglose completo del proceso está en la página del servicio de auditoría de código IA.
Qué Recibes en el Informe
Los hallazgos no llegan como una lista en bruto. Cada uno está clasificado por severidad —crítica, alta, media, baja— con una explicación del riesgo en lenguaje llano, prueba de concepto cuando aplica, y una recomendación de corrección concreta, la misma forma que ha seguido cada hallazgo de arriba. Si quieres ver el formato antes de comprometerte a nada, solicita un informe de auditoría de muestra redactado: la misma estructura de informe que produce un encargo real, con los datos que identifican al cliente eliminados.
El Patrón Detrás del Patrón
Da un paso atrás y los once hallazgos comparten una causa raíz: la IA optimiza cada generación localmente y nadie está optimizando la base de código globalmente. La gestión de secretos, la validación de entrada, la autorización, los tests, la gestión de errores, el rendimiento de las consultas, la higiene de dependencias, la deduplicación, la arquitectura, el modelado del estado y la conciencia del despliegue son todas propiedades de sistema completo. No pueden emerger de prompt en prompt, porque ningún prompt aislado ve el conjunto. Esa es exactamente la brecha que cierra una revisión humana.
Lo tranquilizador es que nada de esto significa que los cimientos construidos con IA estén desperdiciados. Las funcionalidades funcionan; el producto es real. Lo que falta es el tejido conectivo, y añadirlo es mucho más rápido que reconstruir desde cero.
Preguntas Frecuentes
Consigue los Hallazgos de tu Base de Código
Si tienes una app construida con IA y reconoces alguno de estos once, no vas retrasado: estás exactamente donde acaba casi toda base de código generada con IA. La solución no es reescribir; es una auditoría enfocada que añade el tejido conectivo que la IA no pudo.
Pasa tu base de código por una auditoría de código IA, solicita un informe de auditoría de muestra para ver antes el formato, o reserva una evaluación gratuita y te diremos cuál de los once es tu mayor riesgo, qué hace falta para arreglarlo y te daremos un presupuesto de alcance cerrado, no una conjetura.

