Pular para o conteúdo

O que é vibe coding e por que ele quebra na terceira feature

A demo funciona na primeira hora. O problema aparece quando o agente esquece por que a regra existe. O que separa os dois é arquitetura de contexto.

Por Otther04 de set. 202611 min de leitura
Capa do artigo: à esquerda, barras brancas empilhadas em ordem sobre fundo claro; à direita, as mesmas barras espalhadas e tortas sobre fundo coral

Se você já pediu uma tela inteira a um agente de IA e ela veio funcionando, você fez vibe coding. A prática ficou popular porque a primeira hora impressiona: você descreve a intenção em linguagem natural, o agente escreve, você roda, funciona.

O problema aparece na terceira semana, quando o agente propõe a quarta variação do mesmo bottom sheet, importa o cliente de banco dentro de um componente de UI, e reintroduz uma biblioteca que você tirou do projeto dois meses atrás por travar o scroll no Android. Em nenhum desses casos o modelo tinha como saber. A informação existia, mas não num lugar que ele alcançasse.

Este artigo é sobre como estruturar o conhecimento de um projeto para que o agente decida certo sem você reensinar as mesmas regras toda sessão. Os números vêm de um app real, o Gaveta, com cinco meses de produção, 726 commits, 38 versões publicadas nas lojas e um desenvolvedor.

Diagrama: trinta e uma variações do mesmo componente colapsando em um único bloco

O que é vibe coding

Vibe coding é escrever software descrevendo o resultado em linguagem natural e deixando o agente de IA produzir o código, revisando pelo comportamento em vez de pela leitura linha a linha. O termo se popularizou em 2025 e virou guarda-chuva para tudo que vai do protótipo descartável ao produto em produção.

Vibe coding é um modo de trabalho, e ele tem duas versões com destinos bem diferentes.

Na versão descartável, você aceita o código sem contrato. O protótipo é jogado fora na semana seguinte e nada disso importa.

Na versão de produção, o código fica. E aí o custo se desloca: escrever deixa de ser o passo caro, porque o agente escreve rápido. O caro passa a ser que toda sessão começa do zero.

Diagrama: à esquerda um bloco tracejado que se desfaz, à direita um bloco sólido que permanece

Por que o agente esquece o contexto

O agente não sabe por que aquele hook de tela tem um sub-view-model para o sheet. Não sabe que a lib de blur foi trocada porque recompunha a cada frame e travava a lista. Não sabe que a Apple já rejeitou um release por causa de uma palavra na nota da loja.

Você sabe de tudo isso, mas também esquece, e daqui a três meses você é uma pessoa diferente lendo o próprio código.

Sem um lugar que responda essas perguntas no momento da decisão, dois desfechos acontecem, e os dois custam caro:

  • A arquitetura apodrece por acréscimo. Cada tela nova inventa a própria variante do padrão. No Gaveta, esse processo produziu 31 bottom sheets escritos à mão antes de alguém contar.
  • Você vira o compilador humano do agente. Cada PR vira uma sessão de reensinar as mesmas cinco regras. O ganho de velocidade evapora na revisão.

Tratar contexto como artefato de arquitetura resolve os dois, com a mesma disciplina que se aplica a código: fronteiras explícitas, uma fonte de verdade por assunto, e enforcement automático onde couber.

Diagrama: cada linha recomeça de um marcador coral, ilustrando o contexto reconstruído a cada sessão

Um documento soberano por fronteira

Um documento gigante que tenta cobrir tudo não é lido por humano nem por agente. Ele estoura a janela de atenção e vira ruído.

A alternativa é aplicar arquitetura da informação ao próprio repositório. No Gaveta, o documento da raiz tem 212 linhas e abre declarando o que ele não é: ele descreve como os pacotes se conectam, e nunca como codar dentro de um deles. Isso fica com o documento do pacote, e a regra é declarada como inviolável.

A raiz vira uma tabela de roteamento:

