Coloque um portão determinístico na frente do seu revisor mais esperto
A pega-defeito mais barata é um script burro que checa se duas branches mergeadas ainda sobem antes de qualquer julgamento.
Apollo Space Research
Apollo Space
Duas branches passaram no review. Cada uma, por si só, estava correta, testada, lida por um revisor cuidadoso, mergeada limpa. Então elas pousaram no mesmo lugar e a aplicação não subiu. Uma tinha renomeado uma chave de config; a outra ainda lia o nome antigo. Nenhum dos dois diffs estava errado. O par estava. E o revisor que teria pego isso estava ocupado lendo uma terceira branch, porque a coisa que deveria ter parado isso nunca rodou: um script burro demais para ter uma opinião, cujo único trabalho é mergear as duas branches numa cópia de rascunho e checar que o resultado ainda sobe.
Esse script custa quase nada para rodar. O bug que ele teria pego custou uma tarde.
A pega-defeito mais barata é um script burro que checa se duas branches mergeadas ainda sobem antes de qualquer julgamento.
Este post é sobre por que o check mais burro do seu pipeline pertence na frente do seu revisor mais esperto, humano ou agent, e por que times continuam invertendo essa ordem.
A versão ingênua: julgamento primeiro, integração quando der
O pipeline óbvio coloca o cérebro caro primeiro. Uma mudança chega. Um revisor habilidoso a lê, hoje em dia esse revisor pode ser um agent cuidadoso, mas o formato é o mesmo. Ele checa a lógica, questiona os casos de borda, aprova. A mudança mergeia. Em algum momento depois, talvez num build noturno, talvez quando a próxima pessoa puxa main, o sistema de fato tenta rodar com tudo combinado.
Isso funciona lindamente quando as mudanças chegam uma de cada vez. Desmorona no instante em que chegam em paralelo.
A falha tem um formato específico, e não é um revisor ruim. Cada revisor vê uma branch contra um main que ainda não contém a outra branch em andamento. Então cada um aprova uma mudança que é individualmente correta contra uma base que já está velha. O conflito não está dentro de nenhum dos diffs, ele mora no espaço entre eles, na chave renomeada, no import movido, na migração que assume uma coluna que a outra migração acabou de dropar. Nenhuma quantidade de revisar uma branch revela isso, porque a evidência não está naquela branch. Está na soma.
Uma mudança pode estar correta e ainda assim estar errada sobre aquilo em que está sendo mergeada.
Então o revisor esperto assina de boa fé, o merge passa, e o quebra-pau aparece downstream, num build que todo mundo parou de assistir, ou pior, na máquina de alguém uma hora depois quando ele puxa e nada sobe. O julgamento foi sólido. A coisa que o julgamento não consegue ver passou mesmo assim. E ela passou porque o único check que a teria pego foi agendado para rodar depois da decisão em vez de antes dela.
O check burro que tem que ir primeiro
Aqui está a inversão. Antes de qualquer revisor, pessoa ou agent, gastar um segundo de atenção numa mudança, rode o teste mais barato possível: pegue a mudança, combine-a com tudo o mais atualmente indo para main, e cheque que o resultado sobe, instala e passa por um smoke de trinta segundos. Sem julgamento. Sem gosto. Um pass/fail que um shell script consegue produzir.
Esse check não tem opinião sobre se o código é bom. Ele só responde uma pergunta: o mundo ainda começa com isto dentro dele?
A razão pela qual isto pertence primeiro é econômica, e é o argumento inteiro. Atenção de revisor, humana ou de modelo, é o recurso mais caro e mais interrompível do pipeline. Um check determinístico de boot é o mais barato. Quando você coloca o check barato primeiro, toda mudança que falha nele nunca chega ao caro. Você não pede ao seu melhor revisor que leia uma branch que nem consegue subir; você entrega a ele apenas branches que já se sustentam. O portão burro não está competindo com o revisor esperto. Ele está limpando o chão para que o revisor esperto nunca seja desperdiçado num problema que um script conseguia ver.
A pega-defeito mais barata é um script burro que checa se duas branches mergeadas ainda sobem antes de qualquer julgamento. Repare que tipo de defeito ele pega: não o bug lógico sutil, esse é o trabalho do revisor, mas o quebra-pau de integração, o que nenhuma revisão de branch única consegue ver, porque ele só existe uma vez que as branches são combinadas.
Por que “o revisor é esperto o bastante” é a armadilha
Há uma objeção tentadora, e vale dizê-la em voz alta porque é a razão pela qual times pulam o portão. Nosso revisor é bom. Um revisor cuidadoso o bastante, ou um agent capaz o bastante, pegaria o conflito.
Soa certo. Está errado de uma forma fácil de não notar.
Um revisor lê o que está à frente dele. O que está à frente dele é uma branch. Para pegar um quebra-pau de integração de duas branches só por julgamento, o revisor teria que segurar toda outra branch em andamento na cabeça simultaneamente, mergeá-las mentalmente, e simular o boot, para cada mudança, toda vez, enquanto também faz a revisão de fato. Isso não é um revisor mais esperto. É um revisor fazendo o trabalho de uma máquina determinística na mão, mal, sob carga. Quanto mais esperto o revisor, mais caro é desperdiçá-lo nisso.
Esta é a parte que as pessoas invertem. Tornar o revisor melhor não remove a necessidade do portão, ela aumenta o custo de não ter um. Um revisor mais capaz é mais caro de interromper, mais valioso de manter focado, mais desperdiçador de apontar para um problema que tem uma resposta determinística. Quanto melhor sua camada de julgamento fica, mais compensa nunca gastá-la em algo que um script resolve de graça.
Quanto mais esperto seu revisor, mais custa gastá-lo numa pergunta que um script consegue responder.
Então o portão não é uma muleta para revisão fraca. É o que deixa revisão forte se manter forte, não distraída, apontada apenas para as perguntas que de fato precisam de uma mente. Você não coloca o check burro primeiro porque seu revisor é burro. Você o coloca primeiro porque seu revisor é esperto, e esperto é caro demais para desperdiçar.
O que o portão precisa ser, para ser confiável
Um portão só vale a pena ser colocado primeiro se seu veredito é acreditado. No momento em que um portão verde consegue esconder um quebra-pau real, as pessoas começam a re-checar na mão, e o portão é peso morto. Então as restrições de design são estritas, e todas são sobre ser confiável em vez de ser esperto.
Ele tem que ser determinístico. Mesmos inputs, mesmo veredito, todo run. Um portão que é verde na terça e vermelho na quarta para o mesmo código ensina todo mundo a ignorá-lo. Instável é pior que ausente, porque ausente pelo menos não mente.
Ele tem que testar o resultado mergeado, não cada branch sozinha. O ponto inteiro é o espaço entre as branches. Um portão que checa branches isoladas reproduz exatamente o ponto cego que era para fechar.
Ele tem que ser rápido e burro de propósito. O portão roda em toda mudança, na frente de tudo, então tem que custar quase nada. No instante em que ele cria opiniões, estilo, arquitetura, “esta é a abordagem certa”, ele fica mais lento, fica instável, e começa a sobrepor o trabalho de verdade do revisor. Mantenha-o chato. Boot, instala, smoke, um caminho end-to-end real ou dois. Passa ou falha. Nada que ele diga exige um humano para interpretar.
Acerte esses três e o portão conquista a única coisa que o torna útil: um verde dele significa algo, então ninguém refaz seu trabalho. O revisor abaixo dele pode confiar que qualquer coisa que chega a ele já sobe, e gastar sua atenção inteira nas perguntas que um check de boot não consegue responder, isto está correto, isto está claro, esta é a coisa certa para construir. O portão burro e o revisor esperto não são redundantes. São uma divisão de trabalho, e a ordem é o design.
Como isto fica quando as mudanças vêm de agents
Nós nos importamos com isso agudamente porque na Apollo, muitas dessas mudanças paralelas vêm de agents, e agents tornam o problema de integração mais agudo, não mais suave.
Um único engenheiro cuidadoso abrindo um pull request por dia raramente colide consigo mesmo. Uma frota de agents trabalhando em paralelo, cada um em sua própria cópia isolada do repositório, cada um individualmente correto, cada um verde em seus próprios testes, colide constantemente, porque é isso que paralelismo é. Dois agents tocam a mesma config a partir de duas tarefas diferentes. Ambos estão certos. Ambos mergeiam. O par não sobe. O revisor-agent mais esperto do mundo, lendo uma branch, não consegue vê-lo, pela mesma razão que um humano não consegue: o conflito não está na branch, está na soma.
Então colocamos o portão determinístico primeiro, na frente de todo revisor na cadeia, humano ou agent. Antes de uma mudança chegar ao agent cujo trabalho é atacá-la, antes de chegar ao agent que guarda o padrão, antes de qualquer julgamento ser gasto, um script a mergeia com tudo o mais em andamento e checa que o resultado ainda começa. O julgamento caro, e julgamento de agent é caro, em tokens e em latência, nunca é gasto numa mudança que não consegue passar a barra mais barata que existe.
A pega-defeito mais barata é um script burro que checa se duas branches mergeadas ainda sobem antes de qualquer julgamento. Com uma frota, isso não é uma otimização. É a única coisa entre “paralelo” e “main constantemente quebrado”.
A virada: alguém tem que defender o check chato
É estranhamente difícil defender um check burro num time de gente esperta.
Todo instinto num time de engenharia forte empurra para o outro lado. O revisor é afiado, os agents são capazes, a tentação é sempre se apoiar no julgamento, dizer somos bons o bastante para pegar isso, e pular o portão chato que não tem opinião e adiciona um passo. O portão parece desconfiança. Parece a coisa de que você só precisa se suas pessoas não são boas. Então a pessoa que insiste em rodá-lo primeiro, que se recusa a deixar uma mudança chegar ao review até que um script confirme que o mundo mergeado sobe, pode parecer que não confia no time.
Eles estão fazendo o oposto. Estão protegendo o melhor recurso do time, sua atenção, de ser gasto num problema abaixo dele. A disciplina não é “não confiamos no revisor”. É “o revisor é bom demais para desperdiçar em algo que um script resolve de graça, então vamos garantir que um script o resolva primeiro”. Isso não é uma preferência de ferramental. É um respeito por onde o julgamento deve e não deve ir, e alguém em todo time tem que estar disposto a sustentar essa linha quando a sala está cheia de gente que preferiria só confiar em si mesma.
O check continua burro. O revisor continua afiado. E a ordem entre eles, barato-e-certo antes de caro-e-sábio, é uma escolha que uma pessoa faz, de propósito, contra a corrente dos instintos de um time esperto.
É essa a disciplina que construímos na Apollo Space: deixe os checks burros e certos rodarem primeiro para que o julgamento caro e cuidadoso nunca seja gasto num problema abaixo dele. Se seu melhor revisor fica sendo puxado de perguntas reais para debugar um main que duas branches corretas quietamente quebraram, a coisa que falta não é um revisor mais esperto, é o portãozinho chato que ninguém quis defender.
A Apollo cuida da operação repetitiva da sua empresa pro seu time não precisar.
Entre na lista de espera: acesso antecipado, preço de usuário fundador e um lugar na primeira fila enquanto a gente constrói.
Entrar na lista de esperaO imposto oculto dos agents em paralelo é um diamante de migrations
Seis agents escrevendo para um schema conflitam no banco de dados, não no código, e a CI morre em "multiple heads".
EngenhariaUm orchestrator que não sobrevive ao próprio crash não é um
Um crash que apaga o raciocínio do orchestrator perde a única coisa que você não consegue reconstruir.
EngenhariaToda página autenticada deve viver dentro do shell
Uma página que "funciona" mas derruba a navegação ao seu redor é uma regressão mesmo quando todos os testes estão verdes.