Qué código genera Bolt y qué hay que reescribir

Editor mostrando los archivos iniciales de un proyecto generado automáticamente

La primera vez que uno de nuestros clientes nos enseñó un proyecto salido de Bolt, tardamos once minutos en encontrar la clave de servicio de Supabase incrustada en un componente de React. Once minutos. Y el producto llevaba tres semanas en producción con usuarios reales dentro.

No contamos esto para asustar a nadie. Lo contamos porque resume con bastante exactitud el patrón que vemos repetirse al analizar qué código genera Bolt: lo generado funciona, se ve bien, arranca sin errores en el navegador y, al mismo tiempo, contiene decisiones que ningún equipo técnico firmaría. Saber distinguir qué capas puedes conservar tal cual y cuáles hay que rehacer antes de cobrar el primer euro es, básicamente, la diferencia entre un atajo de seis semanas y una deuda de seis meses.

Vamos a desmontarlo por capas, de lo más superficial a lo más estructural.

El stack por defecto: qué te entrega realmente al primer prompt

El proyecto que entrega por defecto es una aplicación de una sola página escrita en TypeScript sobre React 18, empaquetada con Vite, estilada con Tailwind CSS y conectada a Supabase cuando pides persistencia de datos. No incluye renderizado en servidor, rutas de API propias ni pruebas automatizadas.

Esa convergencia es casi absoluta. De los 34 proyectos que hemos revisado entre enero de 2024 y octubre de 2025, 29 llegaron con ese stack exacto. Los otros cinco eran Next.js, y cuatro de esos cinco venían de usuarios que lo habían pedido a mano.

¿Por qué importa esa uniformidad? Porque condiciona todo lo que viene después. Una aplicación de una sola página sobre Vite no tiene renderizado en servidor, no tiene rutas de API propias y no tiene una frontera clara entre lo que ejecuta el navegador del usuario y lo que ejecuta tu infraestructura. Esa ausencia de frontera es el origen del 70% de los problemas que encontramos.

El árbol de archivos inicial suele rondar los 40-60 ficheros, con un src/components plano, un src/App.tsx que hace de router improvisado y un src/lib/supabase.ts con el cliente inicializado. Limpio, legible, casi bonito. Y engañoso: la legibilidad de los primeros veinte archivos no dice nada sobre lo que pasa cuando llegas al archivo cuarenta y cinco.

Lo que entra en el paquete sin que lo pidas

Además del stack, llegan decisiones implícitas que nadie te comunica. Lucide para iconos. Tailwind con la configuración por defecto, sin tokens de diseño propios. A veces shadcn/ui parcialmente copiado, con tres componentes de los cincuenta disponibles y sin el CLI instalado, lo cual significa que no puedes añadir el cuarto sin hacerlo a mano.

Mira, al final esto tiene una lectura positiva: son elecciones mainstream. Nadie te va a meter un framework abandonado de 2019. El problema no es la calidad de las piezas, es cómo están ensambladas.

Inventario rápido: conservar, refactorizar o reescribir

