Advertisement

For a lot of developers, "deploy" is a button someone else set up and a held breath. Understanding what actually happens behind that button turns it from a ritual into a process you can reason about — and reasoning about it is what lets you ship small and often instead of rarely and fearfully.

Underneath, almost every deployment is the same handful of steps: turn source into an artifact, get that artifact onto servers, switch traffic to it, and watch. The tools vary wildly; the shape does not.

Build once, deploy the artifact

The first step is building: your source code is compiled, bundled and packaged into a single artifact — a container image, a zipped bundle, a set of static files. The crucial discipline is to build that artifact once and promote the exact same one through every stage. Rebuilding separately for staging and production invites the classic "worked in staging" failure, where subtle differences creep in.

A good pipeline runs your tests against that artifact before it goes anywhere near users, so a red build never reaches production. The artifact that passed the tests is the artifact that ships — no surprises introduced in between.

Advertisement

Releasing without a cliff edge

Getting new code live does not have to mean flipping every user to it at once. A rolling release updates servers a few at a time, so if the new version is broken, only a fraction of traffic is affected while it rolls. A blue-green release keeps the old version running, brings the new one up alongside it, and switches traffic over in one move — with the old version still warm for an instant rollback.

A canary release goes further, sending a small slice of real traffic to the new version and watching its error and latency metrics before widening. Each of these is a way to limit the blast radius of a bad deploy, which is the whole game in production.

The nets that make it safe

Three things make deploying calm rather than tense. Fast rollback: a one-command way back to the last known-good version, so a bad deploy is an inconvenience, not an incident. Health checks: automated probes that confirm the new version is actually serving before it takes full traffic. And observability: logs, metrics and alerts that tell you within minutes if something degraded.

With those nets in place, the safest strategy is counter-intuitive to newcomers: deploy more often, in smaller pieces. Small changes are easy to review, easy to reason about, and easy to roll back. The teams that deploy many times a day are usually not braver — they have just built the nets that make each deploy boring.

Advertisement

Draining connections before a server is actually removed from rotation

Simply killing an old server process the instant a new one is ready is a common way to turn an otherwise smooth deploy into a burst of failed requests, because any request already in flight to that old server gets abruptly cut off mid-response — a properly orchestrated deploy instead removes a server from the load balancer's rotation first, waits for its currently in-flight requests to finish naturally (connection draining), and only then shuts it down, which is a small sequencing detail that is the entire difference between a deploy that is invisible to users and one that produces a brief, visible spike of failed requests every single time it happens.

Migration ordering: why the database change usually has to land before the code that needs it

A deploy that includes both a database schema change and application code that depends on that change has a real ordering constraint most teams learn about the hard way: if the code deploys before the migration finishes, it queries a column or table that does not exist yet, and if the migration runs but takes longer than the code's own rollout, there is a window where old code is running against a new schema it was never written to expect — the reliable pattern, covered from a different angle in this library's blue-green-specific article, is running migrations in a backward-compatible way and completing them before the corresponding code deploy begins, treating schema and code changes as sequenced steps rather than one atomic event.

Advertisement

What a health check actually verifies, and why a shallow one is worse than none

A health check endpoint that merely returns HTTP 200 without actually verifying that the application's real dependencies — its database connection, any required downstream service — are genuinely reachable gives a false sense of readiness: an orchestrator can mark a server healthy and route real traffic to it while that server is actually unable to serve a single genuine request, because the shallow check never actually exercised the path that would have revealed the problem. A meaningful health check exercises enough of the application's actual critical path to be a genuine signal, while still staying fast enough not to become its own performance burden on every check interval.

Why "deploy" and "release" are, mechanically, two separate events worth naming separately

Everything covered in this article — building an artifact, promoting it through environments, switching traffic, running health checks — describes deployment: getting new code running somewhere. Whether that new code's behavior is actually visible to any user is a separate question, answered by whatever release mechanism, feature flags being the most common, controls visibility independently of deployment — recognizing these as two genuinely separate events, rather than one combined moment, is precisely what unlocks deploying far more frequently than user-visible changes actually ship, since the risk of a broken deploy and the risk of a broken user-facing release are no longer the same risk happening at the same time.

