← Back to blog
Testing FundamentalsBy

Regression Testing: What It Is, When to Run It, and How to Keep It Cheap

The bug that costs you the most is not the one that throws an error. It is the one where everything still works and a number is quietly wrong.

Somewhere in your product right now there is probably a number that is wrong. Not missing, not throwing, not logged anywhere — just wrong. It renders in the right font, in the right place, next to a label that describes it accurately, and it has been wrong since a refactor three weeks ago that nobody thought was risky.

That is a regression, and it is the single most expensive class of bug most teams ship.

The bug you cannot see

Here is a real one. A shopping cart, a $44.00 headlamp, a 20% coupon. Two builds of the same store, ten days apart. Look at them for as long as you like.

After the refactor
A cart with a $44.00 headlamp and NORTH20 applied, showing a discount of $10.79 and a total of $46.07

Discount −$10.79 · Total $46.07

Before the refactor
A cart on the correct previous build with a $44.00 headlamp and NORTH20 applied, showing an $8.80 discount and a $48.23 total

Discount −$8.80 · Total $48.23

Same product, same coupon, same page. A refactor moved the coupon calculation one line, so it now takes 20% of the subtotal plus shipping instead of the subtotal alone.

Every element on the broken page is internally consistent. The subtotal is right. The tax is correctly derived from the discounted amount. The total adds up. Nothing is red, nothing is missing, and nothing is logged — because from the software’s point of view nothing went wrong. It did exactly what the new code told it to do.

$1.99Over-discounted per order
0Errors thrown
0Alerts fired
200HTTP status returned

A crash announces itself. A regression waits, in production, behaving perfectly, until somebody reconciles the numbers at the end of the quarter.

What regression testing actually is

Regression testing is the practice of re-running tests that already passed to confirm that a new change has not broken behaviour that used to work. It is not about finding bugs in the feature you just built — that is what testing the feature is for. It is about proving that the other nine tenths of your product still does what it did yesterday.

The word is borrowed from statistics, where regression means a return toward a previous state. In software the previous state is the one where your checkout worked.

The defining property of a regression: it is invisible to the people most likely to look at it. Your error tracker will not catch it. Your monitoring will not catch it. A human clicking through the release will not catch it. Only something that knows what the number was supposed to be will catch it.

Watch one get caught

Abstract arguments about regression testing are easy to nod along to. So here is the actual run. We described the check in five plain-English lines, pointed it at the broken build, and let it go.

Real Diffie run · demo.diffieai.comTest failed
What we asked for

1. Go to the store home page 2. Add the 300lm Headlamp to the cart 3. Open the cart 4. Enter the coupon code NORTH20 and apply it 5. Verify the discount line shows $8.80 and the order total is $47.80

What came back

Expected discount $8.80, observed $10.79. The agent wrote a standalone Playwright candidate, ran it in a cloud browser, and reported: “the coupon was applied successfully, but the application does not currently produce the specified discount or order total.” Total elapsed: under two minutes, from sentence to verdict.

No selectors were written by hand. The agent explored the page, found the cart and coupon controls itself, and asserted on the numbers a customer would see.

Note what the assertion is about. Not .summary > div:nth-child(3). Not a wait, not a page object, not a fixture. The discount line shows $8.80 — a claim about the product, phrased the way the business would phrase it.

Regression vs smoke vs sanity vs retesting

These four get used interchangeably in conversation and mean genuinely different things in practice. The distinction matters because it determines when each one runs.

Regression vs smoke testing

A smoke test answers one question: did this build come up at all? It touches a handful of critical paths, finishes in seconds, and exists to stop you wasting an hour of deeper testing on a build that was never viable. Regression testing is the broader sweep that runs after smoke passes.

Regression vs sanity testing

Sanity testing is narrow and targeted: a bug was fixed, so you check that specific area behaves. It is a spot check, not a sweep. Regression testing does not assume you know where the damage is — which is the entire point, because with regressions you usually do not.

Regression vs retesting

Retesting verifies that a specific bug is actually fixed. Regression testing verifies that fixing it did not break something else. A fix that closes the ticket and breaks two adjacent flows is a net loss, and retesting alone will report success.

What belongs in a regression suite

The instinct is to test everything. Resist it — a suite that covers everything takes too long to run, breaks constantly, and gets disabled within a quarter. Cover the journeys where breakage costs money or trust:

  • Authentication. Sign-up, sign-in, password reset, session expiry. If these break, nothing else matters.
  • Anything that touches money. Pricing, discounts, tax, shipping thresholds, currency, invoices, refunds. This is where silent regressions are most expensive and least visible — as the example above demonstrates.
  • The primary conversion path. Whatever your equivalent of browse → cart → checkout → confirmation is.
  • Permissions and data boundaries. Can a user on one account see another’s data? This class of regression is a security incident, not a bug.
  • Every bug you have already fixed once. A recurring bug is the clearest possible signal that a test was missing. Adding one at fix time is the cheapest test you will ever write.
  • Integrations you do not control. Payment providers, auth providers, anything with a webhook. Their changes are regressions in your product.

What does not belong: exhaustive coverage of every form field, every empty state, every permutation of a settings page. Those are better served by unit tests, which run in milliseconds and do not need a browser.

Types of regression testing

