Back to BlogQuality & Testing

How Do I Stop My App Breaking Every Time I Change Something?

Rupak Amin

Founder & Lead Engineer, RAITHub

7 min read

RAITHub ships and tests production software. See QA as a Service or talk to us.

Add regression tests on the paths that matter, and gate your CI on them so a change that breaks one cannot ship. Your app breaks on every change because nothing checks the old features when you touch the code, so a fix in one place silently breaks another. The cure is an automatic safety net that runs on every change and blocks the ones that break something.

If you would rather have that safety net built for you, see how RAITHub would test this below, or the QA and test automation overview.

Why does changing one thing break another?

Because software is connected, and nothing is watching the connections. When you change a shared function, a database column or a shared component, everything that depends on it can break, and you will not know until a user hits it. Without tests, the only thing checking your old features is your memory, and memory does not scale past a handful of screens. This is the pattern behind every release breaks something, and it gets worse, not better, as the app grows.

If the app is edited with an AI tool, the problem is sharper: each new prompt can rewrite code you were relying on. The specific version of this is in regression testing AI-edited code.

What is a regression test, and how does it help?

A regression test is an automated check that a feature still works after you change the code. "Regression" just means something that used to work has broken. One test, run on every change, turns "I hope I did not break login" into a red or green answer in seconds. A set of them over your critical paths means a change that breaks a paying customer's workflow is caught before it ships, not after.

Without regression testsWith regression tests gated in CI
You find breakage when a user reports itYou find it in seconds, before it merges
Every change is a gamble on what else movedA broken change is blocked automatically
Fear of touching the code slows you downYou change code confidently, because the net catches regressions
Memory is your only coverageThe suite remembers every feature, on every change

The discipline of running these on every deploy is in regression testing after every deploy. Whether a suite like this is good enough is covered in how to know if your tests are good enough.

Where do I start, and how much is enough?

Start with the paths that cost most when they break, not with full coverage. For most apps that is sign-up and login, the core workflow, payments, and the check that one user cannot see another's data. Four to ten solid tests over those paths catch most expensive regressions. The highest-value one is usually login plus the core workflow, because almost every change touches them:

Free checklist

AI-Built App Launch Readiness Checklist

25 checks before you let real users in. Enter your email and we’ll reveal it below (and send you a copy).

One email, the checklist, no spam. By submitting you agree we can email you this checklist and reply to your enquiry.

import { test, expect } from '@playwright/test'

test('a user can log in and complete the core workflow', async ({ page }) => {
  await page.goto('/login')
  await page.getByLabel('Email').fill(process.env.USER_EMAIL!)
  await page.getByLabel('Password').fill(process.env.USER_PASSWORD!)
  await page.getByRole('button', { name: 'Sign in' }).click()
  await page.goto('/app')
  // ...drive the main job to its done state and assert the result
  await expect(page.getByText('Done')).toBeVisible()
})

Then gate CI on them, so the tests actually block a bad change instead of just reporting it after the fact.

How do I make the tests actually block a bad change?

Run them automatically on every pull request and make a failure stop the merge. A minimal GitHub Actions gate looks like this:

name: tests
on: [pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test   # a failure here blocks the merge

Doing this yourself takes roughly one to three days for a small app if you know the stack: a day to write the core-path tests, and the rest to wire the CI gate and remove flakiness. The main risk of doing it alone is writing tests that pass but do not really check the behaviour, so prove the suite works by breaking a feature on purpose and watching it go red.

Buy, build or hire?

RouteChoose this when
A testing tool or AI test agentYour flows are defined and someone will review and maintain what it generates
Build the tests yourselfYou know the stack, have a few days, and will verify them by breaking the code
A freelancer to build a suite onceYou want the initial safety net built and can brief the critical paths
A managed QA teamYou want tests written, gated in CI and maintained on every release, that you keep

How RAITHub would test this

RAITHub builds the safety net that stops a change from breaking your app, and keeps it running.

  • A risk map first: the journeys that cost most when they break, so the first tests go where it matters.
  • Regression tests in CI: the critical paths gated so a change that breaks them blocks the release automatically.
  • Flakiness handled: stable tests you can trust, not ones the team learns to ignore.
  • Tests you own: the suite and CI config live in your repository, with the IP assigned to you.

Timeline: on a live product the first month usually goes on the baseline and gating the critical paths, as a month-to-month retainer. For proof, PropDesk runs 1,024 tests, Sundor Skin 530+, and this website 400+ in CI before any change deploys. See QA as a Service. The next step is a free 15-minute audit, then a written fixed quote; RAITHub publishes no rates.

Tired of every change breaking something? Ask about regression gates.

Frequently asked questions

How do I stop my app breaking every time I change something?

Add regression tests on the paths that matter and gate your CI on them, so a change that breaks one cannot ship. The app breaks now because nothing checks the old features when you edit the code; an automatic safety net that runs on every change fixes that.

What is a regression test?

An automated check that a feature still works after you change the code. "Regression" means something that used to work has broken. Run on every change, it turns "I hope I did not break this" into a fast red or green answer, so breakage is caught before users see it.

How many tests do I need to start?

Four to ten solid tests over the critical paths catch most expensive regressions: sign-up and login, the core workflow, payments, and the check that one user cannot see another's data. Start there and widen coverage as the app grows, rather than chasing full coverage first.

Why does my app break more as it gets bigger?

Because more code means more connections, and without tests nothing watches them. A change to a shared function or component can break everything that depends on it, and memory cannot track that past a few screens. Tests scale where memory does not.

My app is edited by an AI tool. Does this still apply?

Even more so. Each new prompt can rewrite code you relied on, so an AI-edited app breaks in exactly this way. Regression tests gated in CI check every AI edit, which is the only practical way to keep an AI-edited app stable as you keep prompting.

Can I just test more carefully by hand instead?

Not reliably. Manual checking does not scale, and the breakage you miss is usually in a feature you were not thinking about. Automated regression tests remember every critical path on every change, which careful manual testing cannot do past a handful of screens.

stop app breaking on every changeregression testingCI gatestest automationvibe codingQA as a service

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.