Building a Regression Suite That Doesn't Rot
Part 9 of the Software Testing Course: how a regression suite grows, why it decays, and how to keep it lean, trusted, and worth running.
Most of a mature test suite is regression tests — checks that lock in behaviour that used to work so a future change can't quietly break it. This part is about building that layer so it stays an asset for years instead of becoming the thing everyone dreads touching.
What a regression suite is
A regression suite is the set of tests you run to confirm that a change hasn't broken something that was already working. It's distinct from tests written to check new behaviour: a regression test's job is to notice when old behaviour changes.
It grows the natural way — every bug you fix leaves behind a test that would have caught it, and every new feature adds a few checks that then become regression checks the moment the feature ships. Left alone, this growth is the problem.
Why regression suites decay
- They only grow. Tests get added on every fix and feature; they almost never get removed. After two years you have thousands of them and no one knows which still matter.
- Coverage and value drift apart. A test written for a feature that's since been redesigned may still pass — while asserting something that no longer reflects any real requirement. It's green, and it's testing nothing.
- Flakiness compounds. A handful of unreliable tests train the team to rerun red builds instead of investigating them. Once "just re-run it" is the reflex, real failures get waved through alongside the noise. See false positives vs false negatives.
- Maintenance outpaces the team. A scripted end-to-end suite needs upkeep roughly proportional to how much the UI changes — not to how much value the tests deliver. A fast-moving product generates maintenance faster than a small team can absorb it, and the suite falls behind.
- Run time creeps up. Every added test costs a few more seconds. Eventually the suite takes 40 minutes, developers stop running it locally, and it only catches things after the push.
Keeping it lean
- Add deliberately. Every fixed bug gets a test — that coverage is cheap and proven to matter. Not every passing thought does. Ask whether a proposed test would catch a class of regression or just one trivial case.
- Prune quarterly. Set aside time to delete tests that only ever fail for maintenance reasons, or that no longer map to a real risk because the feature changed. A suite that shrinks while staying all-signal is healthier than one that grows while filling with noise.
- Kill flaky tests immediately. Move a flaky test out of the blocking path the day it's identified, then fix the root cause or delete it. A flaky test left in CI does more damage than the gap it was covering.
- Watch the run time. If the suite is slow enough that people skip it locally and rely on CI, it has stopped protecting the inner loop. Parallelise, or move the slow checks to a later stage.
- Give it an owner. A regression suite with no one responsible for its health degrades within a couple of quarters. Someone needs to be watching flakiness rates, run time, and whether escaped bugs are landing in "covered" areas.
The layered approach
The most durable regression coverage isn't one big end-to-end suite — it's layered, so the expensive part stays small:
- Unit and integration tests carry the logic-level regressions. Fast, cheap, precise, run on every change. Most of your regression count lives here.
- A small scripted end-to-end set carries the exact assertions that must never drift — "an expired card is declined," "the invoice total equals the sum of line items." Keep this deliberately short; it's the highest-maintenance layer.
- Autonomous coverage carries the behavioural regressions across the whole app. It re-explores the app on each run rather than replaying a fixed suite, so this layer doesn't accumulate maintenance — there's no suite to prune, no selectors to repair — and a regression in a flow nobody scripted still gets caught. See automated regression testing.
That structure keeps the part your team actively maintains small enough to keep healthy, while the breadth is carried by a layer that maintains itself.
The takeaway
A regression suite is where fixed bugs go to stay fixed. It decays by default: it only grows, coverage drifts from value, flakiness spreads, and maintenance outpaces the team. Counter that by adding deliberately, pruning on a schedule, killing flaky tests on sight, giving the suite an owner, and layering coverage so the high-maintenance scripted part stays small. Part 10 covers the environments and data those tests run against — and why the results are only as trustworthy as both.