on this page

You changed one button color, pushed straight to main, and now the live site is a blank white page in front of every visitor you have. Your stomach drops. You’re now debugging in production, on a clock, while the people you were trying to impress are watching the white screen.

There’s a calmer way to live, and it costs you nothing. The whole trick is putting a copy of your built site somewhere safe so you can poke at it before it becomes the thing real people see. That copy has a name: a preview deploy. Let’s walk through what these environments actually are, why they exist, and the boring little workflow that means you never have to feel that stomach drop again.

The environments you actually meet

When you connect a repo to Vercel

Heads up. You're leaving raindev.fyi

This link heads to vercel.com, an external site we don't control. Cool to keep going?

Continue
, Netlify

Heads up. You're leaving raindev.fyi

This link heads to www.netlify.com, an external site we don't control. Cool to keep going?

Continue
, or Cloudflare Pages

Heads up. You're leaving raindev.fyi

This link heads to pages.cloudflare.com, an external site we don't control. Cool to keep going?

Continue
, you don’t get one website. You get a little family of them, each built from different code.

Production is the one that matters to the outside world. It’s the deploy that serves your real domain (raindev.fyi, not some random subdomain), and it’s built from your production branch, which is almost always main. When someone types your address, this is what loads. There is exactly one production deploy live at a time, and replacing it is the scary moment we keep coming back to.

A preview deploy is the safety net. Every time you push code to a branch that isn’t main, and every time you open a pull request, the host quietly builds that exact code and hands you a throwaway URL for it. It’s the real production build, minified and compiled the same way the live site is, sitting at an address like my-app-git-fix-navbar-yourname.vercel.app. You can click around it, share it, break it, and none of it touches the live site. When the branch goes away, so does the preview.

People also say branch deploy, and it means roughly the same thing as a preview: a deploy built from one specific branch. The distinction worth keeping is that one branch (your production branch) deploys to production, and every other branch deploys to a preview. Same machinery, different destination.

You’ll also hear about staging: a permanent, always-on environment that sits between your laptop and production, often at a fixed address like staging.yourapp.com, where a team gathers up changes and tests them together before release. It’s useful when several people are shipping into the same app and need one shared place to check that it all works together. As a solo beginner you almost never need it. Your per-pull-request preview deploy already gives you a clean, real build to test against, which is most of what staging was for in the first place.

A table to keep it straight

Deploy typeBuilt fromURL looks likeWho sees it
ProductionYour production branch (main)raindev.fyi, your real domainEveryone, including real users
PreviewA pull requestapp-git-pr-42-you.vercel.appAnyone with the link
BranchOne specific non-main branchapp-git-fix-login-you.vercel.appAnyone with the link

The column that surprises people is the last one. We’ll come back to it.

Rollbacks, or the undo button you forgot you had

Here’s a detail nobody tells beginners: your host keeps your old deploys around. Every successful production deploy you’ve ever shipped is still sitting in the dashboard, frozen, ready to serve.

That means when a release does go wrong, you are not trapped. You don’t have to find the bad commit, write a revert, push it, and wait for a fresh build while the site is down. You open the host’s deploys list, find the last deploy that worked, and click rollback (Vercel calls it “Promote to Production,” Netlify and Cloudflare Pages have a similar one-click option on older deploys). The previous good version is live again in seconds, and then you go fix the code at a normal heart rate.

Rollbacks are the reason this whole approach works. The preview deploy catches most problems before they ship. The rollback catches the ones that slip through anyway.

Why pushing straight to production is scary

When you push directly to main, your build runs and goes live with nobody having looked at the result. There’s no second pair of eyes, not even your own, on the actual built site. If something’s wrong (a missing environment variable, a broken image path, a build that technically succeeds but renders a blank page), the first person to notice is a real visitor.

And because it’s live, the failure is public. You’re not calmly reading an error in your terminal. You’re getting a message that says “the site’s down?” while you frantically try to remember what you changed. The pressure makes you sloppy, the sloppiness makes it worse, and the whole time the broken version is what the world sees. The fix isn’t to be more careful when you push to main. The fix is to stop pushing surprises to main at all.

