Back to all posts

1,3 milhão de linhas, 630 PRs por semana: como mantemos saudável uma base de código vibe-coded

1,3 milhão de linhas, 630 PRs por semana: como mantemos saudável uma base de código vibe-coded

Em julho de 2026, Robert C. Martin escreveu que não lê mais o código produzido pelos seus agentes de programação.

A afirmação chamou muita atenção. O restante do post importa mais para entender como ele trabalha. Martin cerca os agentes de testes unitários, testes de aceitação em Gherkin, procedimentos de QA, testes de mutação, verificações de cobertura e métricas de qualidade. O código conquista a confiança dele ao atravessar esse sistema.

Alguns meses antes ele já havia feito uma observação parecida. Em vez de inspecionar a implementação, ele olha cobertura de testes, estrutura de dependências, complexidade ciclomática, tamanho dos módulos e resultados de testes de mutação. Quando isso foi interpretado como abrir mão da revisão por completo, ele esclareceu: "Reviso bastante — só não o código."

Os comentários dele descrevem um problema prático do desenvolvimento de software com forte presença de IA. Agentes de programação produzem mudanças mais rápido do que as pessoas conseguem ler. Quando o volume de código gerado ultrapassa a capacidade de revisão do time, a inspeção linha a linha não pode continuar sendo a única fonte de confiança.

Na vm0 lidamos com o mesmo problema há oito meses. A maior parte da implementação no nosso repositório é escrita por agentes — vibe coding, no termo corrente — enquanto seis engenheiros continuam responsáveis pelo que esse código faz em produção.

A versão curta

A vm0 é uma base de código vibe-coded: agentes escrevem a maior parte da implementação e seis engenheiros respondem pelo que ela faz em produção. Aos oito meses o repositório tem 1.329.170 linhas e 630 pull requests foram integrados em uma única semana. Cinco mecanismos sustentam a carga de qualidade que a revisão linha a linha já não sustenta sozinha.

  • Ambientes convergentes. Engenheiros e agentes trabalham no mesmo dev container, e cada pull request pode ter sua própria branch de banco de dados. Um comando que funcionou para um agente funciona para uma pessoa.
  • Restrições executáveis. TypeScript estrito, cerca de 135 módulos de contrato de API tipados, Oxlint, ESLint e regras de arquitetura, com CI que não aceita avisos. Uma regra verificável mecanicamente não fica a cargo da revisão.
  • Testes nas fronteiras. Um troféu de testes em vez de uma pirâmide: testes de integração pelas fronteiras públicas dos módulos, PostgreSQL e migrações reais, mocks apenas em sistemas externos. O código de teste é cerca de 36 por cento do repositório.
  • Ciclos de feedback completos. Os agentes sobem a aplicação e o banco, rodam migrações e conduzem um navegador real; depois reportam os comandos executados, o caminho testado e capturas de tela. O pull request mediano é integrado em cerca de 53 minutos.
  • Limpeza contínua. O Knip remove código morto de forma determinística, workflows diários tiram o AI slop que nenhum linter sabe nomear, e um padrão que se repete o bastante vira regra de lint ou restrição de tipo.

O que vibe coding e agentic coding realmente significam

Vibe coding é pedir código a um LLM, executar o que ele produz, pedir mudanças e não prestar atenção ao código gerado. A definição de Martin Fowler é deliberadamente estreita, e ele destaca a ideia de "esquecer que o código sequer existe". Serve para protótipos, software descartável e ferramentas pequenas de consequências limitadas.

Agentic coding, o que Fowler chama de programação agêntica, é o que os times fazem quando pretendem manter o resultado. Um agente lê o repositório, edita arquivos, roda testes e trabalha sozinho por um período longo, enquanto as pessoas seguem donas da estrutura e do comportamento e revisam as evidências de cada execução: resultados de testes, sinais de qualidade, previews e comportamento em produção.

A diferença não está em quanto código o modelo escreve, e sim em para onde vai a atenção humana.

Vibe codingAgentic coding
Autonomia do agentePrompt, executar, novo promptLê o repositório, edita, testa, itera
Quem lê o códigoNinguémAs pessoas leem o que carrega risco
O que é verificadoSe o resultado parece certoTipos, contratos, testes, previews, sinais de produção
Melhor encaixeProtótipos e ferramentas descartáveisSoftware com horizonte de manutenção
Falha típicaCódigo que ninguém entendeVerificação fraca demais para o volume

