Regressão é um bug que aparece após fazer alterações no código, mesmo que a mesma funcionalidade funcionasse corretamente antes. Regressão significa que uma nova alteração “quebrou” o que já havia sido escrito e testado anteriormente. É um dos problemas mais comuns e perigosos no desenvolvimento: ao corrigir um bug, o desenvolvedor pode, sem querer, quebrar outras três funcionalidades. De acordo com Capers Jones Software Engineering 2023, a densidade média de bugs de regressão é de 1–3 a cada 100 linhas de código alteradas. Vamos analisar as causas das regressões, métodos para detectá-las e estratégias de prevenção.
Principais Pontos
Regressão é uma situação em que uma funcionalidade que funcionava em uma versão anterior para de funcionar após a realização de alterações. A alteração pode ser qualquer coisa: correção de bug, adição de nova funcionalidade, refatoração, atualização de biblioteca ou até mesmo uma mudança de configuração. A regressão é a principal inimiga da estabilidade: cada alteração corre o risco de quebrar algo que já foi verificado e lançado.
O termo vem dos testes: teste de regressão é a reexecução de testes existentes após cada alteração para verificar se a funcionalidade antiga não foi quebrada. Se um teste que passava anteriormente falha, ocorreu uma regressão. Em um sentido mais amplo, regressão não é apenas uma falha de teste, mas qualquer degradação de comportamento percebida pelo usuário ou QA. De acordo com Tricentis State of Testing 2023, as regressões representam 35–45% de todos os bugs encontrados em produção.
O que distingue uma regressão de um bug comum é o contexto temporal: um bug pode ter existido desde sempre, enquanto uma regressão é sempre resultado de uma alteração. Essa é uma distinção importante porque encontrar a causa de uma regressão começa analisando o que mudou entre “funcionava” e “parou de funcionar”. Git bisect é a ferramenta padrão para encontrar o commit que causou a regressão.
Regressão local — uma alteração no módulo A quebra a funcionalidade no mesmo módulo A. Exemplo: um desenvolvedor reescreve uma função de ordenação e ela para de tratar corretamente um array vazio. A regressão local é a mais fácil de detectar e corrigir porque a causa e o efeito estão próximos.
Regressão remota — uma alteração no módulo A quebra a funcionalidade no módulo B, que não está diretamente conectado por código, mas está conectado por dados ou temporização. Exemplo: alterar o esquema do banco de dados no módulo “Usuários” quebra um relatório no módulo “Analytics” que usa a mesma tabela. As regressões remotas são as mais traiçoeiras: o desenvolvedor não suspeita que sua alteração afetará outro módulo.
Regressão de efeito colateral — uma alteração em um efeito colateral (logging, cache, envio de notificações) quebra o comportamento esperado. Exemplo: um desenvolvedor adiciona cache para acelerar o desempenho, mas devido ao cache obsoleto, os usuários veem dados desatualizados. Regressões de efeito colateral são difíceis de detectar com testes automatizados porque os efeitos colaterais geralmente não são cobertos por testes.
Regressão de desempenho — o código continua funcionando corretamente funcionalmente, mas está mais lento do que antes. Exemplo: um novo algoritmo de criptografia produz os mesmos resultados, mas o tempo de execução aumentou de 2 ms para 200 ms. Regressões de desempenho não são detectadas por testes unitários comuns — são necessários benchmarks e profiling.
| Tipo de Regressão | Exemplo | Método de Detecção |
|---|---|---|
| Local | Ordenação quebrada | Testes unitários |
| Remota | Alteração de esquema BD | Testes de integração |
| Efeito colateral | Cache obsoleto | Testes E2E |
| Desempenho | Resposta mais lenta | Benchmarks |
A primeira causa é o acoplamento do código. Quanto mais os módulos dependem uns dos outros, maior a probabilidade de que uma alteração em um cause uma regressão em outro. Antipadrões clássicos: God Object (um objeto que faz tudo), Shotgun Surgery (uma alteração em um lugar requer edições em uma dúzia de lugares), dependência circular. Reduzir o acoplamento é uma questão de arquitetura: princípios SOLID, Injeção de Dependência, arquitetura hexagonal.
A segunda causa é a falta de testes para a funcionalidade alterada. Se o código não estiver coberto por testes, o desenvolvedor só fica sabendo de uma regressão pelo QA ou pelos usuários. De acordo com o Google Testing Blog, projetos com cobertura de testes >75% têm 5 vezes menos regressões do que projetos com cobertura <25%. TDD (Test-Driven Development) garante que os testes sejam escritos antes do código, não “quando houver tempo”.
A terceira causa são os fatores humanos. O desenvolvedor não conhece a funcionalidade relacionada, não entende todas as dependências ou está simplesmente com pressa. A razão é a falta de compartilhamento de conhecimento sobre a base de código. Soluções: revisão de código com desenvolvedores de outros módulos, programação em par, documentação de arquitetura. O bus factor do projeto é inversamente proporcional ao número de decisões arquiteturais documentadas.
O teste de regressão é o processo de reexecutar os testes existentes após cada alteração para verificar se a funcionalidade antiga não foi quebrada. É a única maneira de garantir que uma nova alteração não prejudicou o código existente. Sem teste de regressão, cada lançamento é uma loteria: o desenvolvedor espera não ter quebrado nada, mas não pode confirmar.
O teste de regressão manual é a abordagem mais cara e menos eficaz. Conforme um projeto cresce, o número de cenários de teste de regressão cresce linearmente, enquanto o tempo de execução manual cresce exponencialmente. Após 2–3 anos de desenvolvimento, a regressão manual pode levar 2–3 semanas, tornando lançamentos frequentes impossíveis. A única solução é a automação.
O teste de regressão automatizado é dividido em níveis de acordo com a pirâmide de testes:
De acordo com o Google Testing Blog, a proporção ideal é 70% testes unitários, 20% testes de integração, 10% E2E. Desviar-se dessa proporção reduz a eficácia do teste de regressão: muitos testes E2E tornam o pipeline mais lento, poucos testes unitários deixam micro-bugs não detectados.
A primeira estratégia é a Regressão Completa. Todos os testes do projeto são executados. A abordagem mais confiável, mas também a mais lenta. Adequada para projetos pequenos (até 10.000 testes, tempo de execução <30 minutos). Para projetos grandes, a regressão completa pode levar horas, tornando o pipeline CI/CD impraticável.
A segunda estratégia é a Regressão Seletiva. Apenas os testes relacionados ao código alterado são executados. Um grafo de dependências do código é usado para determinar as relações. Ferramentas: Bazel (Google), Nx (JavaScript), sbt (Scala). A regressão seletiva economiza 60–80% do tempo de execução, mas requer uma construção precisa do grafo de dependências — erros levam a regressões não detectadas.
A terceira estratégia é a Regressão Priorizada. Todos os testes são classificados por prioridade: caminho crítico (cenários de usuário mais importantes), alto risco (código com histórico de bugs), código alterado (código afetado pela alteração). Os testes de maior prioridade são executados primeiro — se passarem, o desenvolvedor recebe feedback rápido. Execução com tempo limitado: os testes críticos são verificados em 10 minutos, o restante é executado em segundo plano.
O primeiro e mais importante passo é uma cultura de escrever testes. Cada alteração deve ser acompanhada por um teste que verifique se a alteração funciona e um teste que verifique se nada foi quebrado. TDD (Test-Driven Development) dá os melhores resultados: o desenvolvedor primeiro escreve um teste que falha, depois o código que o faz passar. Isso garante que o teste existe antes do código.
O segundo passo é um pipeline CI/CD com execução obrigatória de testes. Um pull request não pode ser mesclado até que todos os testes passem. Os testes não podem ser “pulados” devido à urgência — alterações urgentes passam por um conjunto de testes acelerado, mas obrigatório. De acordo com o Google DevOps Research, equipes com CI/CD obrigatório têm 3 vezes menos regressões em produção.
O terceiro passo é o monitoramento em produção. Mesmo os melhores testes não garantem 100% de proteção contra regressões. Ferramentas de observabilidade (Sentry, Datadog, New Relic) devem rastrear métricas-chave após cada implantação: taxa de erro, latência, throughput. A reversão automática quando os limites são excedidos é uma rede de segurança caso uma regressão chegue à produção.
O quarto passo é a revisão de código com mentalidade de regressão. O revisor deve perguntar: “Quais outros módulos podem quebrar com esta alteração?”. Não basta verificar se o código está correto — é preciso verificar se ele não prejudicará a funcionalidade relacionada. A lista de verificação de revisão de código deve incluir um item de “verificação de regressão em módulos relacionados”.
Perguntas Frequentes
Uma regressão é um bug que não existia antes. Um bug comum pode ter existido desde a criação da funcionalidade. Uma regressão está sempre ligada a uma alteração específica — isso permite usar git bisect para encontrar a causa.
Use git bisect: indique o commit onde tudo funcionava e o commit onde quebrou. O Git realiza uma busca binária no histórico e encontra o commit que causou a regressão. Isso funciona mesmo para projetos grandes com milhares de commits.
Não há um número definitivo, mas existe uma regra empírica: a cobertura dos fluxos de usuário principais deve ser 100%, a cobertura de todas as funções deve ser de pelo menos 70%. Qualidade importa mais que quantidade: um teste que verifica um caso extremo vale mais do que dez testes no caminho feliz.
Sim, e isso é chamado de regressão de infraestrutura. Uma atualização do SO, alteração de versão do banco de dados, atualização de certificado SSL ou mudança na configuração do servidor web podem quebrar código que funcionava. IaC (Infraestrutura como Código) e testes de infraestrutura (Test Kitchen, Terratest) ajudam a detectar essas regressões.
Comece com um fluxo de usuário crítico. Escreva um teste automatizado para o cenário mais importante (login, checkout). Mostre em uma demonstração como o teste detecta uma regressão. Quando a equipe ver o benefício, expanda gradualmente a cobertura.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também