Every code change is a chance to break something that already worked. You fix a bug in checkout and the discount code stops applying. You upgrade a dependency and login quietly fails on Safari. Regression testing is the practice that catches this: re-running tests on existing features to confirm a change did not break them. It is the difference between shipping with confidence and shipping with crossed fingers.
What is regression testing?#
Regression testing is re-testing software that already worked, after a change, to make sure it still works. A "regression" is the bug itself: functionality that used to behave correctly and now does not. The change that triggers it can be anything, a new feature, a bug fix, a refactor, a config tweak, or a dependency upgrade.
The point is not to test the new code. That is what you write fresh tests for. The point is to verify that the new code did not have side effects on the old code. Most regressions are surprises, which is exactly why you need a repeatable safety net rather than hoping you remember everything a change might touch.
Why it matters#
Software is interconnected, so a change in one place can ripple into another with no obvious link between them. As a codebase grows, the number of those hidden connections grows faster than any person can track. Without regression testing, each release quietly raises the odds that something old is now broken, and you find out from users instead of from your suite. A good regression suite turns "did we break anything?" from a guess into a check that runs on every change.
When to run it#
Run regression tests whenever the code or its environment changes:
- After every bug fix, to confirm the fix did not introduce a new break.
- After a new feature, to confirm existing features still work alongside it.
- After a refactor, where behavior should be unchanged, so any difference is a regression by definition.
- After dependency or infrastructure upgrades, a common and easily missed source of breakage.
- Before a release, as the final gate.
The ideal is automated regression tests wired into CI, running on every pull request and every deploy, so nothing reaches production without passing.
Types of regression testing#
The term covers a few distinct strategies. The practical ones:
| Type | What it does | Trade-off |
|---|---|---|
| Retest-all | Re-run the entire suite | Safest, but slow and expensive at scale |
| Selective | Re-run only tests touching the changed area | Fast, but you must map tests to code well |
| Progressive | Update existing tests as the feature spec legitimately changes | Keeps the suite honest as the product moves |
| Partial / smoke | Re-run a small critical-path subset for quick confidence | Fast signal, lower coverage |
Most teams blend these: a fast partial or smoke pass on every commit, and the full suite on a schedule or before release. The skill is choosing how much to re-run for a given change so you catch regressions without paying to re-run everything every time.
Manual vs automated regression testing#
Regression testing is the clearest case for automation in all of testing, because it is repetitive by nature: the same checks, run again and again, every time something changes. Doing that by hand does not scale. It is slow, it is boring, and bored humans miss things, so the regressions slip through exactly when you most need them caught.
Automated regression testing runs the suite on every change with no human effort, in CI, in minutes. That is where it belongs for anything you check more than a few times. Manual testing still has a role, but not here: keep humans on exploratory and usability work, the judgment-heavy testing a script cannot do, and let automation own the repetitive regression pass. For how this fits a wider plan, see software testing strategies.
How to build a regression suite#
- Identify what must not break. Start from risk: the flows where a regression would hurt most, like auth, checkout, and data integrity. These are your first regression tests.
- Cover every fixed bug. When you fix a bug, add a test that fails on the old behavior and passes on the fix. This is how the same bug stops shipping twice.
- Pick the right level. Push each check as low as it can go: a unit test for logic, an integration test for a contract, an end-to-end test only for true user flows. A suite that is all browser tests is slow and brittle.
- Automate and wire into CI. A regression suite that depends on someone remembering to run it is not a safety net. Gate merges and deploys on it.
- Keep it green and trusted. A flaky suite gets ignored, and an ignored suite is worse than none because it gives false confidence. Fix or quarantine flaky tests fast.
- Prune as you go. Delete tests that never catch a real regression and only add runtime. Coverage of risky paths beats a high test count.
Common pitfalls#
- Letting it rot. As the app changes, tests drift out of date and start failing for the wrong reasons. An unmaintained suite gets muted, then deleted.
- Testing everything through the UI. Slow and flaky. Most regressions are catchable lower in the stack.
- Ignoring flakiness. One flaky test trains the team to ignore red, which defeats the entire purpose.
- No bug-to-test discipline. If fixed bugs do not become regression tests, you will fix some of them more than once.
Where automation and AI fit#
The real cost of regression testing is not running the suite, it is keeping it green as the UI changes. Selectors drift, flows get redesigned, and tests break for reasons that are not real regressions. That maintenance is why teams abandon end-to-end regression coverage. This is where AI test tooling helps: ObserveOne's Autopilot generates Playwright regression suites from a URL and self-heals selectors when the app shifts, so the suite survives redesigns instead of rotting. You still decide what must not break; the tooling lowers the cost of keeping it covered.
The short version#
Regression testing re-runs checks on existing features to make sure a change did not break them. Run it after every fix, feature, refactor, and upgrade, and before every release. Automate it and wire it into CI, since it is the most repetitive testing there is. Anchor the suite on your riskiest flows, turn every fixed bug into a test, push checks as low in the stack as they will go, and keep the suite green so people trust it. For the bigger picture, the software testing strategies guide and the Cypress vs Selenium comparison are good next reads.