Keep Your Regression Suite From Rotting
"When a bug ships, add a test so it never comes back." Every team agrees with this rule, yet a regression suite that only ever grows eventually rots. It takes 40 minutes to run, something fails on every run, and nobody looks when it does. At that point the suite is no longer a net that catches bugs; it is a ritual you wait through before merging.
This article focuses on one part of the testing phase: how to grow and prune a regression suite by writing down explicit rules for adding tests and for removing them.
Three Symptoms of a Rotting Suite
- Runtime creep: CI that took 5 minutes six months ago now takes 25, and developers stop running it locally
- Normalized flakiness: hitting "re-run" on the same flaky test becomes a daily habit
- Lost intent: nobody can explain what a test protects, so when it breaks it gets skipped instead of fixed
All three share one cause: there is a rule for adding tests but no rule for removing them, so the suite only grows.
Rules for Adding: Make "Add a Test" Specific
If the rule is just "add a test," you end up with several near-identical E2E tests. Decide these three things every time.
1. Write it at the lowest possible layer
Even if reproducing the bug required clicking through the UI, a bug in calculation logic can be covered by a unit test. Reserve E2E for failures in flows that span screens. Rule of thumb: the lowest layer that can call the faulty code directly.
2. Put the bug identifier in the test name
it("total never goes negative at 100% discount (Issue #482)", () => {
expect(calcTotal({ price: 1000, discountRate: 1.0 })).toBe(0);
});With the issue number in the name, anyone can find out what the test protects in one click, six months later. It is the cheapest way to avoid tests with lost intent.
3. Check whether an existing test can be extended
If a single boundary value was missing, one more row in an existing parameterized test is enough. No new file needed.
Rules for Removing: Find Tests You Can Delete
Deleting tests feels risky, so don't leave it to individual courage. Write the criteria down.
Four conditions that make a test a removal candidate
- A lower layer already verifies the same thing: unit tests cover the boundary values, yet an E2E test tries the same inputs
- The feature no longer exists: a test for a retired screen still passes thanks to stubs
- An E2E test that has not failed in 90 days and is covered at a lower layer: never failing alone is not a reason to delete, but combined with duplication it is
- High flakiness with no owner to fix it within two weeks: a test nobody trusts is worse than no test
How to spot duplicate tests
- Collect coverage per test and line up tests that exercise the same set of lines (with Jest or Vitest, run
--coverageper test file and compare) - Each month, pull the top 10 tests by failure rate and by runtime from your CI history
- Grep test names for the same issue number or screen name appearing at multiple layers
Make It a Habit: A 30-Minute Monthly Pruning Session
Criteria do nothing without a moment to apply them. We recommend a short monthly session: 30 minutes, two or three people.
- Put the top 10 slowest tests and the top 10 flakiest tests on screen
- Label each one: keep / move down a layer / merge / delete
- Open the PR for every "delete" during the session. Don't carry it over
- Record total suite runtime and compare it with last month
In the first few sessions, the most common candidates are E2E tests checking boundary values that unit tests already cover. E2E tests are slow one by one, so simply pushing these down a layer shortens runtime noticeably. Also track whether missed regressions increased in the months you pruned; that record is what justifies the next round.
Common Mistakes
- Hiding behind skip:
it.skipis just a postponed deletion. Set a deadline: skipped for 30 days means deleted - Guarding only the coverage number: keeping low-value tests to protect a percentage only adds runtime
- One person does all the pruning: when they move teams, the suite grows back. Document the criteria so anyone can decide
Putting It Into Practice With Bugoon
"Write it at the lowest layer" depends on how quickly you can pin down the root cause. Bug reports sent through the Bugoon widget automatically include a screenshot with annotations, recorded user steps, and console logs and network errors, so you can guess which layer is at fault before building an E2E reproduction.
Reports are linked to GitHub Issues, so you can drop the issue number straight into the test name and trace any test back to the original report. If you hand bug fixes to Claude Code or Cursor through the MCP server, adding "also add a regression test named with the issue number" to your instructions keeps the adding rule in place without extra effort.
Being able to record on a report whether the fixed bug has a matching test would make gaps in regression coverage visible on the kanban board, which could make this workflow even easier to manage.
Streamline bug reporting for your team.
Bugoon is free to get started. Add one line of code to your site and transform how your team handles bugs.
Get Started