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.

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.

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.

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 mexer | Documento soberano |
|---|---|
mobile/** | mobile/CLAUDE.md (1.259 linhas) |
web/** | web/CLAUDE.md (1.853 linhas) |
supabase/** | docs/SUPABASE.md |
| Produto e design | PRODUCT.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.

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":
| Camada | Pode | Não pode |
|---|---|---|
| Serviço | async puro, banco, RPC, fetch | Hooks React, JSX |
| Hook de dados | Chave de cache via registro central, invalidações | JSX de página |
| Hook de tela | Agregar hooks, navegação, derivar view model | Importar o cliente de banco direto |
| View | Props tipadas, JSX | Query, mutation, acesso a dados |
| Rota | const vm = useXScreen() e renderizar a View | Ló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.

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.

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.

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
grepprocurando 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.

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:
- 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.
- 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.
- 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.
- 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.

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.

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.
| Quando | O que entra | O que ainda não entra |
|---|---|---|
| Antes da primeira feature | Documento de fronteiras, uma página: quais pacotes existem, quem decide o quê, e a regra de soberania | Tabela de camadas, cookbook, FAQ |
| Depois da primeira feature | A tabela de camadas, extraída do código que você acabou de escrever | Receita para caso que ainda não apareceu |
| Na segunda ocorrência de um padrão | O padrão vira entrada do cookbook, com link para o exemplo real no repositório | Padrão que apareceu uma vez só |
| Sempre que escrever uma regra | Uma linha correspondente na configuração de lint | Regra sem enforcement, a menos que declarada aspiracional |
| Depois de cada incidente | Uma linha de regra com a data e o que aquilo custou | Justificativa 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.

