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

  1. God Object / God File — um index.html de 4.486 linhas com tudo dentro, que cresceu durante 33 commits sem que ninguém propusesse dividi-lo.
  2. God entry point (hub dependency) — depois do split, main.js importava 25 módulos e frame() era um if/else de 107 linhas sobre cada estado.
  3. Big Ball of Mud — 32 arquivos sem fronteiras: o código foi repartido, o design não mudou.
  4. Cyclic Dependency — cockpit → cruise → entry → cockpit.
  5. 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

  1. Low Cohesion — progress.js misturava storage, scoring, unlocks, o catálogo de naves, HTML e toasts.
  2. Global Mutable State — os objetos app, cam e input, escritos a partir de 10 módulos.
  3. Shotgun Surgery — cada mini-jogo espalhado em 5 a 7 arquivos. Mudar um implica mexer em todos.
  4. Fat Interface — sound.js exportava 38 funções, uma por evento do jogo.

Encapsulamento

  1. Encapsulamento quebrado — vários módulos adicionavam campos ao objeto world depois de criá-lo, incluindo GPU handles. app.cruise e app.comms apareciam sem estar declarados.

Magic values

  1. 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.
  2. Magic Numbers — cerca de 7.800 constantes numéricas inline no JavaScript, sem nome.

Estado e fluxo de controle

  1. 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.
  2. Polling em vez de eventos — 6 setTimeout reverificavam o estado ao disparar em vez de estarem atrelados à transição. Podiam disparar em uma visita posterior ao mesmo estado.

Separation of concerns

  1. Apresentação dentro da lógica — 262 cores hardcoded em JS e 18 templates HTML montados à mão com innerHTML.
  2. Conteúdo acoplado à lógica — 286 chamadas L(es, en) inline dentro dos módulos do jogo.
  3. Code as strings — 2.725 linhas de GLSL dentro de template strings de JavaScript, com variantes de qualidade montadas por text-replace.
  4. Regras do jogo acopladas ao renderer — a altura do solo para o drone era lida da GPU com readPixels a 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

  1. Swallowed Exceptions — 7 blocos catch {} vazios, e nenhum tratamento de context loss do WebGL: depois de um reset da GPU, a tela ficava morta.
  2. Dados untrusted (não confiáveis) sem encoding — nomes guardados no localStorage entravam no innerHTML sem 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 .md que 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.

Maximiliano Díaz Doglia

AI Platform Engineer & Full-Stack Developer
Building Enterprise Integrations & Automations