Agentes que editam um repositório são apenas um ramo de uma mudança mais ampla; tratamos o lado de quem não programa separadamente.

Costumamos chamar a vm0 de projeto vibe-coded porque a expressão virou o atalho comum para software escrito principalmente por IA. Nos termos de Fowler, está mais perto de agentic coding. Nossos engenheiros digitam menos implementação do que antes, e seguem donos da arquitetura, do custo de manutenção e do comportamento em produção.

À medida que a geração de código ficou mais rápida, mais dessa responsabilidade migrou para o ambiente de desenvolvimento, o sistema de tipos, a suíte de testes e workflows de manutenção automatizados.

Oito meses de crescimento em uma base de código vibe-coded

O repositório da vm0 foi criado em novembro de 2025. Na data do nosso relatório semanal de escala de engenharia mais recente, ele tinha cerca de oito meses e meio.

O repositório continha:

MétricaTotal
Linhas lógicas não vazias1.329.170
Linhas de produção850.913
Linhas de teste478.257
Arquivos-fonte5.549
Arquivos de teste1.327
Pacotes44
Commits na branch principal14.005

Esses números vêm do nosso relatório semanal de escala de engenharia da semana de 20 a 26 de julho de 2026, medidos diretamente no monorepo.

O código de teste representava cerca de 36 por cento da base de código medida. É uma fração do volume de código, não um número de cobertura, mas dá uma ideia de quanta implementação se acumulou em torno da verificação.

Ao longo de três semanas cheias recentes, seis engenheiros fizeram 541, 640 e 631 commits, respectivamente. Na semana de 20 a 26 de julho, o GitHub registrou 630 pull requests mescladas. Seis engenheiros assinaram 556 delas, e a automação de release respondeu pelas outras 74.

O tempo mediano entre abrir uma pull request e mesclá-la foi de cerca de 53 minutos. O percentil 90 ficou em aproximadamente 7,4 horas.

Nesse ritmo de mudança, a revisão humana segue útil, mas não consegue sustentar todo o sistema de qualidade. Precisamos de sinais independentes em vários pontos do ciclo de vida de uma mudança.

Nossa abordagem atual tem cinco temas recorrentes: ambientes convergentes, restrições executáveis, testes focados em fronteiras, ciclos de feedback completos e limpeza contínua.

Convergir o ambiente de desenvolvimento com dev containers

Muitas falhas difíceis de reproduzir vêm de estado que nunca aparece em uma pull request.

Uma pessoa desenvolvedora pode ter uma variável de ambiente não documentada, uma ferramenta instalada globalmente, um arquivo de configuração antigo ou um banco de dados rodando há meses. As pessoas se acostumam com esses detalhes. Os agentes normalmente não conseguem enxergá-los.

Aos poucos deixamos de tratar a máquina host como ambiente de desenvolvimento padrão. Engenheiros e agentes trabalham dentro de um dev container. A imagem de desenvolvimento inclui o toolchain do projeto, PostgreSQL, pgvector, Chromium e utilitários de automação de navegador.

A CI roda em imagens de toolchain versionadas, construídas a partir da mesma família de Dockerfile multiestágio. As imagens de desenvolvimento e de CI servem a propósitos diferentes, e produção tem seu próprio formato de deploy. A propriedade útil é a convergência: versões de ferramentas, dependências e premissas de runtime são explícitas e versionadas.

Isso nos dá uma expectativa direta. Comandos que um agente executa devem ser reproduzíveis por um engenheiro no mesmo container de desenvolvimento. Verificações que passam durante o desenvolvimento devem rodar na CI sob um toolchain bem próximo.

Aplicamos ideia semelhante aos dados. Cada preview de pull request pode ter sua própria branch de banco e rodar migrações e seeds reais. Um agente pode criar dados, alterá-los e repetir testes destrutivos sem tomar emprestado o banco de uma pessoa desenvolvedora nem herdar estado de outra pull request.

Mudanças de ambiente trafegam pelo repositório. Atualizações de ferramentas, versões de navegador e extensões de banco são revisadas e propagadas como código de aplicação.

Tornar os padrões de engenharia executáveis em tipos e regras de lint

