Staging guide
Test before you ship.
Setting up a staging environment for your web app.
A safe place to break changes before customers see them. Paste one prompt into your AI coding tool and it will plan the sandbox — database, data, and keys — without changing anything until you say so.
In a hurry? Start here
Paste the prompt below into your AI coding tool (Codex, Claude Code, Cursor, Lovable, Bolt, Replit) with your project open. It will read this guide, look at how your app is actually set up, and hand you a plan in day-sized steps. It will not change anything until you say so.
Plan-only prompt
Plan only. Do not make any changes. Analyze and plan.
Read the guide at https://gimbl.dev/test-before-you-ship and use it as the checklist. Then look at the current
setup of this codebase and its hosting, database, and services, and plan the steps required to
set up a staging (acceptance test) environment that is completely sandboxed from production,
including the database, all data, and every environment variable and API key.
As you plan:
1. Take inventory first. List every part of this app the guide's Step 1 table asks about, with
the specific service and project you find for each. For every outside service, say whether
the app only reads from it or also writes to it.
2. Prefer the simplest shape the guide describes: a long-lived staging branch, a preview or
branch deployment with a fixed URL, a database branch or second project, and a separate set
of keys and settings scoped to staging. Say which of these my platforms support directly.
3. For each outside service, recommend isolate, fake, or share, and give the reason, using the
guide's three questions. Anything that can text, email, notify, or charge a real person must
be isolated or faked.
4. Check whether the migration history can rebuild the database from scratch. If it cannot,
list the gaps as the first thing to fix.
5. Tell me what you can do yourself with the access and tools you have right now, and what I
will have to do by hand in a dashboard or console. Keep the two lists separate.
6. Keep this context in mind: [services you plan to retire, data feeds that are read-only,
features that are coming soon, anything the guide marks "later" that you want kept on the
roadmap but not prioritized].
7. Roll the plan out in stages. Order them by low-hanging fruit: the thing I can finish in
about a day that gives me something useful I do not have yet goes first. Every stage should
leave me with a working benefit on its own. Put "nice to have" items such as restricting
staging to approved testers and the test-site banner on the roadmap, but after the stages
that make staging exist and prove it is isolated.
8. End with the guide's readiness checklist, marked with what the plan covers and what it
leaves for later.
Plan only. Do not make any changes. When I have read the plan, I will tell you which stage to
start.If your tool cannot open links, paste the Step 1 and Step 2 tables and the readiness checklist into the chat instead.
Who this guide is for
If you built your app with an AI coding tool and real people now use it every day, you need a safe place to test changes before they go live. This guide shows you why, and how to set one up on whatever tools you use.
It is written for AI-assisted builders: people who use tools like Codex, Claude Code, Cursor, Lovable, Bolt, or Replit to build and fix their app. You do not need a computer science background. You do need to know which services your app runs on.
By the end you will have:
- A clear picture of what a test environment is and why it matters
- A checklist of every part of your app that needs a test version
- A simple way to decide what to copy, what to fake, and what to leave alone
- A step-by-step rollout you can do one small piece at a time
- A ready-to-use prompt that gets your AI coding tool to plan the work for you
The ideas work on any stack. Where it helps, we use popular tools (Vercel, Netlify, Render, Railway, Supabase, Neon, GitHub) as examples.
Why you need a test environment
If every fix goes straight to your live app, your customers are your testers, and every mistake lands on them in the middle of their workday.
A real story
One builder we worked with runs a logistics company with about 100 employees. In a few months he used an AI coding tool to build an app that runs his daily operations: schedules, vehicle assignments, driver dashboards, and live performance data.
His process looked like this:
- 1Someone finds a bug, or he spots one himself.
- 2He describes it to his AI coding tool.
- 3The tool writes a fix.
- 4The fix goes straight to the live app his whole team is using.
It worked, until it didn't. One morning the app shut off right as his office manager was sending out the day's vehicle assignments. She called him in a panic. He had to stop what he was doing and fix it while the whole operation waited.
What goes wrong without one
- Your users find your bugs. A broken button is a small thing on your laptop. It is a big thing for a dispatcher with 50 drivers waiting.
- Bad changes hit real data. A buggy fix can overwrite, duplicate, or delete records you cannot easily get back.
- Outages happen at the worst time. You usually ship changes during the day, which is when your users need the app most.
- Growing makes it riskier. Once other companies use your app, a mistake can expose one customer's data to another.
- Testing tools can't help you. Automated testers like Gimbl use your app the way a person would. They fill in forms, click buttons, and save records. You would never point that at your live app.
The goal
A test environment is a full copy of your app that is safe to break. You make changes there first, try to break them, fix what you find, and only then send them live. We would rather break it before it goes out to customers.
What a test environment actually is
A test environment is a second, complete copy of your app that real users never see. Think of it as a ladder every change climbs, one step at a time.
Step 1
Local
Write and try changes. Only you see them.
Step 2
Staging
A full copy with its own database, keys, and test data.
Step 3
Production
The live app, with real data, that customers use.
- Local: where you or your AI tool write and try changes. Only you see it.
- Staging: a full copy of your app with its own database, keys, and test data. You and a few trusted testers try to break it here.
- Production: the live app your customers and team use every day, with real data.
Tools use different names for the middle step: staging, acceptance, UAT (user acceptance testing), preview, QA, or sandbox. They all mean the same idea: a safe place to prove a change works before it goes live.
Step 1: Take inventory of your app
Before you build anything, list every moving part of your app. Anything you miss will quietly keep talking to your live systems.
AI-built apps often have more parts than their owners realize. Go through this list and write down what you use for each one. If you are not sure, ask your AI coding tool to list them for you (see the prompt at the top of this guide).
Scroll sideways to read every column.
| Part | What to write down | Vibecoding tools | Enterprise tools (everything on the left, plus) |
|---|---|---|---|
| Web app | Where it is hosted and its live URL | Vercel, Netlify, Render, Railway | Your own servers or containers on AWS, Azure or Google Cloud |
| API or backend | Where server code runs, if separate | Vercel functions, Render service, Supabase Edge Functions | Dedicated backend services behind an API gateway; AWS Lambda or Azure Functions |
| Database | Which service and project | Supabase, Neon, Firebase, PlanetScale | Managed PostgreSQL, MySQL or SQL Server (AWS RDS, Azure SQL); Oracle; MongoDB Atlas |
| Logins | How users sign in and where accounts live | Supabase Auth, Clerk, Firebase Auth | Company single sign-on through Okta, Auth0, Microsoft or Google |
| File storage | Where uploads, photos, and documents go | Supabase Storage, S3, Cloudinary | Azure Blob Storage, Google Cloud Storage, a shared drive |
| Mobile apps | iPhone and Android builds and how they ship | Expo, TestFlight, Google Play testing | Company-managed phones where IT controls which build installs |
| Browser extensions | Any plugin that reads or sends data | Custom Chrome extension | Extensions pushed to every company browser by IT |
| Scheduled jobs | Anything that runs on a timer | Vercel cron, database cron, Zapier schedules | A job runner built into the app; cloud schedulers (AWS EventBridge, Google Cloud Scheduler) |
| Outside services | Every service you send data to or pull from | Twilio, Resend, Stripe, Slack, payroll, partner APIs | Salesforce or HubSpot, Workday or ADP, SAP, Teams, partner data feeds |
| Keys and settings | API keys, secrets, and URLs your app reads at startup | Environment variables in your host | A secrets vault (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault) with one set per environment |
| Links in messages | URLs inside emails, texts, and push notifications | Password reset links, app download links | Links inside generated PDFs, invoices and partner notifications |
| Code repository | Where your code lives and which branch is live | GitHub, GitLab, Bitbucket | GitHub Enterprise, GitLab, Azure DevOps |
For each outside service, also note one thing: does your app only read from it, or does it also send data to it? That answer drives most of the decisions in Step 3.
Step 2: Set up the five building blocks
Every good test environment, on any platform, is built from the same five pieces. Get these right and the rest is details.
- 1A separate code branch. Keep one branch for live (usually
main) and one for testing (usuallystaging). Changes land instagingfirst and only reachmainafter they pass testing. - 2A separate deployment with a stable URL. Your testing branch should deploy to its own web address that stays the same every time. Testers, automated tools, and login settings all need one fixed address to point at.
- 3A separate database. This is the most important one. Your test app must never read or write your live data. Use a copy of the structure with test data in it.
- 4Separate keys and settings. Your test environment gets its own API keys, secrets, and URLs. If a test key leaks or gets misused, your live system is not affected, and there is less to replace.
- 5Test data. Fake companies, fake users, and test accounts that let you try every feature without touching real people.
How popular tools handle each piece
Scroll sideways to read every column.
| Building block | What to look for | Vibecoding tools | Enterprise tools (everything on the left, plus) |
|---|---|---|---|
| Code branch | A long-lived staging branch | GitHub, GitLab, Bitbucket | Protected branches that require a review before anything reaches live |
| Deployment and URL | Branch or preview deploys with a fixed per-branch address | Vercel preview deployments, Netlify branch deploys, Render preview environments, Railway environments | A dedicated staging server, slot or cluster namespace on AWS, Azure or Google Cloud |
| Database | Database branching, or a second project | Supabase branches, Neon branches, a second Firebase project | A staging database restored from a live backup, with names, emails and phone numbers masked |
| Keys and settings | Settings scoped to preview or staging only | Vercel and Netlify environment variables by environment or branch | A secrets vault with one set of entries per environment |
| Test data | A seed file your tool can load into a fresh database | seed.sql, fixture files, a setup script | A masked copy of live data plus seed scripts, or a synthetic data generator |
A few platform notes worth knowing
- On Vercel, a branch's preview URL stays the same across pushes, so give testers that branch link, not the one-off link for each deployment (Vercel).
- On Netlify, a branch named
stagingdeploys to its own address that includes the branch name (Netlify docs). - Supabase charges a preview branch for the compute it uses, starting around $0.32 a day on the smallest size, with no fixed fee (Supabase docs). For most small apps a full-time staging branch is roughly $10 a month in compute.
- On Neon, a branch made from a protected production branch gets its own new login details, so staging cannot reuse production credentials by accident (Neon).
If your platform does not offer branching, the simple fallback still works: create a second project (a second database, a second hosting project) and call it staging.
Step 3: Isolate, fake, or share each outside service
You do not need a perfect test copy of every service your app touches. For each one, pick one of three options based on what it does.
- Isolate: use a separate test account or test keys for that service.
- Fake: turn it off in staging, or have it log what it would have done instead of doing it.
- Share: let staging use the live service as-is. Only safe when your app only reads from it.
Ask these questions in order:
- 1Does it send anything to real people or move real money? Texts, emails, push notifications, payments, payroll. Then isolate or fake it. A test should never text a real user or charge a real card.
- 2Does your app write data into it? Then isolate it, so tests do not create junk records in a system you rely on.
- 3Does your app only read from it? Sharing live data is often fine and saves a lot of work. Building fake data for every feed can cost more than it is worth.
- 4Are you replacing it soon? If you plan to drop a service in favor of a feature in your own app, do not spend time isolating it. Turn it off in staging and move on.
- 5Do several customers use your app? Then each customer's staging setup should use its own keys, never a shared set.
Scroll sideways to read every column.
| Service type | Usual choice | Why |
|---|---|---|
| Text messages (Twilio and similar) | Isolate or fake | Tests must not reach real phones |
| Email (Resend, SendGrid, Postmark) | Isolate or fake | Send to a test inbox, or log only |
| Payments (Stripe and similar) | Isolate | Most payment tools have a built-in test mode |
| Read-only data feeds | Share | No risk of changing anything |
| Tools you are about to replace | Fake (turn off) | Not worth the setup time |
| Chat and team tools (Slack, etc.) | Fake or a test channel | Keeps noise out of real channels |
| Payroll and HR systems | Isolate or fake | Never let a test touch real pay data |
Three ways to keep messages away from real people
The table says "isolate or fake" for texts and emails. That is one option. There are two more, and the teams who have been burned use all three.
Option 1: separate keys, or test mode.
Give staging its own Twilio, SendGrid, Resend or Postmark credentials, or use that provider's sandbox mode, so a message sent from staging never leaves the provider. This is the right first step and it fails safe. Its weakness is that it only protects you where you remembered to swap the key. One service set up by copy-paste from live, and staging is texting your drivers.
Option 2: only testers in your test data.
If any email address or phone number in your staging database belongs to someone who does not know the test system exists, you have a problem waiting to happen, whatever your keys say. Make it a rule: every contact in staging is a tester. When you refresh staging from a copy of live, rewrite the contact columns as part of the refresh. Replace addresses with tester addresses, or with a plus-tagged form of one tester inbox such as qa+driver17@yourcompany.com, and replace phone numbers with tester numbers. Then a message that slips out reaches someone who expected it.
Option 3: label everything that leaves.
Whenever the system is not live, put something unmistakable at the front of every email subject and every text: [TEST SYSTEM]. Testers can tell at a glance which copy sent it. If Options 1 and 2 both fail, the person who gets it knows to ignore it instead of acting on a vehicle assignment that never existed. This costs one line in your send function. Add it whichever of the other two you pick.
The next section shows the cleanest way to decide "is this live?" for that one line.
A pattern worth stealing: let the database answer “Am I production?”
Everything above depends on your app knowing which environment it is in. Should this email send? Should this text go to a real number? Should this timed job run? Should the banner say TEST SITE?
Most apps answer with a setting on the host: an environment variable like NODE_ENV, a build flag, a hostname check. It works until it does not. Settings get copied along with everything else when someone clones a deployment, a build made with staging settings gets promoted, a hostname check breaks when you add a custom domain. The one time the flag is wrong is the time it matters.
The pattern we use at Gimbl, and the team behind it has used for years across many systems large and small, turns the question around. Instead of asking the host "Am I production?", ask the database "Are you the production database?" Store the production database's own address in a settings row inside it:
Settings
-----------------------------------------------------
Key | Value
ProductionDb | prod-db.yourcompany.com / yourappAt startup the app compares that row to the server and database name it actually connected to. Match: this is production. No match, or no row: this is a test system.
Why this beats a true/false flag: when you restore the production database to staging, the row comes along, but it still names the production server. Staging connects to staging-db and finds a row that says prod-db. They differ, so staging knows it is a test system, automatically, with no step for anyone to remember and nothing to flip. A plain IsProduction = true flag would be restored as true, and the one safety you built would be the one thing the restore turned off.
Why the database and not the host? Because what makes an environment production is not which server runs the code or which branch built it. It is which database the code is pointed at. If you are hitting the production database, you are production, whatever your settings claim. So the fact belongs with the data, and the data should describe itself.
Three rules make it safe:
- 1Missing means test. No row, or a row that does not match, means test system. A fresh database is never accidentally production.
- 2Set it once, on the production database only. The row is written when you stand up production and then never touched. Nothing in your deploy or restore scripts needs to know about it.
- 3Drive a TEST SITE banner from the same answer. Every page of a test system shows a banner that says so, and the banner reads the same "Am I production?" answer as your email and text gates. This is your early warning: if production ever misreads the setting because of a bug, the banner shows up on your real site and someone tells you within minutes. If the banner is missing on staging, your test system thinks it is production, and you find out before it sends anything.
You still keep separate keys and tester-only data. This pattern is what makes those two hard to bypass by mistake. This check and the banner are built into the Precision Framework (precisionwebapp.com), the platform Gimbl runs on; ask your AI coding tool to add the same check and route every "Am I production?" decision through it.
Common traps and how to avoid them
Most staging setups that fail do so for one of these reasons. Check each one before you trust your test environment.
- Your database can't be rebuilt from your code. If you changed your database by hand or by asking an AI tool to "just fix it," those changes may not be saved as migration files. A fresh staging database will then be missing tables or columns. This is often the hardest part. Ask your AI tool to check that your migration history can rebuild your current database from scratch, and to fill any gaps.
- Live addresses are hard-coded. Your app, mobile build, or browser extension may have your live URL typed directly into the code. Staging will then quietly send data to production. Move every URL into a setting that changes per environment.
- Your staging link keeps changing. Many hosts give each deployment its own one-off link. Use the fixed branch link or a staging domain instead, or testers and login settings will break.
- Scheduled jobs don't run in staging. Some hosts only run timed jobs on the live site. On Vercel, cron jobs only run for production deployments and preview deployments are skipped (Vercel). Plan to trigger them by hand in staging, or use a separate scheduler.
- Database timers are separate. Jobs scheduled inside the database follow different rules from your host. Review them on their own.
- Links in messages point to live. Password resets, invites, and download links built from a live URL will send testers to production.
- Staging builds get promoted as-is. A build made with staging settings should not be pushed live unchanged. Merge the code to your live branch and let it rebuild with live settings.
- Custom domains skip your protection. Some hosts only protect their own preview addresses. If you add a custom staging domain, check that it is protected too, rather than assuming it is.
- Storage and limits surprise you. A test environment won't catch your live database running out of space. Watch your live usage and plan limits separately.
Step 4: Roll it out in stages
Do not try to build the perfect setup in one weekend. Break the work into small stages you can finish in about a day each, where every stage gives you something useful right away.
A good order for most apps:
- 1Staging branch and stable URL. Create the
stagingbranch and confirm it deploys to a fixed address. Benefit: you can see changes before they go live. - 2Separate database with test data. Point staging at its own database and load fake companies, users, and test accounts. Benefit: you can click anything without fear.
- 3Separate keys and settings. Give staging its own API keys and secrets, and move hard-coded URLs into settings. Benefit: staging can no longer reach live systems by accident.
- 4Outside services. Work through your Step 3 decisions one service at a time, starting with anything that texts, emails, or charges money.
- 5Mobile apps and extensions. Add a way for test builds to point at staging (see "Who tests and how").
- 6Scheduled jobs. Decide which timed jobs staging needs, and how to trigger them.
Later, when you have time
- Limit staging access to approved testers only
- Add a clear "STAGING" banner so no one confuses it with live
- Add short-lived previews for each feature, on top of your one long-lived staging
These are good ideas, but they are not blockers. Put them on your list and keep moving. Start with one long-lived staging environment, prove it is truly isolated, then add extras.
Your new release workflow
Once staging is set up, every change follows the same path. Nothing goes live until it has been tried in staging and approved.
Step 1
Build or fix
Make the change with your AI coding tool.
Step 2
Push to staging
Staging redeploys on its fixed URL.
Step 3
Test in staging
Trusted users and an automated tester try real tasks.
Step 4 · decision
Approve or send back
The decision. Wrong means fix it and push to staging again.
Step 5
Merge into main
The live site rebuilds with its own live settings.
Step 6
Know the rollback
Learn the host’s “last good version” control before you need it.
Send it back returns to step 1. Approve continues to merge, then you keep the rollback path ready.
- 1Build or fix the change with your AI coding tool.
- 2Push it to your staging branch. Staging redeploys automatically.
- 3Test it in staging. Your trusted users run their real tasks, and an automated tester like Gimbl works through the app.
- 4Approve it, or send it back. If something is wrong, fix it and push to staging again.
- 5Merge staging into main. Your live site rebuilds with its own live settings.
- 6Know how to roll back. Most hosts let you return to the last good version in a few clicks. Learn where that button is before you need it.
The big change is step 3. You stop learning about bugs from upset users in the middle of the day, and start finding them yourself in a place where breaking things costs nothing.
Who tests and how
The best testing mixes a few trusted people with an automated tester. People catch what feels wrong. Automated tools catch what you forgot to check.
Pick a small group of trusted users. You do not need your whole team. Choose a few people who know the work well and give honest feedback. Ask them to run through their real daily tasks in staging before you ship.
Use staging to preview new features. Put a new feature in staging first and let your trusted group try it. If something breaks, it only affects a handful of people in a test copy, and they can switch back to the live app to keep working.
Add automated testing. A tool like Gimbl.dev uses your app the way a person would: it signs in, clicks through pages, fills in forms, and reports what breaks or confuses users. Because it takes real actions, always point it at your staging URL, never your live one.
Mobile apps need a way to switch. Web apps are easy because staging has its own address. For phone apps, common options are:
- A separate test build (for example through TestFlight or Google Play internal testing) that always points at staging
- A hidden menu, behind a code only you know, that lets approved testers switch between live and staging
Browser extensions need the same thing. Either a setting that picks the environment, or a separate test copy of the extension installed in a different browser profile.
Make staging look different. A colored banner or label that says "STAGING" keeps people from doing real work in the test copy by mistake.
Let your AI coding tool do the heavy lifting
Your AI coding tool can plan most of this for you, and often do much of the setup, if it is connected to your hosting, database, and code services. The key is to ask for a plan first and forbid changes until you approve it.
Tips that make the plan better
- Start a fresh chat so old context does not confuse it.
- Say "plan only, make no changes" at the start and the end of your prompt.
- Ask what it can do vs. what you must do by hand. This tells you where you will need to log in and click.
- Give it context it can't see. Tell it which services you plan to retire, which data feeds are read-only, and what is coming soon.
- Ask for small stages. Request a rollout where each stage takes about a day and is useful on its own.
- Turn up its thinking level if your tool has one. Planning is worth the extra effort.
- Read the plan before you say yes. Ask follow-up questions about anything you do not understand.
The prompt
Use the prompt in In a hurry? Start here at the top of this guide. It already follows every tip above: plan only at the start and end, inventory first, what it can do vs. what you must do by hand, your context, and stages ordered by payoff.
Readiness checklist
Your staging environment is ready when you can tick every box below. Do this before you let anyone, or any automated tester, loose in it.
Tick these as you go. Checks stay in this browser tab and reset when you leave.
Quick isolation test: create a test record in staging, then check your live app. If it shows up there, stop and find the leak before going further.
Staging is where Gimbl points. Never production.
Gimbl uses your app the way a person would: it signs in, clicks through pages, fills in forms, and saves records. That is real traffic. Point it at the staging URL — the copy that is safe to break — and never at the live app your customers use.