Pitch: um app "vibe-coded" com 8 erros clássicos, cada um com um teste que falha antes e passa depois (e o que o dono precisa ler primeiro)
Um pedido que aparece toda semana em site de freelance: "o app foi feito quase todo com um agente de IA, funciona na demonstração e agora vai receber cliente e pagamento de verdade; alguém confere o que está errado, conserta e prova que consertou?". Em vez de um case de cliente, escrevi um app aberto que responde a essa pergunta do começo ao fim: github.com/tales-andre/ai-built-app-rescue.
Por que uma empresa fictícia. O app é o agendamento online da Brightside Salons, uma rede de salões que não existe (Next.js, TypeScript, Postgres). A versão original foi escrita de propósito com os erros que código gerado por IA costuma entregar. Isso me deixa mostrar tudo, inclusive o código errado, sem expor ninguém. A versão original está na tag vibe-coded (não publique essa), e a corrigida no main.
Os 8 erros, do mais grave ao menos grave:
- A chave secreta do provedor de pagamento ia para o navegador, visível a qualquer visitante.
- Qualquer pessoa logada via e cancelava o agendamento de outra: o id do cliente vinha da requisição, não da sessão.
- O webhook de pagamento aceitava mensagem sem assinatura e cobrava de novo quando o provedor reenviava.
- Uma pessoa podia acabar com duas contas: e-mail com letra maiúscula e cadastro em paralelo.
- O horário do agendamento era salvo no fuso do servidor, não no do salão.
- O cliente definia o próprio preço, e uma entrada malformada derrubava a requisição.
- Tentativas de senha ilimitadas.
- Erros escondidos: o build ignorava erros de tipo (um deles era bug de verdade) e o webhook engolia falhas.
Enquanto corrigia, apareceu mais um: a chave que assina o cookie de sessão tinha um valor padrão no código, e o cookie não era Secure.
Como o teste prova que o problema existia. Um teste que só existe depois da correção prova pouco. A suíte é uma só, chama os route handlers reais do Next.js contra um Postgres de verdade (PGlite, Postgres compilado para WebAssembly, então roda com npm ci e nada mais) e usa só as interfaces que as duas versões compartilham. npm test roda no main e tudo passa; npm run test:before faz um git worktree da tag vibe-coded, copia a pasta de testes para dentro e roda lá: os testes de cada erro falham, e os de uso normal (entrar, agendar, cancelar o próprio horário, pagar o sinal) passam nas duas versões. Cada correção é um commit pequeno, "Fix 1" a "Fix 8", para dar para revisar um de cada vez.
O que uma segunda revisão pegou. Dois follow-ups entraram depois que revisei o próprio trabalho. O primeiro limite de login dava para passar mandando os palpites em paralelo; a correção conta a tentativa antes de checar a senha, numa transação com lock por conta: trinta palpites ao mesmo tempo recebem cinco "senha errada" e vinte e cinco "pausado". E os primeiros testes do webhook não provavam cada camada sozinha; agora o webhook tem três (tabela de id de evento, status do agendamento sob lock de linha, chave de idempotência no provedor) e os testes desligam uma de cada vez para mostrar o que cada uma segura.
O que eu não corrigi, e por quê. O limite de login é por conta e vive no banco; limite por IP pertence a uma camada na frente do app. O provedor de pagamento "PayDemo" é simulado dentro do repositório: o padrão de assinatura, dedup e idempotência segue o que provedores reais documentam, mas nenhum provedor real é chamado. O PGlite roda uma transação por vez, então os testes paralelos não pegam a falta de um lock de linha; isso só aparece num Postgres com várias conexões, que a demonstração não testou. Está tudo escrito no relatório para o dono, que é a parte que vale ler primeiro: cada achado em "o que pode acontecer com o seu negócio", em ordem de risco, com a seção técnica separada.
Faço essa revisão sob encomenda, em português e por Pix: https://tales-andre.github.io/raio-x-producao/. O repositório é público e MIT.