You will see these named as distinct disciplines. In practice they are four scopes of the same activity, and the useful question is which one you can afford on a given trigger.

  • Corrective — no code changed, so the existing suite is re-run unchanged. Cheapest possible run; useful after dependency or infrastructure updates.
  • Selective — run only the tests touching the area that changed. Faster, and the right default for pull requests, but it depends on knowing what a change affects, which is often wrong in exactly the cases that matter.
  • Progressive — the tests themselves are updated alongside deliberate behaviour changes. Necessary when the spec genuinely moved.
  • Complete — everything, every test. Too slow per-PR for most teams; correct before a major release or after a large refactor.

When to run it

The single highest-leverage decision in regression testing is not what you test — it is when the test runs.

A regression caught on the pull request that introduced it costs minutes to fix. The author still has the change loaded in their head, the diff is small, and the blame is unambiguous. The same regression caught a week later, after twenty more commits, costs hours — somebody has to bisect, reconstruct intent, and work out whether the new behaviour was deliberate.

  • Every pull request — the core suite, ideally under ten minutes. Blocking.
  • Every merge to main — the same suite against the integrated branch, because merges introduce failures no individual PR contained.
  • Nightly — the complete suite, including slow paths you cannot justify per-PR.
  • Before release — complete suite, production-like environment, production-like data.
  • After any dependency or infrastructure change — these break things in ways nobody predicts, and the diff gives you no hint where.

Why regression suites decay

Almost every team that abandons automated regression testing abandons it for the same reason, and it is not that the tests failed to find bugs. It is that the tests broke constantly for reasons unrelated to bugs.

The root cause is nearly always the same: the tests were written against the DOM, not against intent. A test asserting on .cart-summary > div:nth-child(3) .value is asserting on the shape of your markup. Move that div into a wrapper for a layout fix and the test fails, having found nothing.

Do that twice a sprint and the team learns that red means “the tests are broken again” rather than “the product is broken”. Once that association forms the suite is finished, whether or not anyone turns it off.

Three things keep a suite alive:

  • Assert on outcomes, not structure. “The discount is $8.80” survives a refactor. “The third div contains 8.80” does not.
  • Keep it fast enough to block a PR. A suite that takes forty minutes gets moved to nightly, then weekly, then nowhere.
  • Treat a flaky test as a broken test. One randomly failing test teaches the whole team to ignore red builds. Fix it or delete it — leaving it is the one option that actively costs you.

When the spec is the thing that is wrong

Look closely at the run above and you will notice something we did not plan. The expected total in that specification reads $47.80. The correct total is $48.23.

We wrote the acceptance criterion by hand and forgot that shipping applies below the $100 free-shipping threshold. So we ran the same test against the correct build, expecting a clean pass, and got this:

Real Diffie run · demo.diffieai.comTest failed
What we asked for

1. Go to the store home page 2. Add the 300lm Headlamp to the cart 3. Open the cart 4. Enter the coupon code NORTH20 and apply it 5. Verify the discount line shows $8.80 and the order total is $47.80

What came back

The discount assertion passed at $8.80. The total assertion failed — the page showed $48.23, because $44.00 minus $8.80 plus $9.95 shipping plus $3.08 tax is $48.23. The code was right. Our written criterion was wrong, and had been since we typed it.

An honest failure, kept in. This is the second thing a regression suite does, and almost nobody advertises it.

A good regression suite does not only catch code that drifted from the spec. It catches specs that were wrong to begin with — and in our experience those are considerably more common than anyone admits.

What actually changed

Everything above is standard practice and has been for twenty years. The reason teams still ship regressions is not that they disagree with any of it. It is that building and maintaining the suite costs more than the regressions appear to — right up until the moment one of them does not.

That calculation changes when writing a test costs a sentence instead of an afternoon. Every run on this page came from a plain-English description, executed in a real cloud browser, with video of exactly what happened. There is no page object model to maintain and no selector to update when the markup moves, because the test was never written against the markup.

That is the actual argument for automating regression testing in 2026: not that machines are more thorough than people, though they are. It is that the maintenance burden which killed every regression suite you have previously abandoned is the part that has changed.

Frequently asked questions

What is regression testing?

Regression testing re-runs tests that already passed, to confirm that a new change has not broken behaviour that used to work. It is not about finding new bugs in a new feature — it is about proving the rest of the product still does what it did yesterday.

What is the difference between regression testing and smoke testing?

A smoke test answers "did the build come up at all" and runs in seconds against a handful of critical paths. A regression suite answers "is everything that used to work still working" and covers far more surface. Smoke testing is a gate you run first; regression testing is the broader sweep you run after it passes.

How often should you run regression tests?

On every pull request for the core suite, and nightly for the full one. Regressions are cheapest to fix in the minutes after they are introduced, while the author still has the change in their head. A suite that only runs before a release finds the same bugs a week later at ten times the cost.

Should regression testing be automated?

Yes, for anything you intend to run more than a few times. Regression testing is repetitive by definition — the same journeys, over and over, forever. That is exactly the work humans are worst at and machines are best at. Manual regression testing is viable only for a very small suite or a one-off release check.

What should go in a regression suite?

The journeys that lose money or trust if they break: sign-up, sign-in, search, add to cart, checkout, payment, and any flow tied to billing or permissions. Add a test for every bug you have already fixed once, so it cannot come back silently.

Why do regression suites become so expensive to maintain?

Because most are written against CSS selectors and DOM structure rather than against intent. A button moving from one container to another breaks a selector but does not break the product. Suites written against what a user is trying to do survive refactors that selector-based suites do not.

Keep reading

Build a regression suite in a sentence

Describe the journey in plain English. Diffie runs it in a real browser, saves it as a regression test, and checks every pull request with video evidence.