Skip to main content
ANVISoftware Solutions
Lesson 13 of 17Intermediate17 min

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:

 RollingBlue-green
How it worksReplace instances in batches until all are newStand up a full second copy, verify it, then switch traffic
Extra capacity neededOne batch's worthA second full environment, for the duration
Both versions live at onceYes, throughout the rollYes, but only one receives user traffic
Verifying before users arrivePer instance, through the readiness checkFully, against the real environment, before the switch
RollbackRoll the previous version back through the same process. MinutesSwitch traffic back. Seconds, as long as the old environment is still there
Main constraintOld and new must tolerate each other, including the same database schemaCost, and the fact that the database is usually shared between both sides
SuitsMost services, including the employees API. It is the default for a reasonReleases 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-greenCanary
Traffic to the new versionNone, then all of itA few per cent, then more, over minutes or hours
What it protects againstA version that is visibly broken before the switchA version that looks fine and misbehaves under real traffic and real data
NeedsTraffic switching and double capacityTraffic splitting, per-version metrics, and a rule for what counts as bad
If something is wrongEveryone saw it, brieflyA small share of users saw it, for longer
Time to fully releaseOne actionAs long as the ramp takes, which is the cost of the evidence
Poor fit whenYou cannot afford the second environmentTraffic is low. Five per cent of twenty requests an hour tells you nothing, so you are waiting without learning
Deploy the promoted digest, then prove it is healthy before finishing
YAML
  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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

Why are database migrations the hard part of deployment?
The employees API serves about twenty requests an hour from internal staff. Why is a canary release a poor fit?

Saved in this browser only.