Contents

Zero-downtime releases happen when the new version starts alongside the old one, traffic switches only after checks, and the database changes in compatible steps. Three approaches help: blue-green switches between two environments, canary moves a share of users first, and feature flags ship code switched off and enable features separately. A small product usually needs only rolling updates and flags.

Here is how each approach works and how not to break the database during a deploy.

Why do releases cause downtime?

Releases cause downtime when the application is stopped before the new version starts, when the new version is incompatible with the current database schema, or when a bug is discovered only after it hits every user. Each cause has its own fix.

Typical sources of downtime:

  • stopping the only application instance for the update;
  • a database migration that locks a table or breaks the old code;
  • clearing and warming caches, during which the site is slow;
  • a bug in the new version that must be rolled back under load;
  • background jobs old workers cannot process after a format change.

How do blue-green, canary and feature flags differ?

Blue-green, canary and feature flags reduce risk in different ways: the first allows instant switching and rollback, the second limits how many users a bug reaches, the third separates deploying code from turning a feature on.

Approach How it works Pros What it requires
Rolling update Instances are updated one at a time Simple, built into orchestrators Several instances, version compatibility
Blue-green Two environments; traffic switches all at once Instant rollback Double resources during the release
Canary The new version first gets a small share of traffic A bug affects few users Traffic routing and metrics
Feature flags Code ships switched off; the feature is enabled separately Turn on and off without a release Discipline in removing old flags

A grey monitor on a desk Photo: Unsplash

How do you change the database without downtime?

Change the database without downtime using the expand–migrate–contract pattern: first add what is new and compatible with the old code, then switch the code, and only afterwards remove what is old. At every moment both the old and new application versions work with the same schema.

Example: renaming the phone column to phone_number.

Release 1 — expand
  add a nullable phone_number column
  code writes to both columns, reads from phone
  a background job copies existing values

Release 2 — switch
  code reads and writes phone_number

Release 3 — contract
  drop the phone column

Rules for safe migrations

  1. Never drop or rename columns in the same release that changes the code.
  2. Add new columns as nullable or with a default value.
  3. Create indexes on large tables without blocking writes — see PostgreSQL indexes.
  4. Move large amounts of data with background jobs in batches.
  5. Test migrations on a copy with production-sized data.

How do you use feature flags?

Feature flags let you ship an unfinished or risky feature switched off and enable it separately — first for the team, then for some users, then for everyone. If something goes wrong, the feature is turned off in seconds without a new release.

if (Feature::active('new-checkout', $user)) {
    return $this->newCheckout($cart);
}

return $this->legacyCheckout($cart);

What matters:

  • roll features out gradually while watching metrics and errors;
  • remove the flag and the old code path once fully enabled, or the code fills with conditions;
  • do not use flags as a substitute for permissions and settings.

Where should a small team start?

A small team should start with automated deployments, several application instances with rolling updates, readiness checks before taking traffic and compatible migrations. Blue-green and canary come later, when the cost of errors and the load justify them.

The plan:

  • automate deployment in the pipeline — see CI/CD on GitHub Actions;
  • run at least two application instances and replace them one at a time;
  • add a readiness endpoint so the load balancer never sends traffic to an instance that is not ready;
  • write migrations using expand–migrate–contract;
  • prepare a fast rollback and test it.

In short

  • Release downtime comes from stopping the app, incompatible migrations and bugs that hit every user.
  • Rolling updates are the baseline; blue-green gives instant rollback; canary limits a bug's reach.
  • Feature flags separate deploying code from enabling a feature.
  • Change the database with expand–migrate–contract, never in one step.
  • Start with automated deploys, two instances, readiness checks and compatible migrations.

FAQ

  • Is Kubernetes needed for zero-downtime releases?

    No. Rolling updates work on ordinary servers behind a load balancer and on managed container services. Kubernetes makes it easier but is not required.

  • How do you roll back a release that included a migration?

    If the migration is compatible with the previous code version, simply redeploy the old application — the schema suits both. That is why destructive schema changes go into a separate later release.

  • How many feature flags can be kept at once?

    As many as the team can keep track of. Every flag needs an owner and a removal date, or the code fills with conditions nobody dares to remove.