Regression
Context
Something that used to work has stopped working. Not because the requirements changed, but because someone changed the code and accidentally broke an unrelated behavior. This is a tactical pattern that names one of the most common and frustrating categories of software defect.
Regressions are directly addressed by Tests, and preventing them is a primary motivation for Test-Driven Development and Refactoring discipline.
Problem
Software is interconnected. A change to the payment module might break the email notification system. A performance optimization in the database layer might subtly alter query results. An updated dependency might change behavior in ways the changelog didn’t mention. The larger and older the codebase, the more likely that any change will break something unexpected. How do you detect when a change breaks existing behavior?
Forces
- Every code change risks breaking something that currently works.
- The connection between a change and its side effects is often not obvious.
- Manual testing after every change is too slow and unreliable.
- Users experience regressions as a loss of trust: “it worked yesterday.”
- Finding and fixing a regression after release is far more expensive than catching it before.
Solution
Treat previously working behavior as something that must be actively protected. The primary defense is an automated test suite that runs after every change. When a test that previously passed now fails, you’ve detected a regression, and you know exactly which change caused it, because the tests ran right after the change was made.
The term “regression test” sometimes refers to the entire test suite run in this protective mode, and sometimes to specific tests written after a bug was found, to ensure that particular bug never returns. Both uses matter. The first provides broad coverage; the second plugs specific holes.
When a regression is found in production, the fix should always include a new test that would have caught it. This turns every bug into a permanent defense against that class of failure.
The most important property of regression detection is speed. If you find out about a regression five minutes after introducing it, the fix is trivial; you know exactly what you just changed. If you find out five weeks later, you’re debugging a mystery.
How It Plays Out
A team ships a new search feature. Two days later, users report that the shopping cart is dropping items. Investigation reveals that the search feature introduced a session-handling change that conflicted with the cart’s session logic. The team fixes the bug and adds a test: “after adding three items to the cart, the cart contains three items.” This test will catch any future change that accidentally breaks cart behavior.
In agentic workflows, regressions are the primary risk of AI-generated code changes. An agent modifying one part of the system may not understand the implicit dependencies elsewhere. This is why running the full test suite after every agent-generated change is non-negotiable. The test suite is the safety net that catches what the agent (and the human) didn’t foresee.
A regression found by a user is a failure of process, not just of code. If your tests didn’t catch it, ask why, and add the missing test.
“A user reported that adding items to the cart sometimes drops existing items. Write a regression test that reproduces this: add three items, verify all three are present. Then find and fix the bug.”
Consequences
Strong regression detection gives teams the confidence to change code. Without it, codebases become fragile: developers are afraid to touch anything because they can’t predict what will break. With it, change becomes routine and safe.
The cost is the test suite itself. Maintaining tests takes ongoing effort. Tests must be updated when behavior intentionally changes, or they become obstacles. The key insight is that the cost of maintaining tests is almost always lower than the cost of regressions reaching users.
Related Patterns
- Detected by: Test — automated tests are the primary regression defense.
- Prevented by: Refactor — disciplined refactoring with tests avoids regressions.
- Prevented by: Red/Green TDD — building tests alongside code catches regressions at introduction.
- Related to: Silent Failure — the worst regressions are the ones nobody notices.
- Related to: Invariant — violated invariants are a common form of regression.
- Detected by: Harness — the harness runs the full suite to catch regressions.
- Relates to: Performance Envelope — performance regressions push the system toward the edge of its envelope.
- Contrasts with: Test-Driven Development — TDD prevents regressions; regression testing detects them after the fact.
- Related: Technical Debt – debt-heavy code breaks more often when changed.