Esta es la clasificación que aplicamos en la primera hora de cualquier revisión, antes de abrir un solo archivo de lógica:

  • src/components/*.tsx (marcado y clases) → conservar
  • tailwind.config.js y valores de color escritos a mano → refactorizar
  • Componentes duplicados de botón, modal y campo → refactorizar
  • src/lib/supabase.ts invocado desde la vista → reescribir
  • Políticas de acceso a las tablas → reescribir desde cero
  • .env, prefijos VITE_ y literales de credenciales → reescribir y rotar
  • useState y useEffect con peticiones sueltas → refactorizar
  • Configuración de linter, pruebas e integración continua → crear, no existe
Capa del proyecto Estado al generarse Acción recomendada
Marcado y estilos Coherente, sin accesibilidad Conservar y revisar
Componentes de interfaz Duplicados con nombres distintos Refactorizar
Acceso a la base de datos Invocado desde el navegador Reescribir
Control de acceso a los datos Ausente o permisivo Reescribir desde cero
Credenciales y variables Expuestas en el paquete público Reescribir y rotar
Gestión de estado Peticiones duplicadas Refactorizar
Arquitectura de carpetas Plana, sin dominios Refactorizar
Pruebas, linter, despliegue Inexistentes Crear

El mecanismo interno: por qué escribe así y no de otra forma

Bolt no es un programador remoto. Es un modelo de lenguaje con un entorno de ejecución encima que, según la documentación de StackBlitz, corre Node.js dentro de la propia pestaña del navegador. Esa arquitectura explica casi todo su comportamiento.

Primero: optimiza para que algo aparezca en pantalla en el menor número de iteraciones posible. Su función de éxito es «el preview carga y se parece a lo que pediste». No es «esto aguanta 5.000 usuarios concurrentes». Cuando el objetivo es la demo inmediata, el camino más corto pasa por meter la llamada a la base de datos directamente en el componente que muestra los datos. Funciona. Es horrible. Y funciona.

Segundo: trabaja con una ventana de contexto finita sobre un proyecto que crece. En nuestras pruebas internas, a partir de unos 25-30 archivos empezamos a ver duplicación silenciosa: dos funciones de formateo de fechas con nombres distintos, dos hooks que hacen la misma petición, tres variantes del mismo botón. No es dejadez del modelo, es que ya no ve todo a la vez.

Tercero, y esto es lo que más nos costó entender: el generador arrastra sus propias decisiones anteriores. Si en el prompt cuatro creó un patrón discutible, en el prompt veintitrés lo replicará por coherencia interna. Nosotros llegamos a creer que bastaba con corregirle una vez y seguir adelante. No. Lo que descubrimos tras tres proyectos seguidos es que el patrón malo se reproduce a menos que lo elimines de raíz del archivo original, porque el modelo lee lo que ya existe y lo toma como referencia de estilo. Desde entonces intervenimos antes, no después.

Diagrama en pizarra de las capas de una aplicación web y sus fronteras

Capa de interfaz: lo que casi siempre puedes conservar

Aquí van buenas noticias, que también hacen falta. El marcado y los estilos son, con diferencia, lo mejor que sale del generador.

Estructura semántica razonable, clases de Tailwind coherentes, estados de carga y vacío contemplados en la mayoría de vistas, diseño adaptativo que aguanta un móvil de 375 píxeles sin romperse. En un proyecto de gestión de reservas que auditamos en junio, conservamos el 84% del marcado original. Tocamos solo la accesibilidad: faltaban etiquetas en catorce campos de formulario y el contraste de dos botones secundarios se quedaba en 3.1:1 cuando la norma pide 4.5:1.

Los tres retoques que sí merecen la pena

Extraer los valores repetidos a la configuración de Tailwind. Cuando el mismo azul aparece escrito a mano en 23 sitios, cambiar la marca cuesta una tarde en lugar de dos minutos.

Unificar los componentes duplicados. Normalmente encontrarás entre cuatro y siete variantes del mismo elemento con nombres tipo Button, PrimaryButton y ActionButton. Uno solo, con props.

Y revisar teclado y lector de pantalla, que es lo que el modelo ignora sistemáticamente porque el preview visual no lo penaliza.

Datos y autenticación: donde empieza la intervención obligatoria

Si solo vas a rehacer una cosa, que sea esta.

El patrón por defecto consiste en llamar al cliente de Supabase desde el componente, con el anon key expuesto en el paquete que descarga el navegador. Eso, por sí mismo, no es un fallo: el anon key está diseñado para ser público. El fallo es lo que viene con él, o mejor dicho, lo que no viene: las políticas de seguridad a nivel de fila.

De los proyectos que hemos revisado, 26 de 34 las tenían desactivadas o reducidas a una regla del tipo USING (true), que en la práctica significa que cualquier persona con una consola del navegador puede leer la tabla completa de usuarios. Uno de ellos guardaba ahí datos de facturación. Lo detectamos un viernes por la tarde y lo parcheamos esa misma noche.

El orden correcto de esta capa

Primero, activar esa protección en todas las tablas y escribir las reglas una a una, verificando cada rol. Segundo, mover cualquier operación privilegiada a funciones de servidor o edge functions, nunca al navegador. Tercero, validar la entrada en el servidor con algo como Zod, porque la validación que el generador escribe vive en el formulario y un curl se la salta sin esfuerzo.

¿Es tedioso? Bastante. ¿Se puede posponer? Solo si tu aplicación no guarda ningún dato de terceros, lo cual descarta el 95% de los SaaS. Cuando un cliente nos pide priorizar y el presupuesto solo alcanza para una intervención, siempre proponemos esta antes que cualquier otra: es la única cuyo fallo no se mide en horas de trabajo, sino en obligaciones legales.

Claves, variables y secretos: el fallo más repetido del generador

Volvemos al comienzo del artículo, porque merece su propio apartado.

Las claves acaban en tres sitios distintos, a veces en los tres a la vez: literales dentro de un archivo .ts, variables con prefijo VITE_ que Vite inyecta en el paquete público, y un .env que, sorpresa, no siempre aparece en el .gitignore. Esto último lo hemos visto en 9 de los proyectos revisados. Nueve repositorios con el secreto en el historial de commits.

La regla práctica es simple y no admite matices: si una credencial permite escribir, borrar o cobrar, no puede estar en el navegador. Ni ofuscada, ni en base64, ni partida en dos trozos. El navegador es territorio del usuario.

Y si ya la has publicado alguna vez, no vale con borrarla del archivo. Hay que rotarla en el proveedor, porque el historial de Git es para siempre. (Sí, esto lo aprendimos por las malas, en 2022, con una clave de Stripe en modo test que por suerte no era la de producción. Por suerte).

Representación del control de acceso a credenciales en infraestructura web

Estado y arquitectura de archivos: el desorden que escala mal

Hasta unas 15 pantallas, el enfoque del generador aguanta. A partir de ahí empieza a crujir.

El patrón típico: useState por todas partes, peticiones dentro de useEffect sin cancelación, y el mismo dato pedido cuatro veces en cuatro componentes hermanos. Medimos un caso concreto en agosto: la pantalla de panel principal lanzaba 11 peticiones al cargar, de las cuales 6 eran redundantes. Con una capa de caché de consultas bajamos a 5 y el tiempo hasta contenido útil pasó de 2,8 a 1,1 segundos.

La arquitectura de carpetas es el otro frente. Todo plano dentro de components, sin separar por dominio funcional. Cuando el proyecto pasa de 60 archivos, encontrar dónde vive la lógica de precios se convierte en una búsqueda de texto libre. Reorganizar por features, no por tipo de archivo, es el cambio más rentable en horas invertidas frente a horas ahorradas. Nos suele llevar entre dos y cuatro horas y devuelve el control de golpe.

Total, que aquí no hablamos de seguridad ni de legalidad, sino de velocidad futura. Puedes convivir con el desorden. Lo que no puedes es convivir con él y prometer una nueva función cada semana.

Dependencias, pruebas y build: lo que nunca aparece en el proyecto

Hay una categoría entera de cosas que simplemente no existen en lo que recibes, y merece la pena enumerarlas porque su ausencia no genera ningún error visible.

  • Pruebas de cualquier tipo: ni unitarias ni de extremo a extremo
  • Configuración de linter y formateador con reglas del equipo
  • Integración continua que bloquee un despliegue roto
  • Registro de errores en producción
  • Versiones fijadas en las dependencias
  • Análisis del tamaño del paquete final

Ese último punto tiene truco. Un proyecto que arranca en 180 KB de JavaScript puede acabar en 1,4 MB después de treinta iteraciones, porque cada petición nueva añade una librería y ninguna retira la anterior. Encontramos un caso con tres librerías de gráficos instaladas y solo una en uso. Borrarlas recortó 340 KB.

Para una versión de prueba con veinte usuarios amigos, puedes vivir sin nada de esto. Para un producto que factura, no: el primer despliegue que rompe el pago de un cliente cuesta más que todas las pruebas que no escribiste. Ese cambio de mentalidad, el que separa el prototipo que enseñas a un conocido del sistema que sostiene una suscripción mensual, es exactamente el trabajo de convertir tu MVP en un producto real, y no consiste en añadir funciones sino en añadir garantías alrededor de las que ya tienes funcionando.

Panel con resultados de pruebas automatizadas y métricas de rendimiento web

Reconstruir el proyecto: orden de intervención en una tarde

Si tuviéramos que condensar el procedimiento en una secuencia que cualquiera pueda seguir un sábado, sería esta. El reparto de tiempos sale de promediar nuestras últimas doce intervenciones.

  1. Auditar secretos y rotar todo lo expuesto (30 min)
  2. Activar seguridad a nivel de fila y escribir reglas por rol (2-3 h)
  3. Mover operaciones privilegiadas fuera del navegador (2 h)
  4. Validar entradas en servidor con esquemas (1 h)
  5. Reorganizar carpetas por dominio funcional (2-4 h)
  6. Unificar componentes duplicados (1-2 h)
  7. Instalar linter, formateador y una prueba de humo (1 h)
  8. Limpiar dependencias muertas y medir el paquete (45 min)

Los cuatro primeros pasos son innegociables antes de abrir el registro al público. Los cuatro últimos pueden esperar una semana, no un trimestre.

Hay un paso cero que solemos recomendar y casi nadie hace: mapear qué existe realmente antes de tocar nada, y hacerlo con un inventario escrito en lugar de con la intuición del primer vistazo. Por eso arrancamos siempre con nuestra auditoría de código antes de mover un solo archivo, y no por formalismo: nos ahorró repetir el error de dedicar seis horas a ordenar un módulo de facturación que el cliente terminó descartando quince días después. Creímos que empezar por lo más sucio era lo lógico; descubrimos que empezar por lo que sobrevive al roadmap ahorra el doble. Sin ese mapa, la prioridad la decide el pánico.

Cuándo empezar de cero y cuándo seguir iterando en el editor

Esta es la pregunta que más nos llega por correo, y la respuesta no depende del volumen de archivos sino de dónde está el problema.

Sigue iterando dentro del entorno si lo que falta son pantallas, si el modelo de datos es estable y si nadie ha pagado todavía. Es lo que mejor hace la herramienta y no tiene sentido renunciar a esa velocidad por purismo.

Sal del entorno y trabaja en local, con control de versiones, cuando aparezca cualquiera de estas tres señales: hay dinero real circulando, hay datos de terceros almacenados, o llevas más de dos prompts seguidos peleando con el asistente para que no rompa algo que ya funcionaba.

Empieza de cero solo en un escenario: cuando el modelo de datos está mal planteado de raíz. Todo lo demás se arregla por capas. Un esquema equivocado, en cambio, contamina cada consulta, cada regla de acceso y cada componente, y llega un punto en que corregirlo cuesta más que rehacerlo. Nos ha pasado dos veces en dos años. Las otras treinta y dos, la reconstrucción por capas salió más barata.

Dos desarrolladores revisando juntos un proyecto antes de pasarlo a producción

Preguntas frecuentes

¿Qué lenguaje y framework usa el código generado?

TypeScript sobre React 18, empaquetado con Vite y estilado con Tailwind CSS. Cuando el proyecto necesita guardar información, la integración habitual es Supabase. Salvo petición explícita en el prompt, no verás Next.js ni renderizado en servidor.

¿El código generado es apto para producción?

La capa visual sí, prácticamente entera. La capa de datos, no. Arranca sin errores y pasa una demo, pero le faltan control de acceso, validación en servidor, pruebas y registro de errores. Es un punto de partida válido, no un producto terminado.

¿Cómo se exporta el proyecto a GitHub?

Desde la propia plataforma puedes publicar el proyecto en un repositorio y clonarlo en local. A partir de ahí se continúa en un editor como Cursor o VS Code y se despliega en el hosting que prefieras, fuera del entorno original.

¿Qué modelo de IA hay detrás?

Según la documentación pública del producto y los análisis técnicos del sector, el generador se apoya en un modelo de la familia Claude, de Anthropic. Conviene tomarlo como dato sujeto a cambios: la pila de modelos de estas herramientas se actualiza con cada versión del agente.

¿Es seguro publicar una aplicación creada así?

Tal como sale, no. Con las reglas de acceso a las tablas escritas rol por rol, las credenciales de escritura fuera del navegador y la validación movida al servidor, sí. Ese trabajo son entre seis y ocho horas, y es previo a cualquier registro público.

Conclusión

Bolt es una máquina excelente de fabricar puntos de partida. Su interfaz es aprovechable casi entera, su stack es sensato y la velocidad que da en las primeras semanas es difícil de igualar por un equipo humano.

Lo que no fabrica son garantías. Y las garantías viven en tres sitios concretos: el control de acceso a los datos, la gestión de credenciales y la frontera entre lo que ejecuta el usuario y lo que ejecutas tú. Esas tres capas hay que rehacerlas a mano, siempre, sin excepciones y antes de cobrar. El resto es orden, y el orden se puede ir ganando por el camino.

Nadie pierde nada por empezar con un generador. Se pierde por confundir «arranca sin errores» con «está terminado».

Founder & Software Engineer
Ingeniero de Software y Desarrollador Full Stack con más de 10 años de experiencia operativa. Dirige MVPtoSaaS aplicando los mismos estándares de fiabilidad y rendimiento exigidos en sistemas industriales y de alta criticidad. El enfoque de Felipe asegura que cada producto que transita por MVPtoSaaS cuente con código limpio, bases de datos optimizadas para alta concurrencia y despliegues automatizados preparados para el crecimiento sostenido sin deuda técnica.