Padrões escritos ajudam as pessoas a entender decisões de design. Fazem bem menos para garantir que toda mudança siga essas decisões, especialmente durante sessões longas de agentes.

Colocamos regras estáveis no sistema de tipos, nos linters e na CI sempre que a regra puder ser expressa de forma confiável.

A vm0 usa configurações estritas de TypeScript, incluindo strict e noUncheckedIndexedAccess. Alguns pacotes habilitam verificações adicionais para valores não usados e retornos implícitos.

A API usa uma camada de contratos REST tipada e schema-first, construída com a maquinaria de tipos do tRPC e schemas Zod. O Drizzle conecta o acesso ao banco aos tipos do TypeScript. Na data desta revisão, o repositório tinha cerca de 135 módulos de contrato.

Essas escolhas não pegam decisões de produto equivocadas. Mas expõem cedo o desvio de interfaces. Quando um campo ou uma resposta muda, os chamadores relacionados tendem a falhar já na análise estática. O erro resultante costuma ser específico o bastante para um agente aproveitar na iteração seguinte.

O linting cobre um segundo grupo de regras. A plataforma roda Oxlint, verificações cientes de tipos e ESLint, além de regras arquiteturais específicas do projeto. A CI não aceita warnings. Warnings que podem permanecer indefinidamente viram ruído de fundo, e ruído de fundo é fácil de ignorar tanto para pessoas quanto para agentes.

Quando um problema se repete, avaliamos onde a verificação pertence:

  1. O sistema de tipos consegue expressá-la?
  2. Um linter ou ferramenta estrutural consegue detectá-la com precisão?
  3. Ela exige julgamento semântico ao longo do repositório?

As duas primeiras dão feedback rápido e determinístico em toda mudança. O terceiro grupo tratamos com os fluxos recorrentes descritos adiante.

Restringir padrões propensos a erro no código gerado por IA

Alguns recursos da linguagem têm usos legítimos e também aparecem com frequência em código frágil gerado por agentes.

try/catch pode borrar uma fronteira de erro. Quando um agente encontra uma falha, adicionar um bloco catch e um fallback é um jeito fácil de manter o caminho atual funcionando enquanto se perde o erro original. Misturar .then() e .catch() de Promise com async/await espalha o fluxo de controle por vários estilos.

O useEffect do React cria problema parecido no gerenciamento de estado. Costuma ser usado para copiar estado, sincronizar duas fontes de verdade ou codificar uma dependência de ordem difícil de perceber pelo modelo de dados.

A plataforma web principal restringe esses padrões por padrão. Uma exceção justificada pode permanecer, com uma explicação explícita ao lado.

O código de produção da plataforma web principal atualmente não contém nenhum useEffect. Usamos ccstate e outros padrões sem efeitos para modelar dependências e efeitos colaterais de forma mais explícita. Ainda existe um pequeno número de chamadas useEffect em produção em outros pontos do monorepo, principalmente em código de UI compartilhada e desktop, então o escopo da afirmação importa.

Essas regras nasceram de falhas recorrentes neste repositório. Quando um padrão cria repetidamente o mesmo problema de manutenção, nós o movemos de orientação de revisão para restrição executável.

Testar nas fronteiras dos módulos: um troféu de testes, não uma pirâmide

A vm0 não segue uma pirâmide de testes convencional. Nosso guia de testes descreve um troféu de testes: análise estática na base, testes de integração como camada dominante e um número pequeno de testes ponta a ponta para jornadas críticas.

Testes unitários são relativamente incomuns. Nós os usamos seletivamente para lógica sensível à segurança, algoritmos e máquinas de estado. A maior parte do comportamento de negócio é testada pela fronteira pública de um módulo.

Testes de API chamam a aplicação real por meio de seus contratos. Os dados de teste são preparados via endpoints de produção quando é prático, e as asserções são feitas sobre comportamento público. Os testes evitam alcançar diretamente um serviço interno ou modificar tabelas do banco, porque isso os acopla à implementação atual.

A infraestrutura interna permanece real onde o custo é razoável:

  • PostgreSQL e pgvector
  • migrações de banco de dados
  • o sistema de arquivos
  • serviços internos
  • mocks nas fronteiras com sistemas externos

