en
· 3 min de leitura

O que um build verde esconde

Um pipeline que passa prova que os testes que você escreveu passam na máquina em que rodaram. O resto é fé. Eis o que conferir.

auditinfra

Um build verde é a mentira mais reconfortante do software. Significa que os testes que existem passaram, no ambiente em que rodaram, contra os mocks que receberam, no código que foi commitado. Cada trecho dessa frase é um lugar em que a realidade pode divergir. Já auditei sistemas com histórico cem por cento verde e uma produção que caía toda semana. O build não estava errado. Estava respondendo a uma pergunta mais estreita do que qualquer um percebia.

Testes que não testam

Abra o arquivo de testes do módulo mais importante e leia as asserções. Não a quantidade, o conteúdo. Encontro com regularidade testes que afirmam que a função retornou alguma coisa, testes que chamam a função e não conferem nada, testes que mockam a própria coisa sob teste, e testes marcados como pulados na pressa e nunca desmarcados. Ferramentas de cobertura contam tudo isso como cobertura. Uma suíte com mil testes e nenhuma asserção significativa é um jeito lento de compilar.

Mocks exatamente nas fronteiras que falham

Testes mockam o banco, o provedor de pagamento, a fila, o relógio, o sistema de arquivos. Isso é razoável por velocidade. Também significa que a suíte nunca exercita as fronteiras, e as fronteiras são onde estão os bugs. O mock retorna o formato que o desenvolvedor esperava. A dependência real retorna um nulo num campo que o mock nunca teve, um timeout que o mock nunca produziu, uma duplicata que o mock nunca enviou. Verde significa que o código funciona contra um mundo idealizado. A pergunta é se alguma coisa, em algum lugar, o roda contra o mundo real.

Testes instáveis e o retry que os esconde

Procure na configuração do pipeline uma opção de repetir em caso de falha. Se o build roda de novo os testes que falharam até passarem, a suíte tem falhas intermitentes que alguém decidiu tolerar em vez de investigar. Falhas intermitentes são quase sempre reais: uma condição de corrida, um teste que depende da ordem, uma fixture compartilhada que vaza estado. O retry transforma um sinal em silêncio. Quando encontro um, desligo por um dia e observo o que cai. Nunca é nada.

O ambiente não é a produção

O build roda num container limpo com as dependências do lockfile e um banco vazio populado por fixtures. A produção roda em instâncias que estão no ar há semanas, contra um banco com anos de dados, atrás de um balanceador, com variáveis de ambiente definidas à mão num console e que não aparecem em arquivo nenhum. Migrações que funcionam num banco vazio falham num grande. Queries rápidas em fixtures são lentas em volumes reais. Feature flags com um padrão em teste têm outro em produção. O build não enxerga nada disso, e um resultado verde não diz nada a respeito.

A última é banal e eu a vi duas vezes num único ano: o build verde não era do código que foi implantado. Um cache de build serviu uma camada velha. Um artefato de uma execução anterior foi promovido por engano. O deploy puxou de um branch três commits atrás. Tudo no painel estava verde. O código em produção nunca tinha sido testado. A correção é fazer o artefato implantado carregar o hash do commit e conferir isso, mecanicamente, depois de todo deploy.

O que verde deveria significar

Nada disso é argumento contra CI. É argumento para saber o que o seu CI de fato confere. O exercício que faço com times é simples: escreva, em uma frase, o que um build verde prova. Depois escreva o que ele não prova. A segunda lista é sempre mais longa, e é a lista das coisas que precisam de outro tipo de verificação: um ambiente de homologação com volumes reais de dados, um smoke test depois do deploy, um teste de integração que bate na dependência real uma vez por dia, um exercício de restauração do backup, um humano lendo as asserções. Verde é necessário. Nunca foi suficiente.