Why immutable infrastructure changed what 'deploying' a server even means

An older model of deployment updated a long-running server in place — pulling new code, restarting the process — which left room for configuration drift between servers that had each been updated slightly differently over time; the immutable infrastructure approach instead never updates a running server at all, it builds a brand new server image containing the new version and replaces the old server entirely, which guarantees every server is running from an identical, known-good image rather than an accumulated history of in-place patches nobody can fully reconstruct.

Why a deploy dashboard showing 'success' can still be lying

A deploy pipeline reporting success typically only confirms that the deploy mechanics themselves completed without error — the new version is running and passing its health checks — which is a meaningfully narrower claim than 'the application is behaving correctly for real users,' and conflating the two is a common mistake; the only way to actually confirm the second, stronger claim is watching real production metrics for some period after the deploy completes, not merely trusting a green checkmark on the deploy tool's own dashboard.

Why the very first deploy of a brand-new service looks nothing like the hundredth

A service's first-ever deploy typically involves manually provisioning infrastructure, wiring up monitoring, and validating an entire pipeline end to end for the first time, which is meaningfully slower and more involved than the routine, largely automated deploys that follow once that infrastructure and pipeline already exist — recognizing that the deploy process itself has its own maturity curve, improving considerably after the first few iterations, sets more realistic expectations for a team standing up a new service for the first time.

Why a deploy freeze around high-traffic events is a deliberate trade-off, not overcaution

Teams that pause routine deploys during a known period of unusually high or critical traffic — a major sale, a live event — are not contradicting the frequent-small-deploys philosophy discussed throughout this article, they are making an explicit, temporary trade-off: the marginal risk any single deploy carries, however small, is judged not worth taking during the specific narrow window when a problem would be most costly, with normal deploy cadence resuming immediately once that window passes.

Why the last step of a deploy is watching, not walking away

It is tempting to treat a deploy as finished the moment the pipeline reports success and traffic has switched over, but the actual riskiest window is usually the several minutes immediately afterward, while real traffic is exercising the new version for the first time at scale — deliberately watching key dashboards for a defined period after every deploy, rather than immediately moving on to the next task, is what actually catches a problem while it is still small and easily reversed rather than discovering it later from a user complaint.

Why a deploy's blast radius depends on server count, not just on code quality

A bug that would affect one hundred percent of a two-server fleet affects a much smaller fraction of a rolling deploy across fifty servers updated a few at a time, purely as a function of fleet size rather than anything about the bug itself — this is a structural reason larger fleets, somewhat counterintuitively, can afford to deploy more aggressively than small ones, since the same rolling-deploy strategy naturally limits exposure to a smaller fraction of total traffic the more servers there are to roll across.

Why a deploy checklist matters even once most of the process is automated

Automation handles the mechanical steps reliably, but a short, explicit checklist for anything that still requires human judgment — confirming a risky migration has actually completed, checking whether now is an appropriate time given other activity — catches the specific category of mistake automation cannot, since it is precisely the steps nobody thought to automate that are most likely to be silently skipped under time pressure without a checklist actively prompting for them.

Why a rollback plan is part of what 'happens when you deploy', not a separate topic

Every deploy this article describes should be planned with its own undo already in mind, not as an afterthought bolted on once something has already gone wrong — the specific mechanics of doing that well are covered at more length elsewhere in this library, but it is worth naming here explicitly as the final, essential step in the sequence build-release-watch this article has walked through: a deploy is not actually complete until the team also knows exactly how it would be undone if needed.

Why understanding this sequence changes how a team talks about deploys at all

Once build, release, and watch are understood as three distinct, separately reasoned-about phases rather than one monolithic scary event, a team's own language about deploying tends to shift correspondingly — from treating a deploy as a single high-stakes moment to discussing which specific phase, if any, actually carries risk for a given change, which is itself evidence the underlying mental model has genuinely changed, not just the vocabulary describing it.