Esses testes de integração rodam mais devagar que testes unitários muito isolados e exigem um ambiente mais completo. Em troca, reduzem a distância entre "o teste passou" e "a aplicação consegue executar esta operação com um banco real".

Testes de fronteira deixam espaço para grandes refatorações internas. Um agente pode reorganizar módulos, dividir um serviço ou trocar a camada de acesso a dados enquanto o comportamento público segue protegido.

Uncle Bob prefere outra mistura, com uso intenso de testes unitários, Gherkin e testes de mutação. O princípio compartilhado é a verificação independente. O processo que gerou o código não deveria ser a única fonte afirmando que aquele código funciona.

Dar aos agentes de código um ciclo de feedback completo

Nossas primeiras execuções de agentes de programação terminavam com um relatório familiar: o código foi alterado e o TypeScript passa; por favor, suba a aplicação e verifique a página.

Isso deixa metade do ciclo de desenvolvimento com a pessoa engenheira. Alguém ainda precisa preparar um banco, subir os serviços, abrir um navegador, criar dados, observar a falha e descrevê-la de volta ao agente.

Os agentes agora têm ambiente de desenvolvimento suficiente para fazer mais desse trabalho. Conseguem subir aplicação e banco, rodar migrações, criar dados de teste, visitar um preview de pull request e realizar interações reais no navegador.

A verificação no navegador pega uma classe de problemas que checagens de tipo e testes de API cobrem mal: navegação quebrada, estados de carregamento que nunca terminam, erros de permissão que só aparecem em um fluxo completo e regressões visuais.

Ao fim de uma execução, o agente relata os comandos que rodou, o caminho que testou e o que observou. Para mudanças de interface, pode incluir capturas de tela. Uma pessoa engenheira consegue inspecionar essa evidência antes de decidir se abre o preview pessoalmente.

Uma captura de tela não é um teste e não prova a ausência de outras falhas. Ela reduz o custo de reconstruir o contexto do agente. Para uma pequena mudança de interface, passos reproduzíveis e uma captura final são muito mais úteis do que uma mensagem dizendo que a mudança "deve estar corrigida".

A qualidade do trabalho de um agente depende muito do feedback que ele consegue obter sem esperar por uma pessoa.

Manter as branches curtas com trunk-based development

Geração rápida de código pode produzir um estoque grande de branches.

Branches de funcionalidade de vida longa acumulam conflitos de merge, trabalho duplicado e contexto desatualizado. Conforme as pull requests crescem, a revisão fica mais difícil e mais lenta. Usamos desenvolvimento baseado em tronco para manter branches curtas e integrar continuamente em torno da main.

As regras da nossa branch principal exigem:

  • pull requests para mudanças
  • histórico linear e merges com squash
  • uma merge queue
  • verificações de Turbo, Rust e segurança
  • nenhum bypass rotineiro dos gates obrigatórios

A automação segue o mesmo caminho. Um agente pode criar uma pull request, e algumas tarefas de manutenção de baixo risco podem habilitar merge automático, mas a mudança ainda passa pela CI e pela merge queue.

O tamanho e a vida útil das pull requests ajudam a explicar como 630 delas puderam ser mescladas em uma semana. Uma mudança pequena carrega menos contexto, é mais fácil de verificar e tem menos chance de conflitar com trabalho de produto ou com outro reparo automatizado.

Knip como coleta de lixo do repositório: remover código morto

Geração de código naturalmente adiciona arquivos e abstrações. Apagar costuma exigir um prompt separado.

Depois de uma refatoração, arquivos antigos podem continuar no repositório. Remover uma funcionalidade pode deixar para trás exports, dependências e entry points. Esses resíduos raramente quebram testes, mas com o tempo tornam a base de código mais difícil de navegar.

Usamos o Knip para encontrar arquivos, exports, dependências e entry points sem uso. O TypeScript pode confirmar que o código é válido; o Knip pergunta se ele ainda participa do sistema.

Esse resíduo tem custo adicional para agentes de programação. O repositório é uma das suas fontes de contexto mais importantes. Um helper obsoleto ou uma implementação abandonada pode parecer um padrão aprovado para o próximo agente que o ler.

Remover código morto também melhora as entradas disponíveis para execuções futuras. O Knip resolve a parte determinística desse trabalho rápido o suficiente para virar uma verificação de qualidade regular.

