In defense of the boring deploy
Blue-green, canaries, feature flags, and the deeply unfashionable practice of shipping the same way every time.
The best deployment I ever worked on took eleven minutes, ran the same way every time, and had a rollback that was a single command anyone on the team could run from their phone. Nobody wrote a conference talk about it.
Deployment is a solved problem that organizations keep re-opening because the solution is boring and boring does not get anyone promoted.
the properties that matter#
Strip away the tooling debates and a good deploy has four properties.
It is the same every time. Not "mostly the same, except for the database migration, and except on Fridays, and except when Sarah does it." The same. If there is a manual step, it is in the pipeline as an approval gate, not in someone's head.
It is reversible in under a minute. This is the one people get wrong. They build elaborate progressive rollouts and then discover that rolling back requires reverting a migration that dropped a column. Reversibility is a design constraint on your changes, not a feature of your deploy tool.
Failure is detected without a human looking. If the way you find out about a bad deploy is a customer email, you do not have a deployment process, you have a deployment ritual.
It happens often enough to be unremarkable. The failure rate of a deploy scales superlinearly with the amount of change in it. Ten small deploys are dramatically safer than one deploy containing ten changes, and this is the single most robust finding in the entire delivery-metrics literature.
the expand-contract discipline#
Most rollback pain is schema pain. The fix is a discipline, not a tool:
- Expand. Add the new column, nullable. Deploy. The old code ignores it.
- Migrate. Backfill. Dual-write from application code. Deploy. Both shapes work.
- Switch. Read from the new column. Deploy. Still reversible — the old column is intact.
- Contract. Drop the old column, days or weeks later, once you are certain.
Four deploys instead of one. Every intermediate state is reversible. It feels slow and it is the reason you do not have a 3 a.m. incident where the rollback made things worse.
flags are not free#
Feature flags decouple deploy from release and that is genuinely valuable. They also multiply your state space: n flags means 2^n possible configurations, and you are testing approximately one of them.
Rules that keep this manageable:
- Every flag gets an owner and an expiry date at creation.
- A flag that has been at 100% for a month is deleted, not left "just in case."
- Flags never nest. If flag A only matters when flag B is on, you have made something unreasonable.
- Kill-switch flags are a separate category with separate rules and they can live forever.
The failure mode is flag debt: a codebase with two hundred flags where nobody knows which combinations are actually exercised in production. That is worse than no flags, because it looks like safety.
the actually unfashionable opinion#
Most teams do not need Kubernetes to deploy well, and many teams running Kubernetes deploy worse than they did before, because the platform's complexity consumed the attention that used to go into the process.
If your deploys are fast, boring, and reversible, your infrastructure is correct regardless of what it is. If they are not, no amount of platform will fix it, because the problem is that nobody has treated the deploy as a product with a user.
The user is your team at 2 a.m. Design for them.
— Dom, March 10, 2025