Code flows downhill: main to staging to prod, no cherry-picking
The second you cherry-pick a hotfix straight to prod, you've built an environment containing code that exists nowhere else, and what's actually live becomes a research project.
When you’re the only engineer, staging feels like ceremony you’re paying for entirely alone. There’s no review board to satisfy, no release manager to answer to, just you, a passing test suite, and a tempting shortcut. So you start asking the dangerous question: can I just push this one straight to prod?
I asked it. I answered yes a few times. And that’s exactly how I ended up with production quietly serving buggy code that no other environment had ever seen, because the broken commit lived in precisely one place: the only environment that mattered.
The rule is boring on purpose
Everything lands on main. main promotes to staging. staging promotes to prod. In that order, every time, no exceptions for “it’s just a one-line fix” or “it’s urgent.” The discipline is dull, and that dullness is the whole point. A release process is only as trustworthy as its most-skipped step.
The shortcut always shows up wearing the costume of a hotfix. Something is on fire in prod, you have the fix in your hand, and the honest pipeline (commit to main, watch staging, then promote) feels like making a bleeding patient fill out forms. So you cherry-pick the fix straight onto the production branch and move on. The fire goes out. You feel fast.
What you’ve actually done is create a fourth environment that exists nowhere in your diagram: prod, plus one commit. Staging no longer matches production. main no longer matches production. The thing you tested against and the thing your users are hitting have quietly diverged, and nobody told you, because you’re the only one here and you already know what you did. Except three weeks from now, you won’t.
”What’s live” should never be a research project
Here’s the failure I actually hit. I’d cherry-picked a couple of urgent fixes straight to prod over a few weeks, each one sensible in the moment. Then I went to debug a strange report and could not reproduce it anywhere. Not locally, not on staging. The behavior only existed in production, and I burned an embarrassing afternoon before I understood why: prod was running a combination of commits that had never existed together in any environment I could inspect.
One of those hand-applied fixes had a bug. Because it had skipped staging, nothing had ever exercised it against realistic data. Because it lived only on prod, my real users were the test environment. I had built, by hand, the exact situation a pipeline exists to prevent: code in production that I could not trace, reproduce, or trust.
The whole value of an environment chain is that “what is live” is a fact you can read off a branch, not a question you have to investigate.
The moment you cherry-pick, you trade a fact for an investigation. And you trade it cheaply, because the cost lands on a future version of you who has lost all the context you have right now.
The discipline is for the you of three weeks from now
People frame release hygiene as something you do for the team, for auditors, for the on-call rotation. As a solo dev I have none of those, which is exactly why I almost skipped it. But the real beneficiary was never the team.
- Promotion in order means reproducibility. If staging always becomes prod, then staging is a faithful rehearsal. Anything you see in production, you can see in staging first, on purpose, before a user does.
- No cherry-picking means traceability. Production is whatever main was at the last promotion, full stop. You never have to diff a live server against your own memory of what you panic-patched.
- Boring is fast, eventually. The hotfix shortcut is faster today and slower every day after, because every divergence you create is a future hour spent reverse-engineering your own infrastructure.
So now, even when it’s three in the morning and the fix is one character, it goes to main, rides to staging, and promotes to prod like everything else. Not because a process document says so. Because I’ve met the person who debugs a production-only mystery at the far end of a shortcut, and that person is me, tired, with no idea what I was thinking. Code flows downhill. I just make sure it only flows one way.