Deployment
By the end of this lesson
Deploy automatically and safely to a target environment.
Deployment is replacing what is running with a new version. Done by hand it is a sequence of steps someone remembers, performed under time pressure, at the exact moment mistakes are most expensive. Automated, it is the same sequence written down, so it happens identically every time and anyone can trigger it.
The goal is not only automation. It is a deployment that can fail without taking the service with it, which means the new version is checked before it receives traffic, and there is a way back if the check passes and reality disagrees.
The vocabulary, which is shared across providers even when the product names are not:
- Deploy and release
- Worth separating. Deploying puts the new version on the infrastructure; releasing means users reach it. Feature flags let you deploy on Tuesday and release on Thursday, which turns two risks into one at a time.
- Readiness check
- An endpoint the platform calls to ask whether this instance can serve traffic yet. Until it answers yes, no requests are sent to it. This is the mechanism that makes an automated deployment safe.
- Liveness check
- A separate question: is this instance still working, or should it be restarted? Keep it cheap and independent of downstream services, or a slow database will cause a restart loop that makes everything worse.
- Rolling deployment
- Replace instances a few at a time. The platform's default almost everywhere. Both versions serve traffic during the roll.
- Blue-green
- Run the new version as a second complete environment, check it, then switch traffic across in one action. Azure App Service deployment slots and AWS load balancer target group swaps both implement this.
- Canary
- Send a small share of traffic to the new version, watch the metrics, then increase it. Reduces the number of affected users rather than the chance of a fault.
- Drain
- Letting an instance finish its in-flight requests before it is stopped. Without it, a deployment produces a burst of errors that look like an outage to whoever was mid-request.
The two strategies you will meet first:
| Rolling | Blue-green | |
|---|---|---|
| How it works | Replace instances in batches until all are new | Stand up a full second copy, verify it, then switch traffic |
| Extra capacity needed | One batch's worth | A second full environment, for the duration |
| Both versions live at once | Yes, throughout the roll | Yes, but only one receives user traffic |
| Verifying before users arrive | Per instance, through the readiness check | Fully, against the real environment, before the switch |
| Rollback | Roll the previous version back through the same process. Minutes | Switch traffic back. Seconds, as long as the old environment is still there |
| Main constraint | Old and new must tolerate each other, including the same database schema | Cost, and the fact that the database is usually shared between both sides |
| Suits | Most services, including the employees API. It is the default for a reason | Releases where you want a full check against real infrastructure and a fast way back |
Canary is not a third flavour of the same thing. It is a way of limiting exposure while you gather evidence.
| Blue-green | Canary | |
|---|---|---|
| Traffic to the new version | None, then all of it | A few per cent, then more, over minutes or hours |
| What it protects against | A version that is visibly broken before the switch | A version that looks fine and misbehaves under real traffic and real data |
| Needs | Traffic switching and double capacity | Traffic splitting, per-version metrics, and a rule for what counts as bad |
| If something is wrong | Everyone saw it, briefly | A small share of users saw it, for longer |
| Time to fully release | One action | As long as the ramp takes, which is the cost of the evidence |
| Poor fit when | You cannot afford the second environment | Traffic is low. Five per cent of twenty requests an hour tells you nothing, so you are waiting without learning |
deploy-staging:
needs: build-and-test
runs-on: ubuntu-latest
environment: staging # credentials and any gates are configured here
steps:
- uses: actions/checkout@v4
- name: Fetch the digest that passed the tests
uses: actions/download-artifact@v4
with:
name: artifact-digest
- name: Deploy that exact image
run: |
set -euo pipefail
DIGEST=$(cat digest.txt)
az containerapp update \
--name employees-api-staging \
--resource-group employees-staging \
--image "$DIGEST"
- name: Wait for it to report ready
run: |
set -uo pipefail
for attempt in $(seq 1 30); do
code=$(curl -s -o /dev/null -w '%{http_code}' "$READY_URL")
if [ "$code" = "200" ]; then echo "ready after $attempt checks"; exit 0; fi
sleep 10
done
echo "new revision never reported ready" >&2
exit 1
env:
READY_URL: https://employees-api-staging.internal.example/health/ready
- name: Return to the previous revision
if: failure()
run: ./scripts/rollback.sh employees-api-staging- The digest comes from the build job rather than from a tag or a rebuild. This job deploys a known artifact and decides nothing about what is in it.
- The readiness loop is what separates an automated deployment from a hopeful one. Without it the job finishes as soon as the platform accepts the update, which it does immediately — while the new revision may be crash-looping behind a readiness check that never passes.
- Thirty checks at ten seconds gives the new revision five minutes. Pick a limit from how long your application actually takes to start, then add margin, and treat exceeding it as a failure rather than waiting indefinitely.
- The readiness endpoint should verify the things the instance cannot work without — its configuration loaded, its database reachable — and it should be cheap, because the platform calls it repeatedly. Do not make it check every downstream dependency, or an unrelated outage will take your service out of rotation.
- The rollback step runs only on failure, and it is worth being blunt: a rollback script nobody has executed is a hope, not a control. Run it deliberately in staging, on a normal day, and find out how long it takes.
- AWS reaches the same outcome differently — an ECS rolling update with a deployment circuit breaker, or CodeDeploy shifting traffic between target groups. The parts that matter are identical: deploy a known artifact, verify before traffic, and have a defined way back.
Database migrations are the hard part, because a schema change is not trivially reversible. Splitting each change into additive steps is what keeps a rollback possible.
Add, never change, in the first release
Add the new nullable column or the new table, and deploy that migration before the code that uses it. The currently running version ignores what it does not know about, so this step is safe on its own.
Write to both shapes
Deploy code that writes the old and the new column. Both versions of the application can now run side by side, which is exactly what a rolling deployment requires.
Backfill in batches
Copy existing rows into the new shape in chunks, with a pause between them. One large update statement can lock a table for minutes, and a lock during working hours looks identical to an outage.
Switch reads to the new shape
Deploy code that reads the new column. This is the release you might need to roll back, and you can, because the old column is still there and still being written.
Stop writing the old shape
A separate release. After it, rolling back to the version before step four is no longer straightforward, so this is the point to be confident.
Drop the old column, much later
Only when no deployed version references it and you would not need to roll back past this point. This is the only destructive step, and separating it from everything else is what makes the other five recoverable. A rename is a drop and an add wearing a disguise, so treat it with the same care.
Summary
- Automated deployment means the same written-down sequence every time, triggered by anyone, with no remembered steps
- A readiness check before traffic is what makes it safe; without one the pipeline reports success on a broken revision
- Rolling is the sensible default, blue-green buys a fast switch for double capacity, canary limits exposure and needs real traffic
- Rolling deployments require old and new versions to tolerate each other, including the same schema
- Migrations are the hard part: add, dual-write, backfill, switch reads, stop writing, and drop much later
Practice
Attempt each one before opening the solution. Getting it wrong first is how the idea sticks.
Think about it
Plan a rename
The employees table has a column called phone, and you need it to be mobile_number, with the old data preserved. The employees API runs six replicas behind a rolling deployment.
Write the sequence of releases, and say which one you could not easily undo.
Show solution
A rename in one statement is a drop and an add. During a rolling deployment, half the replicas would be running code that references a column that no longer exists, so requests fail for as long as the roll takes.
The sequence: add mobile_number as nullable; deploy code that writes both columns; backfill in batches; deploy code that reads mobile_number; stop writing phone; drop phone in a later release once you are sure.
The step you cannot easily undo is the drop. Everything before it is recoverable by redeploying the previous artifact, because both columns still exist and both are populated.
Step five deserves a note too. Once you stop writing phone, rows created after that point have no value in it, so rolling back to a version that reads phone gives you records with a blank number rather than an error. Partially recoverable is its own category, and it is worth naming when you plan the release.
Try it yourself
Rehearse the rollback
In a non-production environment, deploy a version, then roll back to the previous one using whatever mechanism you would use in production. Time it.
Write down what you needed that you did not have to hand.
Show solution
The common gaps are that the previous artifact was pruned from the registry, nobody recorded which version was running before, and the rollback needed a permission the person on call does not have.
The time matters because it is the number you will quote during an incident. A rollback you believe takes two minutes and actually takes twenty changes the decision about whether to roll back or fix forward.
Rehearsing on an ordinary day is the whole point. The first execution of any procedure is the slowest and the most error-prone, and you get to choose whether that happens now or during an outage.
Knowledge check
Nothing is recorded and there is no score. The explanation appears either way.
Saved in this browser only.