Why I don't use a staging environment for solo projects
This update was drafted on a schedule by the AI I build with, from real project notes — part of the vibecoding experiment this blog documents.
Let me clear something up, because it comes up whenever I mention I ship straight to production: no, I don't have a staging environment. Not for tab., not for CreatorLens, not for any of them. And I want to explain why that's an actual decision and not just me skipping a step I was too lazy to set up.
A staging environment exists to answer one question: does this change break something before real users see it? That's a completely legitimate question. On a team, with a real user base, staging is how you protect people who didn't sign up to be your test subjects. I'm not arguing against staging in general. I'm arguing it's the wrong tool for exactly one shape of project: a solo build, pre- or barely-launched, where the person most exposed to a broken deploy is me.
What staging actually costs
Here's the part people underrate. A staging environment isn't free once you have it — it's a whole second copy of your infrastructure that needs to stay in sync with production. A second database, or a snapshot of one. Its own environment variables, which drift from prod the moment someone (me) updates one and forgets the other. Its own deploy step, which means every change ships twice — once to prove it works, once to actually ship it — and now there are two places a deploy can silently diverge.
For a team, that overhead buys something real: a safety net between "someone wrote this" and "everyone else is affected by it." For a solo project it buys me a second environment to keep in my head, for a category of bug that's usually cheap to fix in production anyway when I'm the only one who'll see it break.
What I actually do instead
The setup that's replaced staging for me is two things working together: branch previews, and a rollback that's fast enough to functionally be an undo button.
Every branch and pull request gets its own deployed preview URL, automatically, wired to the same database or a safe subset of it depending on the project. That's my staging — except it costs zero extra config beyond what I'd want anyway (a way to look at a change before merging it), and it doesn't need to be kept in sync with anything, because it's built from the same source at the same commit. I click the preview link, poke at the thing I changed, and if it looks right, I merge.
Then there's rollback. This is the part that actually does the job staging is supposed to do. If something ships and breaks, the fix isn't "we would have caught this in staging" — it's "I revert to the last known-good deploy in under a minute and go find the bug without anyone-facing pressure." A fast rollback converts "this could break production" from a scary sentence into a non-event. The bad deploy was live for the length of time it took me to notice, which for a solo project with light traffic is usually before it mattered to anyone but me.
Why this is the right tradeoff for now, not always
I want to be honest about the actual boundary here, because "no staging" is a decision I'd reverse and not a principle I'm attached to. It's a good vibecoding pairing with not adding a whole class of production-grade infrastructure before a project has outside users — it's the same "re-decide when the facts change" instinct, just applied to the deploy pipeline instead of the database tier. Branch previews plus fast rollback is the right setup exactly as long as I'm the primary person who'd be affected by a bad deploy. The day a project has real users depending on uptime — the day a broken deploy costs someone else something, not just me an evening — that's the trigger to add a real staging tier, the same way a first outside user is the trigger to re-decide the database tier or turn on error tracking.
I haven't hit that day yet for most of what I'm running. When I do, I'll add staging back for that one project, not for all nine of them out of habit. The mistake isn't skipping staging early. It's skipping the re-decision later.
This one's auto-drafted from my notes on a schedule. If a number isn't in the notes, it doesn't show up here — I'd rather leave a blank than make something up.