The safe workflow, step by step

This is the loop. Once it’s muscle memory it takes about twenty extra seconds per change, and it’s the difference between “I think this works” and “I watched this work on the real build.”

# 1. Start a branch for the change you're making
git checkout -b fix/navbar-spacing

# 2. Do the work, commit it
git add .
git commit -m "fix: tighten navbar spacing on mobile"

# 3. Push the branch up to GitHub
git push -u origin fix/navbar-spacing

Now go to GitHub

Heads up. You're leaving raindev.fyi

This link heads to github.com, an external site we don't control. Cool to keep going?

Continue
and open a pull request (a PR), which is just a formal request to merge your branch into main. The moment you open it, your host notices, builds your branch, and posts a comment right on the pull request with a link to the preview deploy. No configuration, you just connected the repo once and it does this forever.

Click that link. You’re now looking at your change on the real production build, at a real URL, exactly as it will behave when it’s live. Test the thing you changed. Click around the parts you didn’t change to make sure you didn’t break them. If a teammate or a client needs to see it, send them the link and they can look without installing anything or running your code.

When it looks right, you merge the pull request into main. That merge is the trigger: the host sees a new commit on your production branch and ships the production deploy. Your change is live, on your real domain, and you already saw the exact build that’s serving it.

# After the PR is merged on GitHub, sync your laptop
git checkout main
git pull origin main

The same pattern holds whether you’re shipping an Astro site, a React front end, an Angular app, or a Node and Express API to Railway

Heads up. You're leaving raindev.fyi

This link heads to railway.app, an external site we don't control. Cool to keep going?

Continue
or Render

Heads up. You're leaving raindev.fyi

This link heads to render.com, an external site we don't control. Cool to keep going?

Continue
. Branch, preview, merge, production. The hosts differ in the dashboard wording, not the idea.

Why the preview is worth the extra minute

Localhost lies to you, gently. Your dev server runs unminified code, with source maps and hot reload and a forgiving setup tuned for fast editing. The production build is a different animal: compiled ahead of time, minified, sometimes rendered or bundled in ways that only happen during npm run build. A bug that only shows up in the real build (a tree-shaking surprise, a missing dependency that dev happened to have loaded) will never appear on localhost. The preview is the first place you’d ever catch it, and it catches it before a user does.

The link sharing alone earns its keep. “Here’s the staging link” is a much better sentence than “let me push it live and you can tell me if it’s wrong.” You can get sign-off on a redesign, show a client the new checkout flow, or ask a teammate “does this look right to you?” without the live site ever being involved.

Common mistakes

MistakeWhat actually happensDo this instead
Committing a secretAn API key or DATABASE_URL lands in your code and shows up in a public preview buildKeep secrets in the host’s env-var settings, never in the repo; rotate any key you’ve already committed
Assuming a preview is privateAnyone with the guessable preview URL can open it; it’s public by defaultTreat preview links like you’d treat any link; use the host’s password protection for anything sensitive
Forgetting production only ships on mergeYou push to a branch, see the preview, and wonder why the live site didn’t changeThe live site updates when you merge into main, not when you push the branch
Testing only on localhostLooks perfect in npm run dev, breaks in the real buildOpen the preview deploy and test the actual built site before you merge

That second row trips up almost everyone. A preview URL is built and hosted by the same company that serves your production site, so it feels private and official. It isn’t. It’s a real URL on the public internet, and the only thing protecting it is that nobody’s guessed it. Anything you wouldn’t paste into a public chat shouldn’t be sitting unprotected on a preview.

The payoff

Once branch-preview-merge is just how you work, shipping stops being a held breath. You make a change, you watch it work on the real build, someone else gets a chance to say “wait, that’s wrong” before any user can, and on the rare day something slips through, one rollback click buys it back. The live site stops being a thing you’re scared to touch and goes back to being what it should be: the place your finished work lands, after you’ve already seen it work. That’s the whole point of building real projects, that you can keep changing them without flinching.