Área onde você vai mexerDocumento soberano
mobile/**mobile/CLAUDE.md (1.259 linhas)
web/**web/CLAUDE.md (1.853 linhas)
supabase/**docs/SUPABASE.md
Produto e designPRODUCT.md e DESIGN.md

O escopo do documento acompanha o escopo do problema. O menor deles tem 116 linhas e mora dentro da pasta do componente de blur, porque a decisão que ele guarda só importa para quem está escrevendo blur. A raiz não sabe nada sobre blur, e essa ignorância é proposital.

Fecha com uma instrução direta: se a mudança não cabe em nenhum recipe do documento soberano, pare e pergunte, não improvise arquitetura.

Diagrama: um documento largo no topo roteando para cinco documentos menores, um por fronteira

A divisão entre contrato e receituário

Cada documento de pacote se divide em duas metades com naturezas distintas.

O contrato responde "o que eu não posso fazer". São princípios, uma tabela de camadas, convenção de nomes e a lista de regras de lint. Cabe em poucas seções, porque documento longo demais deixa de ser consultado. A tabela de camadas do Gaveta tem cinco linhas e responde noventa por cento das perguntas de "onde isso vai":

CamadaPodeNão pode
Serviçoasync puro, banco, RPC, fetchHooks React, JSX
Hook de dadosChave de cache via registro central, invalidaçõesJSX de página
Hook de telaAgregar hooks, navegação, derivar view modelImportar o cliente de banco direto
ViewProps tipadas, JSXQuery, mutation, acesso a dados
Rotaconst vm = useXScreen() e renderizar a ViewLógica de negócio, query inline

O receituário responde outra coisa: "como eu faço o que preciso agora". São passos numerados para criar feature do zero e um cookbook de padrões que já se repetiram, cada um com link para o exemplo real no código.

E o cookbook cresce por repetição, não por antecipação: um padrão só entra depois de aparecer em dois lugares. Cookbook antecipado vira enfeite.

O detalhe que mais economiza tempo é uma tabela de "olhe esta feature como referência", ligando cada tipo de trabalho a um arquivo existente. Documentação que aponta para o código real envelhece melhor que documentação que descreve o código real.

Diagrama: cinco linhas curtas e uniformes à esquerda, muitas linhas de comprimento variado à direita

Transformar convenção em regra de lint

Documento normativo costuma sobreviver aos dois primeiros PRs. Na primeira feature você segue a regra, na segunda também, na terceira você tem pressa e nada impede você de ignorá-la. Uma regra de lint impede. No Gaveta, quatro coisas quebram o build em nível de erro: cliente de banco e biblioteca de cache importados na camada de rota ou em qualquer View, chave de cache escrita como literal fora do registro central, e modal escrito à mão em vez do primitive compartilhado.

A mensagem de erro ensina a saída junto com a proibição: "bottom sheet não se escreve à mão, use o primitive, que já traz arrastar-para-fechar, safe area, teclado e tokens".

Vale a mesma lógica para o design system. Um token documentado em prosa depende de alguém lembrar dele. Uma regra de lint que proíbe hex literal em JSX não depende, e a diferença aparece no terceiro mês, com quarenta telas no lugar de quatro.

Diagrama: linhas cruzando uma barreira vertical coral, algumas passando e outras sendo barradas

Declarar a dívida dentro do próprio documento

O documento da web do Gaveta abre dizendo que descreve a arquitetura-alvo e não o estado atual do código: parte do dashboard ainda tem acesso a dados dentro da página, strings hardcoded e cache artesanal. E publica o plano de fases em vez de fingir que as regras já valem, com fase 0 sendo o que está de fato ativo no lint hoje.

Isso muda o comportamento de quem lê, humano ou agente. Um documento que afirma cem por cento de conformidade faz o agente copiar o legado achando que é o padrão, enquanto um que nomeia a dívida faz ele perguntar antes.

Diagrama: quatro fases numeradas de 0 a 3, com as regras ativas preenchidas em coral a cada fase

Cada regra registra o incidente que a gerou

Rastreando o histórico do Gaveta, cada mudança normativa aparece logo depois de um incidente:

  • 28 de julho. A Apple rejeitou a versão 1.5.2 por metadados imprecisos. A causa foi uma linha da nota de novidades que começava com "Android:", porque o mesmo arquivo alimenta as duas lojas. A regra que entrou é um comando verificável: antes de commitar, um grep procurando por nomes de plataforma tem que voltar vazio. E carrega o motivo: custou um ciclo de review inteiro por causa de uma palavra.
  • 31 de agosto. Um link de teste no splash subiu visível em produção. Virou item de checklist de release, com a condição explícita de confirmar que ele está dentro do bloco de desenvolvimento antes de buildar.
  • 22 de julho. Os 31 bottom sheets viraram um primitive só, num commit de 41 arquivos. A regra de lint que proíbe modal à mão nasceu no mesmo dia. Sem ela, o sheet número 32 apareceria em duas semanas.

Nos três casos a regra foi escrita depois da dor, e o texto carrega a dor junto. Uma regra sem história vira burocracia e some no primeiro momento de pressa.

Diagrama: uma linha do tempo com três marcadores coral, um por incidente que gerou uma regra

Como a Otther aplica isso

Quando a Otther entra num produto que já roda com time e agente, a primeira coisa que medimos é quanto tempo alguém leva para tomar a decisão certa sem perguntar a outra pessoa. Esse número prevê velocidade melhor que cobertura de teste ou contagem de dívida.

O que aplicamos, em ordem:

  1. Mapear as fronteiras antes dos padrões. Quem decide o quê, e onde essa decisão mora. Sem isso, o documento vira depósito de tudo que ninguém soube onde colocar.
  2. Escrever o contrato curto e o receituário longo. O contrato precisa caber na cabeça. O receituário pode ser extenso, porque é material de consulta.
  3. Amarrar cada regra normativa a uma linha de lint. As que não têm, marcar como aspiracional e publicar o plano de fases, com a fase atual nomeada.
  4. Amarrar manutenção ao fluxo, não à boa vontade. "Evento novo entra neste documento no mesmo PR que instrumenta" funciona. "Manter o documento atualizado" não funciona.

O mesmo raciocínio vale para o handoff entre design e engenharia, e explica por que tanto handoff falha: quando o documento de spec repete os valores do design system em vez de citá-lo, ele nasce desatualizado. Handoff que aponta para a fonte sobrevive ao primeiro ajuste de token.

Diagrama: quatro colunas iguais, cada uma encimada por uma barra coral

O custo do método

Dos 726 commits do Gaveta, 146 tocam algum arquivo markdown, e 31 deles não tocam em mais nada. É um em cada cinco commits gasto em texto que não compila.

Os documentos também acumulam dívida. Um diagrama na raiz ainda diz "33 migrations" quando a pasta tem 105, porque o número entrou em maio dentro de um bloco que ninguém revisita. E as frases sobre "CI bloqueia merge" descrevem intenção, porque o lint roda na máquina do desenvolvedor, não num runner. Os dois casos estão em pontos que ficaram sem enforcement.

Em troca, você abre o repositório depois de duas semanas fora, num pacote que não tocou, e entrega uma feature sem reconstruir o modelo mental. O agente parte da mesma base. Cinco meses, 38 releases, e a camada de arquitetura continua com cache centralizado, rotas de seis linhas e View sem acesso a dados, porque o lint não deixa passar.

Diagrama: 726 pontos, dos quais 146 em coral claro e 31 em coral sólido na base

Como aplicar isso no seu projeto

O erro mais comum ao copiar essa estrutura é escrever tudo no primeiro dia. Cookbook sem código para apontar é enfeite, e tabela de camadas inventada antes da primeira feature descreve um produto que ainda não existe. A ordem abaixo é a que sobreviveu no Gaveta.

QuandoO que entraO que ainda não entra
Antes da primeira featureDocumento de fronteiras, uma página: quais pacotes existem, quem decide o quê, e a regra de soberaniaTabela de camadas, cookbook, FAQ
Depois da primeira featureA tabela de camadas, extraída do código que você acabou de escreverReceita para caso que ainda não apareceu
Na segunda ocorrência de um padrãoO padrão vira entrada do cookbook, com link para o exemplo real no repositórioPadrão que apareceu uma vez só
Sempre que escrever uma regraUma linha correspondente na configuração de lintRegra sem enforcement, a menos que declarada aspiracional
Depois de cada incidenteUma linha de regra com a data e o que aquilo custouJustificativa abstrata, sem o incidente junto

Duas dessas linhas carregam quase todo o peso.

A regra das duas ocorrências. Um padrão só entra no cookbook depois de aparecer em dois lugares do código. Antes disso, você está documentando uma coincidência e criando trabalho de manutenção para uma regra que talvez nunca se confirme.

A regra do lint ou da fase. Toda regra normativa precisa de uma linha correspondente no lint. As que não têm se declaram aspiracionais e publicam o plano de fases, com a fase atual nomeada. Regra que não tem nem uma coisa nem outra é a que some no terceiro PR.

O esqueleto do documento raiz

# <projeto> — arquitetura entre pacotes

Este documento descreve como os pacotes se conectam.
Ele não ensina a codar dentro de nenhum deles.

**Regra de soberania (inviolável):** como codar dentro de um pacote é
ditado pelo documento daquele pacote. Este arquivo nunca sobrepõe
essas regras. Ele cobre fronteiras, contratos e fluxo entre pacotes.

## Roteamento

| Onde você vai mexer | Documento soberano     |
|---------------------|------------------------|
| `<pacote-a>/**`     | `<pacote-a>/CLAUDE.md` |
| `<pacote-b>/**`     | `<pacote-b>/CLAUDE.md` |

## Estado atual

<Nomeie a dívida aqui: onde o código ainda não obedece ao contrato,
desde quando, e o que precisa acontecer para ele obedecer.>

## Se não couber

Se a sua mudança não cabe em nenhuma receita do documento soberano,
pare e pergunte. Não improvise arquitetura.

O documento de cada pacote segue a divisão em duas metades: contrato curto com o que não se pode fazer, receituário longo com como fazer o que você precisa agora, apontando para exemplos reais em vez de descrevê-los.

O que não fazer

Não escreva o cookbook antes de ter código para apontar. Não afirme conformidade que o repositório não tem, porque documento que finge cem por cento faz o agente tratar o legado como padrão a copiar. E não deixe a manutenção na mão da boa vontade: amarre ao fluxo, no formato "evento novo entra neste documento no mesmo PR que instrumenta".

Conclusão

Vibe coding quebra quando o conhecimento que justifica cada decisão do projeto mora na cabeça de alguém, fora do alcance de quem precisa decidir na hora, agente ou pessoa. Markdown, ADR, design system ou handbook é detalhe de formato. Na prática, a pergunta é: quando a próxima sessão precisar decidir, ela vai encontrar a resposta no lugar onde vai procurar?

Se o seu time está ganhando velocidade com IA e perdendo consistência de produto na mesma proporção, é esse o problema. Fale com a Otther sobre como estruturar a camada de contexto do seu produto.