Vibe coding às cegas: um jogo que fica muito bonito e um código que não escala
Traduzido do original em espanhol. Ler em espanhol
Há muito hype em torno de deixar a IA construir sozinha. E sim, ela consegue: em cerca de 21 horas de trabalho com o Claude montei um jogo completo no browser, com WebGL, mundos gerados proceduralmente e som sintetizado. Fica muito bonito.
O código, não.
Fiz o experimento de propósito: deixar a IA em auto mode, sem guidance de arquitetura, e ver o que ela decidia por conta própria. O resultado é um bom estudo de caso do que acontece quando você faz vibe coding às cegas — e de por que um processo de AI SDLC não é burocracia, e sim o que separa uma demo de um produto.
O experimento
O projeto é 5dimension.com.ar: você clica, salta para o hiperespaço e chega a um mundo diferente a cada vez.
As regras que me impus:
- Auto mode — a IA executa sem pedir confirmação a cada passo.
- Só features — eu pedia o que queria ver na tela: planetas, um drone, mini-jogos, um cockpit. Nunca pedi arquitetura, testes nem lint.
- Sem revisar o código — eu olhava o jogo, não o diff.
Quanto custou
- Tempo total — cerca de 21 horas de trabalho ativo.
- Tokens totais — cerca de 613 milhões.
- Custo de API — cerca de USD 240 a preço de tabela (entre 225 e 270).
O resultado: fica muito bonito
O que a IA construiu nessas horas:
- Dez classes de planeta e quatro lugares secretos, gerados a partir de um seed que vai na URL.
- Um renderer WebGL fotorrealista, outro leve para celulares e um fallback 2D.
- Uma entrada atmosférica diferente em cada mundo, um drone que voa para onde você olha e um cockpit 3D.
- Oito mini-jogos de 20 segundos, um álbum de planetas, um mundo do dia e som sintetizado com WebAudio.
Tudo funciona. Se o critério é “roda?”, o experimento é um sucesso.
E aqui vem a parte incômoda: o código
O jogo inteiro vivia em um único index.html de 4.486 linhas: markup, CSS e JavaScript juntos.
A IA nunca propôs dividi-lo. Durante 33 commits continuou adicionando features no mesmo arquivo. Cada feature deixava o arquivo mais difícil de mudar, e a IA continuava mudando mesmo assim.
A conclusão: sem guidance, a IA não escolheu um anti-pattern ou dois. Escolheu quase todos.
Os 20 anti-patterns
Cada número foi medido no código antes de qualquer correção.
Arquitetura e modularidade
- God Object / God File — um
index.htmlde 4.486 linhas com tudo dentro, que cresceu durante 33 commits sem que ninguém propusesse dividi-lo. - God entry point (hub dependency) — depois do split,
main.jsimportava 25 módulos eframe()era umif/elsede 107 linhas sobre cada estado. - Big Ball of Mud — 32 arquivos sem fronteiras: o código foi repartido, o design não mudou.
- Cyclic Dependency — cockpit → cruise → entry → cockpit.
- Temporal Coupling — importar um módulo executava código (listeners, mudanças no DOM, escritas no storage). A ordem de carga importava e não estava documentada.
Coesão e acoplamento
- Low Cohesion —
progress.jsmisturava storage, scoring, unlocks, o catálogo de naves, HTML e toasts. - Global Mutable State — os objetos
app,cameinput, escritos a partir de 10 módulos. - Shotgun Surgery — cada mini-jogo espalhado em 5 a 7 arquivos. Mudar um implica mexer em todos.
- Fat Interface —
sound.jsexportava 38 funções, uma por evento do jogo.
Encapsulamento
- Encapsulamento quebrado — vários módulos adicionavam campos ao objeto world depois de criá-lo, incluindo GPU handles.
app.cruiseeapp.commsapareciam sem estar declarados.
Magic values
- Magic Strings — 9 estados como strings soltas, escritos a partir de 4 arquivos, e 268 comparações de strings sobre state, mode, kind ou type. Um typo nunca falha: simplesmente nunca dá match.
- Magic Numbers — cerca de 7.800 constantes numéricas inline no JavaScript, sem nome.
Estado e fluxo de controle
- State machine implícita — as transições acontecem onde quer que alguém atribua
app.state. Sem tabela de transições, sem validação, sem hooks de entrada ou saída. - Polling em vez de eventos — 6
setTimeoutreverificavam o estado ao disparar em vez de estarem atrelados à transição. Podiam disparar em uma visita posterior ao mesmo estado.
Separation of concerns
- Apresentação dentro da lógica — 262 cores hardcoded em JS e 18 templates HTML montados à mão com
innerHTML. - Conteúdo acoplado à lógica — 286 chamadas
L(es, en)inline dentro dos módulos do jogo. - Code as strings — 2.725 linhas de GLSL dentro de template strings de JavaScript, com variantes de qualidade montadas por text-replace.
- Regras do jogo acopladas ao renderer — a altura do solo para o drone era lida da GPU com
readPixelsa cada 50 ms. Uma regra do jogo dependia do pipeline de rendering, e a CPU esperava a GPU 20 vezes por segundo.
Tratamento de erros e segurança
- Swallowed Exceptions — 7 blocos
catch {}vazios, e nenhum tratamento de context loss do WebGL: depois de um reset da GPU, a tela ficava morta. - Dados untrusted (não confiáveis) sem encoding — nomes guardados no localStorage entravam no
innerHTMLsem escape.
Ficaram fora da lista os problemas de legibilidade — 723 linhas com mais de 160 caracteres, 218 com cinco statements ou mais, nomes de uma ou duas letras.
O que não é culpa do modelo
Seria fácil ler isto como “a IA programa mal”. Não é isso.
A IA fez exatamente o que eu pedi: features. Fez rápido e elas funcionam. O que eu não pedi — arquitetura, limites entre módulos, um modelo de dados, testes — ficou no default. E o default de um modelo otimizando a tarefa imediata é o caminho mais curto: adicionar a linha onde já está o resto do código.
Vibe coding às cegas vs AI SDLC
Em AI SDLC: um compêndio de boas práticas para desenvolver com IA descrevi como trabalho com IA quando o objetivo é produção. Este experimento é o contraste exato.
- Design — às cegas, a arquitetura surge sozinha, commit a commit, e surge como god file. Com AI SDLC, você decide a arquitetura antes de escrever um único prompt.
- Plano — às cegas, a IA executa direto. Com AI SDLC, você pede um plano detalhado, revisa e o deixa em um
.mdque os dois podem modificar. O custo é mais tempo no início; a economia é não refatorar 4.486 linhas depois. - Scope — às cegas, cada pedido mexe no que for preciso. Com AI SDLC, cada mudança tem um scope explícito e um teste que a verifica.
- Revisão — às cegas, você olha o resultado na tela. Com AI SDLC, você revisa o diff, e os claims do commit message contra o código.
- Guardrails — às cegas, não há lint, nem testes no loop, nem gate de deploy, a menos que você peça. Com AI SDLC, fazem parte do setup desde o primeiro dia.
- Human in the loop — às cegas, o dev é um usuário que pede features. Com AI SDLC, o dev é responsável pelo sistema.
