Vibe coding a ciegas: un juego que se ve muy bien y un código que no escala
Hay mucho hype alrededor de dejar que la IA construya sola. Y sí, puede: en unas 21 horas de trabajo con Claude armé un juego completo en el browser, con WebGL, mundos generados proceduralmente y sonido sintetizado. Se ve muy bien.
El código, no.
Hice el experimento a propósito: dejar a la IA en auto mode, sin guidance de arquitectura, y ver qué decidía por su cuenta. El resultado es un buen caso de estudio de lo que pasa cuando hacés vibe coding a ciegas — y de por qué un proceso de AI SDLC no es burocracia, sino lo que separa un demo de un producto.
El experimento
El proyecto es 5dimension.com.ar: hacés click, saltás al hiperespacio y llegás a un mundo distinto cada vez.
Las reglas que me puse:
- Auto mode — la IA ejecuta sin pedir confirmación en cada paso.
- Sólo features — yo pedía lo que quería ver en pantalla: planetas, un drone, mini-juegos, un cockpit. Nunca pedí arquitectura, tests ni lint.
- Sin revisar el código — miraba el juego, no el diff.
Lo que costó
- Tiempo total — unas 21 horas de trabajo activo.
- Tokens totales — unos 613 millones.
- Costo de API — alrededor de USD 240 a precio de lista (entre 225 y 270).
El resultado: se ve muy bien
Lo que la IA construyó en esas horas:
- Diez clases de planeta y cuatro lugares secretos, generados desde un seed que va en la URL.
- Un renderer WebGL fotorrealista, otro liviano para teléfonos y un fallback 2D.
- Entrada atmosférica distinta en cada mundo, un drone que vuela donde mirás y un cockpit 3D.
- Ocho mini-juegos de 20 segundos, un álbum de planetas, un mundo del día y sonido sintetizado con WebAudio.
Todo funciona. Si el criterio es “¿anda?”, el experimento es un éxito.
Y acá viene lo incómodo: el código
Todo el juego vivía en un único index.html de 4.486 líneas: markup, CSS y JavaScript juntos.
La IA nunca propuso partirlo. Durante 33 commits siguió agregando features sobre el mismo archivo. Cada feature hacía el archivo más difícil de cambiar, y la IA lo seguía cambiando igual.
La conclusión: sin guidance, la IA no eligió un anti-pattern o dos. Eligió casi todos.
Los 20 anti-patterns
Cada cifra está medida sobre el código antes de cualquier corrección.
Arquitectura y modularidad
- God Object / God File — un
index.htmlde 4.486 líneas con todo adentro, que creció durante 33 commits sin que nadie propusiera partirlo. - God entry point (hub dependency) — después del split,
main.jsimportaba 25 módulos yframe()era unif/elsede 107 líneas sobre cada estado. - Big Ball of Mud — 32 archivos sin fronteras: el código se repartió, el diseño no cambió.
- Cyclic Dependency — cockpit → cruise → entry → cockpit.
- Temporal Coupling — importar un módulo ejecutaba código (listeners, cambios al DOM, escrituras a storage). El orden de carga importaba y no estaba documentado.
Cohesión y acoplamiento
- Low Cohesion —
progress.jsmezclaba storage, scoring, unlocks, el catálogo de naves, HTML y toasts. - Global Mutable State — los objetos
app,cameinput, escritos desde 10 módulos. - Shotgun Surgery — cada mini-juego repartido en 5 a 7 archivos. Cambiar uno implica tocarlos todos.
- Fat Interface —
sound.jsexportaba 38 funciones, una por evento del juego.
Encapsulamiento
- Encapsulamiento roto — varios módulos le agregaban campos al objeto world después de crearlo, incluidos GPU handles.
app.cruiseyapp.commsaparecían sin estar declarados.
Magic values
- Magic Strings — 9 estados como strings sueltos, escritos desde 4 archivos, y 268 comparaciones de strings sobre state, mode, kind o type. Un typo nunca falla: simplemente nunca matchea.
- Magic Numbers — unas 7.800 constantes numéricas inline en el JavaScript, sin nombre.
Estado y control de flujo
- State machine implícita — las transiciones pasan donde sea que alguien asigne
app.state. Sin tabla de transiciones, sin validación, sin hooks de entrada o salida. - Polling en vez de eventos — 6
setTimeoutre-chequeaban el estado al disparar en vez de estar atados a la transición. Podían disparar en una visita posterior al mismo estado.
Separation of concerns
- Presentación dentro de la lógica — 262 colores hardcodeados en JS y 18 templates HTML armados a mano con
innerHTML. - Contenido acoplado a la lógica — 286 llamadas
L(es, en)inline dentro de los módulos de juego. - Code as strings — 2.725 líneas de GLSL dentro de template strings de JavaScript, con variantes de calidad armadas por text-replace.
- Reglas de juego acopladas al renderer — la altura del suelo para el drone se leía de la GPU con
readPixelscada 50 ms. Una regla del juego dependía del pipeline de rendering, y la CPU esperaba a la GPU 20 veces por segundo.
Manejo de errores y seguridad
- Swallowed Exceptions — 7 bloques
catch {}vacíos, y ningún manejo de context loss de WebGL: ante un reset de la GPU, la pantalla quedaba muerta. - Datos untrusted (no confiables) sin encoding — nombres guardados en localStorage entraban a
innerHTMLsin escapar.
Afuera de la lista quedaron los problemas de legibilidad — 723 líneas de más de 160 caracteres, 218 con cinco statements o más, nombres de una o dos letras.
Lo que no es culpa del modelo
Sería fácil leer esto como “la IA programa mal”. No es eso.
La IA hizo exactamente lo que le pedí: features. Las hizo rápido y funcionan. Lo que no le pedí — arquitectura, límites entre módulos, un modelo de datos, tests — quedó en default. Y el default de un modelo optimizando la tarea inmediata es el camino más corto: agregar la línea donde ya está el resto del código.
Vibe coding a ciegas vs AI SDLC
En AI SDLC: un compendio de buenas prácticas para desarrollar con IA describí cómo trabajo con IA cuando el objetivo es producción. Este experimento es el contraste exacto.
- Diseño — a ciegas, la arquitectura emerge sola, commit a commit, y emerge como god file. Con AI SDLC, vos decidís la arquitectura antes de escribir un solo prompt.
- Plan — a ciegas, la IA ejecuta directo. Con AI SDLC, le pedís un plan detallado, lo revisás y lo dejás en un
.mdque los dos pueden modificar. El costo es más tiempo al principio; el ahorro es no refactorizar 4.486 líneas después. - Scope — a ciegas, cada pedido toca lo que haga falta. Con AI SDLC, cada cambio tiene un scope explícito y un test que lo verifica.
- Revisión — a ciegas, mirás el resultado en pantalla. Con AI SDLC, revisás el diff, y los claims del commit message contra el código.
- Guardrails — a ciegas, no hay lint, ni tests en el loop, ni gate de deploy, salvo que los pidas. Con AI SDLC, son parte del setup desde el primer día.
- Human in the loop — a ciegas, el dev es un usuario que pide features. Con AI SDLC, el dev es responsable del sistema.