Workflows recorrentes de limpeza de AI slop

Knip e ESLint têm limites claros. Muitas formas de degradação exigem contexto de projeto e julgamento semântico.

Usamos "AI slop" como rótulo prático para esse resíduo: fallbacks desnecessários, abstrações duplicadas, testes que contornam uma fronteira pública ou ramos defensivos para estados impossíveis. Cada caso pode parecer inofensivo. No agregado, deixam o repositório mais difícil de entender e oferecem maus exemplos a agentes futuros.

Vários fluxos recorrentes na vm0 varrem esses padrões.

Uma limpeza diária de AI slop procura resíduo novo e seleciona um conjunto pequeno de correções de alta confiança e baixo risco. Outros fluxos inspecionam testes de API que alcançam serviços internos, antipadrões de React e ccstate, e dívida técnica que pode ser tratada com segurança.

Cada fluxo mantém suas mudanças estreitas. Ele abre uma pull request e depois se apoia nas checagens de tipo, regras de lint, testes e merge queue habituais. Uma pull request configurada para merge automático ainda precisa passar pelos mesmos gates.

Fluxo automatizado de manutenção do repositório, da varredura à CI e ao merge, com reparo em caso de falha

Os fluxos não tentam zerar toda a dívida técnica em uma passada. Um lote diário pequeno é mais fácil de verificar e menos disruptivo do que uma grande limpeza a cada poucos meses.

Quando um fluxo recorrente encontra o mesmo padrão com frequência suficiente, consideramos mover a verificação para o ESLint, o Knip ou o sistema de tipos. O fluxo semântico funciona como um lugar para observar e refinar a regra antes de transformá-la em uma checagem determinística mais barata.

Esses workflows da vm0 recorrentes rodam em agenda.

Transformar testes instáveis em reparos automáticos

Outro grupo de fluxos parte das falhas do GitHub Actions.

Quando um teste falha na branch principal ou na merge queue, um fluxo lê os logs, procura evidências de flakiness e observa retentativas, tempos e fatores de ambiente. Se a evidência sustenta um reparo específico, ele atualiza o teste ou a implementação, abre uma pull request e roda todo o caminho de CI novamente.

Passar em uma retentativa não torna a falha original inofensiva. Times que dependem do botão de retentar vão perdendo a confiança em builds vermelhas. Quando isso acontece, checagens falhas viram mais uma forma de ruído de fundo.

Um fluxo de reparo automatizado transforma uma falha intermitente em uma mudança de código rastreável. Diagnóstico, patch e verificação continuam visíveis na pull request. Engenheiros podem inspecionar mudanças de maior risco, enquanto correções estreitas com evidência forte podem seguir pela merge queue.

Nosso sistema de qualidade tem hoje três camadas amplas:

EtapaMecanismosPreocupações típicas
Ao escreverTypeScript, contratos, Drizzle, ESLinterros de tipo, desvio de interfaces, padrões de código conhecidos
Antes do mergeKnip, testes de integração, bancos reais, previews, merge queuecódigo morto, comportamento de módulos, resultados completos em runtime
Depois do mergefluxos vm0 agendados e orientados a eventosAI slop, antipadrões semânticos, testes instáveis, desvio arquitetural

As camadas se alimentam mutuamente. Problemas encontrados pelos fluxos podem virar regras estáticas. Falhas encontradas na CI ou em produção podem virar testes e novas diretrizes de engenharia.

Manter agentes rodando continuamente tem custo, e escrevemos à parte sobre como reduzi-lo.

Para onde vai a atenção humana na revisão de código gerado por IA

Engenheiros da vm0 ainda leem código, especialmente em mudanças arquiteturais, trabalho sensível à segurança, pagamentos e migrações de dados. Não transformamos "nunca leia o código" em regra do time.

O que mudou foi a alocação da atenção. Ler código é um sinal entre contratos, fronteiras de teste, comportamento de previews, capturas de tela, métricas de qualidade e diagnósticos de fluxos.

Várias decisões ainda exigem julgamento experiente:

  • se o requisito está completo
  • onde as fronteiras de módulo devem ficar
  • quais falhas são recuperáveis
  • quanto impacto de negócio um defeito pode gerar
  • se um modelo de segurança é adequado
  • qual novo padrão de falha as regras atuais não cobrem

