Blind vibe coding: a game that looks great and code that doesn't scale
Translated from the Spanish original. Read in Spanish
There’s a lot of hype around letting AI build things on its own. And yes, it can: in about 21 hours of work with Claude I built a complete browser game, with WebGL, procedurally generated worlds and synthesized sound. It looks great.
The code doesn’t.
I ran the experiment on purpose: leave the AI in auto mode, with no architecture guidance, and see what it decided on its own. The result is a good case study of what happens when you do blind vibe coding — and of why an AI SDLC process isn’t bureaucracy, but what separates a demo from a product.
The experiment
The project is 5dimension.com.ar: you click, jump into hyperspace and land on a different world every time.
The rules I set myself:
- Auto mode — the AI runs without asking for confirmation at each step.
- Features only — I asked for what I wanted to see on screen: planets, a drone, mini-games, a cockpit. I never asked for architecture, tests or lint.
- No code review — I looked at the game, not the diff.
What it cost
- Total time — about 21 hours of active work.
- Total tokens — about 613 million.
- API cost — around USD 240 at list price (between 225 and 270).
The result: it looks great
What the AI built in those hours:
- Ten planet classes and four secret places, generated from a seed that goes in the URL.
- A photorealistic WebGL renderer, a light one for phones and a 2D fallback.
- A different atmospheric entry on every world, a drone that flies where you look and a 3D cockpit.
- Eight 20-second mini-games, a planet album, a world of the day and sound synthesized with WebAudio.
Everything works. If the criterion is “does it run?”, the experiment is a success.
And here comes the uncomfortable part: the code
The whole game lived in a single index.html of 4,486 lines: markup, CSS and JavaScript together.
The AI never proposed splitting it. For 33 commits it kept adding features to the same file. Every feature made the file harder to change, and the AI kept changing it anyway.
The conclusion: without guidance, the AI didn’t pick one anti-pattern or two. It picked almost all of them.
The 20 anti-patterns
Every figure is measured on the code before any fix.
Architecture and modularity
- God Object / God File — a 4,486-line
index.htmlwith everything in it, which grew for 33 commits without anyone proposing to split it. - God entry point (hub dependency) — after the split,
main.jsimported 25 modules andframe()was a 107-lineif/elseover every state. - Big Ball of Mud — 32 files with no boundaries: the code was spread out, the design didn’t change.
- Cyclic Dependency — cockpit → cruise → entry → cockpit.
- Temporal Coupling — importing a module ran code (listeners, DOM changes, storage writes). Load order mattered and wasn’t documented.
Cohesion and coupling
- Low Cohesion —
progress.jsmixed storage, scoring, unlocks, the ship catalogue, HTML and toasts. - Global Mutable State — the
app,camandinputobjects, written from 10 modules. - Shotgun Surgery — each mini-game spread across 5 to 7 files. Changing one means touching all of them.
- Fat Interface —
sound.jsexported 38 functions, one per game event.
Encapsulation
- Broken encapsulation — several modules added fields to the world object after it was created, GPU handles included.
app.cruiseandapp.commsappeared without being declared.
Magic values
- Magic Strings — 9 states as bare strings, written from 4 files, and 268 string comparisons on state, mode, kind or type. A typo never fails: it just never matches.
- Magic Numbers — about 7,800 inline numeric constants in the JavaScript, unnamed.
State and control flow
- Implicit state machine — transitions happen wherever someone assigns
app.state. No transition table, no validation, no entry or exit hooks. - Polling instead of events — 6
setTimeoutcalls re-checked the state when they fired instead of being tied to the transition. They could fire on a later visit to the same state.
Separation of concerns
- Presentation inside logic — 262 colours hard-coded in JS and 18 HTML templates built by hand with
innerHTML. - Content coupled to logic — 286 inline
L(es, en)calls inside the game modules. - Code as strings — 2,725 lines of GLSL inside JavaScript template strings, with quality variants built by text-replace.
- Game rules coupled to the renderer — the drone’s ground height was read from the GPU with
readPixelsevery 50 ms. A game rule depended on the rendering pipeline, and the CPU waited on the GPU 20 times a second.
Error handling and security
- Swallowed Exceptions — 7 empty
catch {}blocks, and no handling of WebGL context loss: after a GPU reset, the screen went dead. - Untrusted data without encoding — names stored in localStorage went into
innerHTMLunescaped.
Left out of the list are the readability problems — 723 lines over 160 characters, 218 with five or more statements, one- or two-letter names.
What isn’t the model’s fault
It would be easy to read this as “AI codes badly”. That’s not it.
The AI did exactly what I asked for: features. It built them fast and they work. What I didn’t ask for — architecture, boundaries between modules, a data model, tests — stayed at its default. And the default of a model optimizing the task at hand is the shortest path: add the line where the rest of the code already is.
Blind vibe coding vs AI SDLC
In AI SDLC: a compendium of best practices for developing with AI I described how I work with AI when the goal is production. This experiment is the exact contrast.
- Design — blind, the architecture emerges on its own, commit by commit, and it emerges as a god file. With AI SDLC, you decide the architecture before writing a single prompt.
- Plan — blind, the AI executes straight away. With AI SDLC, you ask for a detailed plan, review it and keep it in a
.mdthat both of you can edit. The cost is more time up front; the saving is not refactoring 4,486 lines later. - Scope — blind, each request touches whatever it needs to. With AI SDLC, each change has an explicit scope and a test that verifies it.
- Review — blind, you look at the result on screen. With AI SDLC, you review the diff, and the commit message’s claims against the code.
- Guardrails — blind, there’s no lint, no tests in the loop, no deploy gate, unless you ask for them. With AI SDLC, they’re part of the setup from day one.
- Human in the loop — blind, the dev is a user asking for features. With AI SDLC, the dev is responsible for the system.
