Tornando uma suite end-to-end instável determinística sem esconder falhas

Transformei uma suite end-to-end não determinística (25 a 37 falhas variando por execução) em 302 passando com zero falhas (8 skips pré-existentes deixados intocados, e o consumer rodando), sem adicionar um skip ou enfraquecer uma asserção, e, em review, provei empiricamente que três das quatro mudanças de produção propostas eram desnecessárias.

impacto302 testes passando, zero falhas, comportamento determinístico com o consumer rodando, a partir de uma suite que produzia de 25 a 37 falhas variáveis por execução. Os 8 skips na contagem final já existiam e foram deixados como estavam; não adicionei nenhum.O CI de backend voltou a ser confiável, então uma execução vermelha volta a significar um problema real.Protegi o código e as regras de negócio de produção de serem alterados para agradar um teste instável, refutando três das quatro mudanças propostas em review.Qualitativo: regressões voltam a ser capturadas pela suite em vez de escondidas por ela.
01

Contexto

Esta é minha evidência mais forte de rigor metodológico e de liderança técnica em code review. Qualquer um pode deixar uma suite instável verde afrouxando-a; a engenharia está em se recusar a fazer isso. As restrições que impus (nenhum skip novo, nenhuma asserção mais fraca, nenhum timeout maior) são o que forçou cada correção a ser uma causa raiz de verdade.

Isso também captura um momento do qual me orgulho: em vez de aceitar mudanças de produção que tinham sido feitas para acalmar os testes, provei empiricamente que a maioria delas não era necessária e protegi o código de produção de ser alterado para satisfazer um artefato de teste. Esse é o tipo de julgamento que quero que recrutadores vejam.

02

Problema

A suite end-to-end do serviço era não determinística: uma dada execução podia produzir de 25 a 37 falhas, e um conjunto diferente a cada vez. Como o resultado não era confiável, o CI não podia ser confiado. Uma execução vermelha podia significar uma regressão real ou nada, então regressões reais podiam se esconder no ruído. A suite exercitava dependências reais (um provedor de auth real, um banco de dados, um message broker local, e uma API sandbox externa), qualquer uma das quais podia contribuir para a instabilidade.

03

Restrições

A falha era estatística, não reproduzível sob demanda. Você não pode corrigir o que não consegue observar de forma confiável; o primeiro trabalho foi tornar a instabilidade mensurável.
Uma suite instável é um instrumento de medição quebrado. Toda "correção" é medida pela mesma suite não confiável, então tive que validar mudanças ao longo de muitas execuções, não uma.
As correções fáceis eram todas as erradas. Pular os testes ofensores, relaxar asserções, ou aumentar timeouts teria deixado a suite verde enquanto destruía seu valor. Eliminei essas opções de saída.
Parte do ruído vinha de dependências reais, então as causas raiz variavam de setup de teste a código de produção a configuração de ambiente.
04

Decisão

Tratei isso como um problema de diagnóstico com um protocolo rigoroso.

Reproduzir e caracterizar estatisticamente. Rodar a suite muitas vezes para medir quais testes falhavam e com que frequência, transformando "é instável" em uma lista ranqueada de ofensores concretos.
Categorizar por causa raiz, não por sintoma. As causas acabaram sendo variadas: um schema de banco de dados divergente, um bypass de auth retornando sucesso onde deveria retornar proibido, credenciais de teste inválidas, dados de seed ausentes, IDs hardcoded, e um spread acidental de campo em um handler de patch.
Corrigir cada causa na fonte, incrementalmente, rodando de novo para confirmar que cada correção reduzia falhas sem introduzir novas.
Validar ao longo de várias execuções em vez de uma execução verde, já que determinismo é uma propriedade que se demonstra estatisticamente.
Documentei as variáveis de ambiente de runtime das quais a suite dependia, para que seu comportamento parasse de ser folclore.
05

Trade-offs

Correções de causa raiz em vez de mascaramento (pular / enfraquecer / inflar). Mascarar é minutos de trabalho e destrói a razão de existir da suite; corrigir a causa raiz é mais lento mas deixa uma suite em que você pode confiar. Escolhi me restringir a evitar todo atalho.
Validar empiricamente as mudanças de produção propostas por um colega em vez de aceitá-las. Testar cada mudança custou tempo e uma conversa de review potencialmente desconfortável, mas mudar código de produção para satisfazer um teste instável teria sido o rabo abanando o cachorro. Preferi reverter mudanças que eu conseguia provar serem desnecessárias.
Corrigir o ambiente/setup em vez de confiar nas suposições da suite. Documentar variáveis de ambiente e corrigir desvios de seed/schema não é glamouroso mas remove classes inteiras de falha intermitente.
06

Impacto

302 testes passando, zero falhas, comportamento determinístico com o consumer rodando, a partir de uma suite que produzia de 25 a 37 falhas variáveis por execução. Os 8 skips na contagem final já existiam e foram deixados como estavam; não adicionei nenhum.
O CI de backend voltou a ser confiável, então uma execução vermelha volta a significar um problema real.
Protegi o código e as regras de negócio de produção de serem alterados para agradar um teste instável, refutando três das quatro mudanças propostas em review.
Qualitativo: regressões voltam a ser capturadas pela suite em vez de escondidas por ela.
07

Lições Aprendidas

Conhecimento de engenharia reutilizável que levo adiante disso:

Uma suite instável é um instrumento de medição que você precisa primeiro tornar confiável. Até que esteja, todo resultado, incluindo suas próprias correções, é não confiável. Caracterize antes de mudar.
Corrija a causa raiz; nunca enfraqueça o teste. Skips, asserções relaxadas e timeouts inflados convertem um sinal em silêncio.
Nunca mude código de produção para fazer um teste passar até ter provado que o código, não o teste, está errado, e conseguir provar isso empiricamente.
Determinismo se demonstra estatisticamente. Uma execução verde não prova nada sobre uma suite que era instável; muitas provam.
08

Evidência

De 25 a 37 falhas variáveis por execução para 302 aprovados, 8 pulados, 0 falhos com o consumer ativo, que foi o critério de aceite escrito no ticket antes do trabalho e o número em que fechou.
Restrições autoimpostas: nenhum skip *novo*, nenhuma asserção enfraquecida, nenhum timeout inflado. Os 8 skips já existiam; não adicionei a eles nem os reivindiquei como meus.
Verificado contra o tracker (2026-06-02 a 2026-06-23): as causas raiz registradas são setup/dados de teste mais uma race condition genuína em um teste de cancelamento (uma instabilidade 404-vs-202 onde a operação deixava seu estado pendente antes do cancelamento rodar). De-instabilizado semeando a operação diretamente em seu estado pendente através do repositório, contornando o message broker, o que tornou o teste determinístico em vez de meramente re-tentado.
Verificado: três das quatro mudanças de produção propostas revertidas como desnecessárias: um remapeamento de status de auth-guard, uma cláusula de ordenação dentro de uma subquery para o ciclo ativo, e um filtro no caminho de opções de localização, com os testes de opções de localização reescritos para verificar o comportamento que já existia. A única mudança mantida foi um lateral join resolvendo um identificador de versão na listagem de ciclos: revertê-la teria reintroduzido um bug conhecido reportado por clientes, então foi mantida, documentada e rastreada em seu próprio ticket em vez de contrabandeada junto com correções de teste.
Entregue como um único pull request no serviço de backend, o mais discutido do épico (25 comentários de review).
Fonte (privada): ticket do Jira no domínio de inspeção, 2026-06; base de conhecimento de carreira consolidada.