Engenheiros também mantêm o ambiente ao redor dos agentes. Quando um problema se repete, decidimos se adicionamos uma restrição de tipo, uma regra de lint, um teste ou um fluxo recorrente. O sistema de desenvolvimento virou, por si só, um artefato de engenharia importante.

Código legível continua importando. O próximo leitor pode ser um engenheiro ou outro agente. Código emaranhado consome mais contexto, amplia o escopo de mudanças futuras e torna a verificação menos confiável.

Uma mudança parecida aconteceu no design, onde o design-as-code levou as decisões visuais para o mesmo repositório e o mesmo caminho de revisão.

Segurança e as mudanças que não aceleram

Velocidade não se distribui por igual. A vm0 mantém uma lista curta de mudanças que andam no próprio ritmo: trabalho sensível a segurança, pagamentos, migrações de dados e decisões de arquitetura. Um engenheiro lê essas linha a linha, não importa quem ou o que as escreveu.

O risco de segurança do código gerado por IA, na nossa experiência, tem menos a ver com vulnerabilidades exóticas do que com código plausível que ninguém assume. Os controles que importam são os comuns, aplicados com constância:

  • Testes unitários, que de resto usamos pouco, são usados de forma deliberada para lógica sensível a segurança, algoritmos e máquinas de estado.
  • Os testes mockam apenas nas fronteiras de sistemas externos. A infraestrutura interna continua real, então uma mudança que quebra um contrato interno falha na CI e não em produção.
  • Os agentes trabalham dentro do dev container contra uma branch de banco por pull request, não na máquina de um desenvolvedor nem em um banco compartilhado.
  • A merge queue e os checks obrigatórios não têm desvio de rotina, nem para um pull request que um agente abriu e marcou para merge automático.
  • try/catch é restrito por padrão, para que uma falha não seja engolida por um fallback que um agente acrescentou só para manter o caminho atual funcionando.

O tratamento de credenciais é um problema de projeto à parte, com resposta própria: descrevemos o padrão de broker que mantém os tokens fora do alcance de um agente em um artigo dedicado.

Nada disso torna o código gerado seguro por si só. Reduz o conjunto de mudanças em que uma decisão humana é o único controle, e deixa esse conjunto explícito.

O que esses números não mostram

O relatório semanal demonstra escala do repositório e velocidade de entrega. Por si só, não demonstra confiabilidade em produção.

Avaliar o efeito sobre a qualidade em runtime exige disponibilidade, taxas de erro em produção, contagem de incidentes, taxa de falha por mudança, frequência de rollback e tempo médio de recuperação. Uma execução verde de CI descreve uma parte do processo de entrega.

Seguimos montando essas medidas de resultado. Práticas de engenharia explicam como um sistema administra risco; dados de produção mostram quão bem essa administração funciona.

Perguntas frequentes

O que é vibe coding? Vibe coding é pedir código a um LLM, executar o que ele devolve, pedir mudanças e não ler o código gerado. A definição de Fowler é estreita de propósito: a pessoa deve "esquecer que o código sequer existe". Combina com protótipos, software descartável e ferramentas de consequências limitadas.

O que é agentic coding? Agentic coding é uma forma de desenvolvimento assistido por IA de execução mais longa, em que um agente lê um repositório, edita arquivos, roda testes e itera sozinho por um período extenso. As pessoas continuam donas da arquitetura e do comportamento e revisam as evidências da execução em vez de cada linha escrita.

Qual é a diferença entre vibe coding e agentic coding? A atenção, não a autoria. No vibe coding o código nunca é inspecionado. No agentic coding o agente trabalha sozinho enquanto os engenheiros revisam as evidências ao redor: resultados de testes, sinais de qualidade, previews e comportamento em produção. A vm0 costuma ser descrita como vibe-coded; nos termos de Fowler, é agentic coding.

O que é AI slop? AI slop é o resíduo que o código gerado por IA deixa: fallbacks desnecessários, abstrações duplicadas, testes que contornam uma fronteira pública, ramos defensivos para estados impossíveis. Cada caso parece inofensivo. Somados, tornam o repositório mais difícil de entender e dão maus exemplos ao próximo agente.

