Deliver software straight to production
Laptop to production, nothing maintained in between. The argument for deleting your non-prod environments, the four things that have to be true before you can, and the order to build them in so you don't take the safety net down before the new one is up.
Someone on your team asked "is staging broken or is it just me" in the last month. In a channel where four other people saw it and nobody answered, because they'd all learned not to.
Eventually somebody loses an afternoon to it. Usually the person who least wanted to. The change that was waiting behind it ships two days late, and nobody writes that down anywhere, because a slow week isn't an incident. It's just what Tuesday is like.
Add it up and that box costs you a few thousand a year in compute, an afternoon a month of someone good, and a permanent tax on every lead time you measure. What it buys is a green tick about a system that doesn't exist.
I'd delete it. Not casually, and not first (the order here matters more than the decision does), but the destination is worth naming plainly: code goes from a laptop to production, and nothing in between is something a person has to keep alive.
That's the opinionated version. What follows is the whole way through, including the places where I think this argument is weaker than the people who agree with me tend to admit.
The distinction the whole argument turns on
"No environments" is a silly position and it isn't the one I'm arguing. Your tests have to run somewhere. Your build has to happen somewhere.
The line that matters isn't production against not-production. It's a thing you maintain against a thing you destroy.
A maintained environment has a hostname. It also has a database with eighteen months of junk test data in it, a config that quietly diverged from production last March, an API key nobody can rotate because nobody remembers where it's used, an owner who isn't quite sure it's them, and a Terraform module two versions behind. Above all it exists between deploys, and that persistence is the whole problem, because everything that goes wrong with it goes wrong slowly, in the gaps, while nobody is looking at it.
A destroyed environment is a process. Created from code and a known dataset, runs four minutes, returns a verdict, stops existing. Nothing drifts because nothing is there between runs. Nobody asks whether it's up, because the question doesn't parse.
So the rule I'd actually write down: nothing between your laptop and production gets a permanent hostname.
That single sentence does most of the work here. CI runs are fine. Ephemeral per-branch environments that die on merge are fine, and they're genuinely useful for design review and for anything with a third-party sandbox on the other end. A box called staging that has been up since 2023 is the thing to kill.
Staging is wrong on day one and wronger every week
The case for staging is production parity. That's the whole pitch. It fails immediately, and then it keeps failing in a direction nobody watches.
Wrong on day one, because the data is different, the scale is different, the traffic is different. Wronger every week after, because the drift only runs one way: somebody patches staging to unblock themselves at 4pm and that patch never travels back. Test data piles up. A third-party integration sits on a version production upgraded past eleven months ago. Nobody involved is being unreasonable, and six months in you've got two systems that share a codebase and nothing else.
Charity Majors has been making the sharper version of this argument for the better part of a decade, and the part worth internalising is about which bugs staging can find at all. You can catch the things you predicted would fail, and the things that have failed before. Distributed systems, she points out, are hostile to being cloned. The failure you actually get paged for is a specific interaction between real traffic, real data volume, and a dependency behaving the way it behaves at 4pm on a weekday, and no amount of parity investment reaches it.
Then there's the part nobody puts in the business case.
Staging is a queue. Somebody else's change is in it, so yours waits, and you've built a shared resource with contention and no scheduler. It's a gate too, and gates rot into checklists. "Has this been through staging" turns into a thing you tick rather than a thing you think about, which is worse than never having asked.
Give the counter-argument its due, though, because there's a real one. Ephemeral preview environments do genuinely narrow the parity gap rather than widen it, precisely because each one gets built fresh from code and a known state instead of accumulating. Good pattern. I use it. It's also not staging (it's the destroyed-not-maintained category), and if you keep exactly one idea from this section, keep that distinction rather than a blanket allergy to the word environment.
The chain is what I'd refuse in every case. Dev, then QA, then staging, then pre-prod, then production, each with its own sign-off and its own owner. An org chart wearing a deployment diagram as a costume. Past the first stage the defect-catching value rounds to nothing, while the lead-time cost compounds at every hop.
What has to be true before you can delete anything
Here's where most attempts fail, and it isn't subtle. People read the argument, agree with it, delete staging on a Friday, and find out they took down a bad safety net without having built a good one.
Because staging, for all its lying, was doing something. Badly, late and expensively. Still something.
Four things replace it. Tests fast enough that nobody routes around them. A pipeline short enough that merge-to-production is one sitting. Production you can see into, per change. An undo that's faster than diagnosis.
Until all four are real you aren't simplifying anything. You're going faster into a wall.
Only the last one is fun to build, which is exactly why so many teams have a polished rollback script and none of the other three.
Tests fast enough that nobody routes around them
The budget for your test suite is not set by your CI capacity. It's set by human attention.
Ten minutes is about how long someone waits before opening something else. Once they've opened something else you haven't lost ten minutes of theirs, you've lost twenty-five, and that's the real cost. Slow tests don't hurt you by being slow. They hurt you by teaching people to batch changes, skip the local run, and merge on hope.
DORA's benchmark lands in the same place from the other direction: elite teams keep median build times under ten minutes, and under five for pull request validation is a common target. Treat those as ceilings, not goals.
Four moves, in the order that pays.
Hermetic first. Everyone skips this one and it's the prerequisite for the other three. A test that shares a Postgres instance, a fixture file, or a port with another test can't be parallelised, and the figure that gets quoted around is that roughly two thirds of flaky failures trace back to isolation rather than to anything genuinely nondeterministic. Every one of those gets worse the moment you add workers. So you buy the bigger CI plan, turn on eight lanes, and the suite comes back slower and less trustworthy than it was on one. Demoralising afternoon.
Then parallelise. It's the single biggest separator between fast teams and slow ones, and the returns are real right up until they aren't: past eight or ten shards the coordination overhead starts eating the gain on most suites. Measure before you keep buying lanes.
Then quarantine flakes automatically. Same day, by a job, with no human in the decision. A flaky test is worse than a slow one and worse than no test at all, because it teaches a whole team to disbelieve a red build, and that lesson sticks long after you've fixed the test. You buy the trust back by deleting or repairing it. Never by hitting rerun.
Then delete a tier. A forty-minute suite is usually carrying a layer of slow integration tests somebody wrote in 2021 to compensate for instrumentation that didn't exist yet. It exists now. Some of those tests are doing a job your production telemetry does better, cheaper and about eight minutes sooner, and nobody has gone back to check which ones.
Track p95, not the mean. Nobody experiences your average build.
Fifteen minutes, merge to production
Put a number on the whole path and defend it like a budget, because that's what it is.
Eight minutes of tests. Two to build and package, two to deploy, three of canary bake before it goes wide. Fifteen from merge to fully rolled out, which an engineer can sit through without leaving the tab.
Yours will differ and the numbers aren't sacred. What matters is that the budget is written down, that every segment has a name against it, and that a change which blows it gets treated as a regression instead of as weather. Nobody I've met has this written down. That's how you get a fifty-minute pipeline that no one ever decided on, assembled one reasonable two-minute addition at a time over four years.
It goes wrong in the first segment nearly every time. Build and deploy are solved problems you can mostly buy. Test time is the part you have to earn, and it expands to fill whatever the team will put up with.
Instrument the change, not just the system
Most teams have monitoring. Far fewer can answer the one question that matters in the first two minutes after a deploy: did the thing I just merged change anything.
Different question from "is the system healthy," and the usual telemetry can't touch it. Your dashboard of aggregates says the error rate is 0.4%, same as yesterday. It doesn't say that every new error is on the build that landed eight minutes ago, hitting the 3% of traffic your flag is on for.
Adding dashboards won't fix that. It's structural, and it's two things.
Every deploy and every flag flip emits an event, and that event carries an identifier. Deploy markers on your graphs are the minimum viable version and they're worth the afternoon on their own.
Then that identifier has to ride along as an attribute on your actual telemetry, so you can slice by build, by flag state, by ring. This is the wide-event approach people have started calling observability 2.0: one structured record per unit of work, a few hundred fields wide, stored with its context intact. Rather than metrics here, logs there, traces in a third system, joined by hand at 2am by whoever is most awake. Vendors quote MTTR improvements of 60 to 80 percent from putting them back together. Treat the number as marketing. Treat the direction as right.
The test for whether you have this: can any engineer, without asking anyone, answer "did my change make anything worse" within five minutes of it going out. If the answer involves a Slack message to whoever knows the dashboards, you don't have it yet, and you cannot safely delete staging.
Undo before you understand
The rule is short. The decision to roll back must never require understanding the problem.
If it requires understanding, it won't happen fast, because understanding is precisely the thing in short supply at 2am. Design the whole undo path for someone who has no idea what's wrong and is slightly panicked. That's the actual user.
One command or one button. Not a runbook with eleven steps. Automated reversion on error-budget burn inside a window after each deploy, because a machine beats a person at spotting a bad trend in the first ninety seconds and doesn't need to be woken up first. And rehearse it, deliberately, in production, at 3pm on a calm Tuesday, because a rollback you haven't run in six months is a rollback you don't have.
The hard part is data, and it's hard in a way no amount of tooling removes. Code reverts cleanly. Migrations don't, and a rollback that leaves your database in a state the previous version can't read is worse than having no rollback at all. The discipline is expand-migrate-contract, every schema change compatible with the code on both sides of it, and it's covered properly in the deploy pipeline lesson so I won't repeat it here.
Worth noting what DORA found in 2025, because it cuts against a comfortable assumption: increased AI adoption correlates with increased delivery instability, even where individual productivity and code quality improve. More change arriving faster is exactly the condition where a fast undo pays and a pre-production gate doesn't, since the gate scales with human attention and the undo doesn't.
Ship dark, always
Every change goes to production disabled. Deploying is an engineering event that happens constantly. Releasing is a product decision that happens whenever it's ready. Pull those two apart and most of what people build release process for stops having a reason to exist.
I won't relitigate flags here, because the pipeline lesson covers the case and the flag-debt objection, and the agentic delivery guide covers what changes when the volume goes up.
Two things specific to a world with no staging, though.
The flag is your rollback for everything a deploy-level revert can't undo, which is more than people expect. It's the only undo you have for a change that's already written data, or that a downstream consumer has already seen. That raises the bar on flag hygiene from "good practice" to load-bearing.
And flag state belongs in your telemetry as an attribute, not only in your flag vendor's dashboard. If you can't split your error rate by who had the flag on, a percentage rollout isn't protecting anyone. It's a slower way to break things for fewer people. Better than nothing, not what you paid for.
The order to actually do this in
Deleting staging is the last step, not the first. Every failed version of this I've seen ran the list backwards.
Instrument production first, before you change anything else. You have to be able to see per-change behaviour before you'll ever trust the thing that removes the human gate, and this step is pure upside even if you stop right here and do nothing further. Metric: can an engineer answer "did my change break anything" on their own, in five minutes.
Flags second. Every risky change shipping dark, expiry date attached at creation. Metric: share of changes that reached production disabled.
Test speed third. Expect this one to take two quarters, not two weeks. Hermetic, then parallel, then automatic flake quarantine, then delete a tier. Metric: p95 pipeline time, where the trend matters more than the number.
Rollback fourth. Rehearse it before you lean on it. Metric: time from "someone says stop" to traffic on the old version, measured in a drill rather than estimated in a doc.
Now the honest test, and it costs nothing. Stop routing changes through staging but leave the thing running. Ship straight to production for a month, keep a note of every time you'd have caught something, and read the note at the end. Mine has always come back close to empty. Get your own number rather than borrowing mine.
Then delete it. If the month was quiet, that environment was already decorative and you've been paying rent on a prop.
Where this genuinely doesn't apply
I'd rather name these than pretend the argument is universal, because the version of this advice that ignores them is how people get burned.
Regulated environments with a statutory pre-production sign-off. That isn't an argument you win with delivery metrics and you shouldn't spend a year trying. Build the fast path inside the constraint instead, and note that most of this guide (instrumentation, flags, test speed, rehearsed rollback) still applies untouched. Only the last step doesn't.
Software your customers run themselves. Your production is somebody else's estate, you can't roll it back on their behalf, and you genuinely do need environments that resemble their worst configurations, because their worst configurations are the whole job.
Firmware, hardware, anything with a physical device in the loop. No. The entire argument rests on undo being cheap, and here it isn't.
Third-party sandboxes, where a payment provider hands you a test endpoint and no other way to exercise the integration. You need somewhere to point at it. Fine. Make that thing narrow and ephemeral rather than a general-purpose environment that quietly grows a second job.
Pre-traffic, none of this is urgent either. You can't canary against users you don't have. Deploy, and build the instrumentation early enough that it's already there when the traffic shows up.
Two numbers
Median time from merge to production, over the last month. Under an hour means you have a pipeline. A day means you have a process. A week means you have a release, and everything upstream of it has quietly reorganised itself around that number.
Then the one almost nobody tracks: how many environments have a permanent hostname and someone who has to keep them alive. Count them honestly, including the one that's technically decommissioned but still in the Terraform.
If the second number is bigger than one, you know what the work is. Just don't start at the end of the list.