Como revisar código gerado por IA com 630 pull requests por semana? Não linha a linha. Na vm0 a revisão humana vai onde é preciso julgamento: arquitetura, trabalho sensível a segurança, pagamentos, migrações de dados. O resto é sustentado por mecanismos: tipos estritos e contratos, testes de integração nas fronteiras dos módulos, bancos reais em previews por pull request, verificação em navegador, Knip e uma merge queue sem desvios de rotina.

Quais são as boas práticas de vibe coding em escala? Cinco se sustentaram na vm0 ao longo de oito meses: convergir o ambiente de desenvolvimento para que agentes e pessoas rodem os mesmos comandos; tornar os padrões executáveis em tipos, linters e CI em vez de documentos; testar nas fronteiras dos módulos contra infraestrutura real; dar aos agentes um ciclo de feedback completo, incluindo navegador; e limpar continuamente em vez de em mutirões esporádicos.

Quais são os riscos de segurança do código gerado por IA? O risco comum não é uma vulnerabilidade exótica, e sim código plausível que ninguém assume. A vm0 mantém em revisão humana o trabalho sensível a segurança, os pagamentos e as migrações de dados, usa testes unitários de forma deliberada para essa lógica, mantém a infraestrutura interna real nos testes, restringe padrões como try/catch que engolem falhas e não permite burlar os checks obrigatórios.

Código gerado por IA cria dívida técnica? Cria um tipo específico: código que ainda compila e passa nos testes, mas não participa mais do sistema, além de resíduo semântico que nenhum linter sabe nomear. O Knip remove a parte determinística. Workflows recorrentes cuidam do resto em pequenos lotes diários, e um padrão que se repete o bastante vira regra de lint ou restrição de tipo.

Quanto do código da vm0 é escrito por IA? A maior parte da implementação. Na semana de 20 a 26 de julho de 2026, seis engenheiros assinaram 556 dos 630 pull requests integrados e a automação de release respondeu pelo restante, e os agentes escreveram a maior parte do código dentro desses pull requests. O que os engenheiros mantêm é a arquitetura, as restrições e o comportamento em produção.

O que é um troféu de testes e por que não uma pirâmide? Um troféu de testes põe a análise estática na base, os testes de integração como camada dominante e poucos testes end-to-end no topo. A vm0 o usa porque testes escritos pela fronteira pública de um módulo continuam protegendo o comportamento enquanto um agente reorganiza a implementação por baixo, o que uma camada grande de testes unitários não faz.

Como encontrar código morto em um repositório escrito por IA? A geração de código adiciona arquivos e abstrações; apagar costuma exigir uma instrução separada. A vm0 roda o Knip como verificação regular para achar arquivos, exports, dependências e pontos de entrada sem uso. O TypeScript confirma que o código é válido; o Knip pergunta se ele ainda participa do sistema, que é a pergunta que importa para código morto.

Código vibe-coded é seguro em produção? Depende do que o verifica, não de quem o digitou. Os sinais que exigimos não mudaram: tipos e contratos, testes através de fronteiras públicas, infraestrutura real e um pipeline que precisa estar verde. Os números deste artigo descrevem escala de repositório e velocidade de entrega; disponibilidade, taxas de erro e change-failure rate respondem à pergunta de produção, e ainda estamos reunindo-as.

Oito meses depois

Oito meses é cedo demais para declarar um método definitivo. Modelos, ferramentas de agentes e o repositório continuam mudando, e nossas regras e fluxos mudam junto.

Uma virada já está clara. Conforme a geração de código ficou mais rápida, o ambiente, as restrições, os testes e o sistema de feedback assumiram mais da carga de qualidade. Engenheiros passam menos tempo digitando implementação e mais tempo definindo comportamento, projetando fronteiras e melhorando a verificação.

A base de código da vm0 vai continuar crescendo. O Knip remove o resíduo determinístico. Regras estáticas bloqueiam padrões de falha que já entendemos. Testes de integração protegem o comportamento dos módulos. Fluxos recorrentes tratam a degradação que ainda não conseguimos expressar mecanicamente.

Código ainda importa. Agora usamos mais evidência executável por máquina para decidir se uma mudança pertence à branch principal e se a sua implementação deve permanecer no repositório.

Stay in the loop

// Get the latest insights on AI teammates and collaboration.

SubscribeJoin Discord