Deployment Strategies
Shipping a change is a risk decision. This page compares recreate, rolling, blue/green, canary, shadow and A/B releases, shows how feature flags separate deploy from release, and explains the part that causes real outages: changing databases, APIs and messages while two versions run at once. It ends with progressive-delivery tooling, a release checklist and the rollback playbook.
Why Deployment Strategy Matters, and Deploy vs Release
Replacing running software with a new version is the riskiest routine thing a team does. The strategy you choose decides how many users a bad version can hurt, how long they are hurt, how much extra capacity you pay for, and how quickly you can undo it. A good strategy turns "we shipped and hoped" into "we shipped to 1 percent, watched the numbers, and promoted".
The first idea to internalise is the split between two events that most teams treat as one:
| Deploy | Release | |
|---|---|---|
| Meaning | A new version of the code is running in production | Users are exposed to the new behaviour |
| Controlled by | Pipeline, orchestrator (Kubernetes, ECS, CodeDeploy) | Traffic routing, or a feature flag |
| Typical risk | Crash on start, bad config, missing dependency, slow start | Wrong behaviour, performance regression, data issue |
| Undo by | Roll back to the previous version | Turn the flag off or shift traffic back |
Every strategy in this page is a different answer to the same four questions. Keep them in mind as you read:
- Exposure: what fraction of users or requests meets the new version first?
- Capacity: do old and new versions run side by side, and does that need extra servers?
- Compatibility: can old and new versions run at the same time against the same database and the same messages? (Chapters 6 and 7: this is where most real outages come from.)
- Reversal: how long and how risky is it to go back?
This page builds on page 05 CI/CD Pipelines (the pipeline that triggers deployments), page 03 Kubernetes Core and page 04 Kubernetes Operations (the primitives), and page 07 Observability (the metrics that tell you whether a rollout is healthy).
Recreate and Rolling Update (and Kubernetes Deployments)
Stop everything that runs the old version, then start the new version. It is the simplest strategy and the only honest one when two versions cannot coexist: a single-instance application, a schema change that breaks the old code, a licence that allows one instance. The price is downtime equal to shutdown plus startup plus warm-up. Kubernetes supports it as strategy.type: Recreate: all old pods are terminated before any new pod is created.
Replace instances gradually: start some new ones, wait until they are healthy, stop some old ones, repeat. No downtime and no extra environment, but for a while both versions serve traffic, so they must be compatible with each other and with the data. A rolling update has no built-in notion of "watch the error rate": its safety check is only the health probe.
A Kubernetes Deployment supports exactly two strategy.type values: RollingUpdate (the default) and Recreate. For RollingUpdate, two fields set the pace. Per the Kubernetes documentation both default to 25 percent, and each can be an absolute number or a percentage:
| Field | Meaning | Effect |
|---|---|---|
maxSurge | How many pods above the desired count may exist during the update | Higher = faster rollout, more temporary capacity needed |
maxUnavailable | How many pods below the desired count may be unavailable | 0 keeps full capacity; needs maxSurge above 0 or the rollout cannot start |
minReadySeconds | A new pod must be ready this long before it counts as available | Catches pods that pass the probe and then crash |
progressDeadlineSeconds | How long to wait for progress before the Deployment is marked as failed | Does not roll back automatically; it sets a condition (ProgressDeadlineExceeded) |
The /actuator/health/readiness path is the Spring Boot readiness probe group, available when probes are enabled (they are enabled automatically when Boot detects Kubernetes; management.endpoint.health.probes.enabled=true forces it). The preStop sleep gives the endpoints controller and load balancers time to stop sending new connections before the process gets SIGTERM, which is a standard fix for errors during rollouts.
- What you see: the rollout finishes in seconds and users get connection errors or 503s. Without a readiness probe a pod is "ready" as soon as its container starts, long before Spring has finished starting, so traffic goes to pods that cannot serve it, and the old pods are already gone. Fix: a readiness probe that checks the application (not just the port), plus
minReadySeconds, plus a graceful shutdown (server.shutdown=gracefulin Spring Boot).
- A rolling update shifts traffic in proportion to pod counts, as fast as probes pass, and nothing looks at error rates. With 4 replicas the first new pod receives about 25 percent of traffic almost immediately. If you want "1 percent, watch, then more", you need weighted routing (chapter 4), not a plain Deployment.
Blue/Green Deployment
Run two complete production-like environments: blue (the current version, serving users) and green (the new version, idle for users). Deploy to green, test it with real infrastructure and synthetic or internal traffic, then switch the router so green becomes live. Blue stays up for a while as the instant rollback target, then is retired or becomes the next green.
| Aspect | Blue/green |
|---|---|
| Exposure | All users at once at cutover (unless combined with a gradual weight shift) |
| Downtime | None when the router flips cleanly and sessions/state are handled |
| Rollback speed | Fastest of all strategies: flip the router back |
| Capacity cost | Up to 2x for the cutover window (or permanently if blue is kept warm) |
| Pre-release testing | Excellent: green runs on production infrastructure before any user sees it |
| Hard part | The shared database and any state: both colours usually use the same data store, so schema and messages must stay compatible (chapters 6 and 7) |
| Platform | Router flip |
|---|---|
| Kubernetes | Two Deployments (shop-blue, shop-green) and a Service whose selector you change; or an Ingress/Gateway backend swap; Argo Rollouts automates this with activeService and previewService |
| AWS ALB | Two target groups; change the listener rule's forward action to the other target group (chapter 8) |
| AWS CodeDeploy | Blue/green deployment type for EC2/ASG and ECS: it provisions the replacement, shifts traffic, then terminates the original after a wait |
| DNS | Change a weighted or simple record: slow and unreliable because of TTLs and client caches; avoid as the only mechanism |
- What you see: after cutover green works, but the moment you flip back to blue it fails because the migration that green needed already ran. The "instant rollback" is a lie. Fix: make every schema change backward compatible with the previous application version (expand/contract, chapter 6), so either colour can run against the current schema.
- A cold green (empty caches, cold JIT, empty connection pools) can look slow or fail health checks under real load at cutover. Send it synthetic or mirrored traffic first, or shift traffic in steps. Argo Rollouts calls the pre-cutover phase the preview, and supports
prePromotionAnalysisfor automated checks before the switch.
Canary Releases, and Shadow (Mirrored) Traffic
Named after the canary in the coal mine: send a small share of real traffic to the new version, compare its health with the old version, and only then increase the share. If metrics degrade, the share drops back to zero and only a few users noticed. The blast radius is chosen by you (1 percent, 5 percent) instead of by the size of the fleet.
| Aspect | Canary |
|---|---|
| Exposure | Small and growing; you pick the steps (for example 1, 10, 50, 100 percent) |
| Downtime | None |
| Rollback speed | Fast: set the weight back to 0 (and the old version never stopped) |
| Capacity cost | Small: roughly the canary share plus headroom |
| Needs | Fine-grained traffic split and good metrics. Without observability a canary is just a slow rollout |
| Hard part | Enough traffic for the comparison to mean anything; sticky sessions; both versions on the same data |
- Errors: HTTP 5xx ratio of the canary against the baseline (page 07: RED metrics and the PromQL error ratio).
- Latency: p95/p99 of the canary against the baseline, from a histogram.
- Saturation: CPU, memory, GC pauses, connection pools of the canary pods.
- Business metrics: orders per minute, payment success, login rate. A version can be technically healthy and commercially broken.
- Enough samples: require a minimum request count per step, or a 1 percent canary at low traffic proves nothing.
In a shadow deployment real requests are copied to the new version, whose responses are discarded. Users only ever see the old version, so a bug in the new one hurts nobody, but the new version is exercised with genuine production traffic shapes. It is ideal for validating a rewrite, a new database engine or a performance change. Mirroring is supported by service meshes and gateways such as Istio, and Flagger documents blue/green mirroring for Istio and Gateway API.
- A mirrored
POST /paymentscharges the card twice, a mirroredPOST /orderswrites to the real database, an email gets sent twice. The shadow must be isolated: read-only operations only, or point it at a sandbox database and stubs for payment, email and other third parties. Always ask "what does this request do?" before mirroring it.
| Rolling | Canary | Shadow | |
|---|---|---|---|
| Users see the new version? | Yes, progressively by pod count | Yes, a chosen share | No |
| Compares old vs new on live traffic? | No | Yes | Yes (response diffing, latency) |
| Risk to users | Moderate | Small | None, if side effects are isolated |
| Extra complexity | None | Weighted routing + metrics gates | Mirroring + isolation of side effects |
A/B Testing and Feature Flags
A canary asks "is the new version healthy?" and routes by percentage of traffic. An A/B test asks "which version produces a better outcome?" (conversion, retention) and routes by user attributes or cohorts (a header, a cookie, a user id hash, a country), keeping each user on the same variant for the whole experiment. It needs statistical analysis, runs for days or weeks, and is owned by product as much as by engineering. The routing machinery overlaps with canaries (Flagger documents A/B testing on HTTP headers and cookies), but the goal and the success criteria differ.
| Canary | A/B test | |
|---|---|---|
| Question | Is it safe? | Is it better? |
| Split by | Percent of requests | User cohort, header, cookie, hashed user id |
| User experience | May vary per request | Must be sticky per user |
| Duration | Minutes to hours | Days to weeks |
| Decision by | Health metrics, automated | Statistics on business metrics, human |
A feature flag (feature toggle) is a runtime switch around a code path. The code ships to production disabled; a configuration change enables it for chosen users. That is the second half of the idea in chapter 1: deploy is a technical event, release is a decision that can be made later, per user group, and undone in seconds without a deployment.
A property that needs a restart to change is a deploy-time flag, which defeats the purpose. Dedicated flag systems (commercial products such as LaunchDarkly, open-source ones such as Unleash, or a homegrown service) evaluate flags at request time with user context, so you can enable a feature for employees, then 1 percent of users, then everyone. OpenFeature is a CNCF project that defines a vendor-neutral flag evaluation API, so application code does not depend on one vendor.
| Flag type | Lifetime | Purpose | Example |
|---|---|---|---|
| Release flag | Days to weeks, then delete | Hide unfinished or risky work in main (trunk-based development) | new checkout flow |
| Experiment flag | Duration of the test | A/B variants | two button designs |
| Ops flag / kill switch | Long-lived | Turn off an expensive or risky feature under load | disable recommendations during an incident |
| Permission flag | Long-lived | Entitlements per customer or plan | beta features for one tenant |
- Release flags that stay forever turn the code into a maze of untested combinations (n flags means up to 2 to the n paths). Give every release flag an owner and a removal date, remove the flag and the dead branch once the rollout is complete, and keep a flag inventory. An outdated flag turned on by accident can cause an outage.
- Test both sides of the flag in CI.
- Make the default (when the flag service is unreachable) the safe one, and cache the last known values.
- A flag does not remove the need for compatible schemas: the flag is evaluated per request, so the code for both branches must work with the current database.
- Log or tag the flag state on traces and metrics so you can tell which variant caused an issue.
Database Migrations: Expand and Contract
Application code is easy to replace and easy to roll back: it is stateless, and the old version is one kubectl rollout undo away. The database is not. It is shared by every running version, it is hard to undo, and during any rolling, canary or blue/green deploy two versions of the code run against one schema at the same time. So the schema must always be compatible with both the version being replaced and the version replacing it.
The technique is expand and contract, also called parallel change: make a breaking change in three compatible stages instead of one incompatible step.
| Phase | What changes | Safe because |
|---|---|---|
| Expand | Add the new structure (column, table, index) alongside the old, nullable or with a default | Old code ignores what it does not know about |
| Migrate | Deploy code that writes both old and new; backfill existing rows; then switch reads to the new structure | Each step works with the previous app version |
| Contract | Remove the old structure once no running version uses it | Nothing depends on it; the only irreversible step, done last |
I ran this sequence against an in-memory SQLite database: after V2 the new columns were NULL, after V3 they held Ada/Lovelace and Alan/Turing, and after V4 the old column was gone and the rows were intact. SQLite stands in only to check the statements; substr and instr are SQLite functions, so on PostgreSQL you would use split_part and similar, and other engines have their own string functions.
Both tools version the schema as code, apply pending changes in order at deploy time, and record what was applied in a table so each change runs exactly once.
| Flyway | Liquibase | |
|---|---|---|
| Change format | Plain SQL files named V<version>__<description>.sql, plus repeatable R__ files; also Java migrations | Changelog files in XML, YAML, JSON or SQL, made of changeSets identified by id + author + file |
| History table | flyway_schema_history | DATABASECHANGELOG (and DATABASECHANGELOGLOCK) |
| Spring Boot | Auto-runs at startup when flyway-core (plus the database module for your DB in recent Flyway versions) is on the classpath; spring.flyway.* properties | Auto-runs at startup when liquibase-core is on the classpath; spring.liquibase.* properties |
| Rollback | Undo migrations are a paid-edition feature; the common practice is roll forward with a new migration | Supports rollback tags and rollback blocks per changeSet |
| Strengths | Simple, SQL-first | Database-agnostic abstractions, preconditions, contexts and labels |
- With 20 pods starting at once, 20 instances race to migrate. The tools take a lock so only one runs, but the others block, startup times balloon, and a long migration makes Kubernetes kill pods for failing probes. Safer: run migrations as a separate pipeline step or Kubernetes Job before rolling out the new version (the expand step is compatible with the old version, so it can go first), and keep long data backfills out of the deploy path entirely.
| Operation | Risk on a big, busy table | Safer approach |
|---|---|---|
| Add a nullable column | Low | Fine. On PostgreSQL 11 and later adding a column with a constant default is also cheap |
Add NOT NULL to an existing column | Full scan and lock while checking | Add a check constraint as not-validated, backfill, validate, then set NOT NULL (database-specific; read your DB docs) |
| Create an index | Blocks writes on some engines | Use the online option (CREATE INDEX CONCURRENTLY on PostgreSQL, which cannot run inside a transaction) |
| Rename or drop a column | Breaks every running old version immediately | Expand/contract: add new, dual write, migrate, remove last |
| Change a column type | Rewrites the table, long lock | Add a new column of the new type, copy in batches, switch, drop old |
| Backfill millions of rows in one statement | Long transaction, replication lag, lock contention | Batches of a few thousand rows with pauses, as a background job |
Backward-Compatible APIs and Messages; Rollback vs Roll-Forward
During any gradual deploy, clients and servers of different versions talk to each other. A caller on v1 hits a server on v2, a v2 service calls a v1 service, a message written by v2 is read by a consumer still on v1. The rules for staying safe are old and consistent.
| Change | Backward compatible? | Note |
|---|---|---|
| Add an optional response field | Yes | Clients must ignore unknown fields (Spring Boot configures Jackson to not fail on unknown properties by default) |
| Add an optional request field/parameter | Yes | Server must tolerate its absence |
| Add a required request field | No | Old clients fail validation. Make it optional with a default first |
| Remove or rename a response field | No | Add the new one, keep both, deprecate, remove after all consumers moved |
| Change a field's type or meaning | No | New field name instead |
| Tighten validation | No | Can reject requests that used to succeed |
| Add an enum value | Risky | Clients with exhaustive switches break; document an "unknown" fallback |
Follow the robustness principle in clients (be liberal in what you accept) and treat the contract as additive-only. Deploy order matters when a contract does change: for a new optional capability deploy the provider first, then the consumers; when removing, stop the consumers from using it first, then remove it from the provider.
Messages are harder than HTTP because they outlive the deployment: events written by v2 sit in a Kafka topic or queue and are read later, possibly by v1, possibly in a replay months later. Use a schema format with explicit evolution rules (Avro, Protobuf, JSON Schema with a registry) and check compatibility in the build. In schema-registry terms: backward compatible means new readers can read old data (add optional fields with defaults); forward compatible means old readers can read new data; full means both. When consumers are upgraded before producers, backward compatibility is enough; when producers may be upgraded first, forward compatibility is needed; when you cannot control the order, require full. See page 07 Event-Driven Patterns and page 08 Reliability & Operations in the Messaging section.
- What you see: after a producer deploy, a consumer enters a retry loop or a dead-letter queue floods. Fix: tolerant readers (ignore unknown fields, log and skip unknown event types), and deploy consumers that understand the new format before any producer emits it.
| Roll back | Roll forward | |
|---|---|---|
| What | Redeploy the previous known-good version | Fix the problem and deploy a new version |
| Speed | Fast if the previous version is still around (blue/green, canary weight to 0, rollout undo) | Bounded by how fast you can write, review and ship a fix |
| Works when | The change is compatible with the old version: no incompatible schema or message changes happened | Data has already moved on, an irreversible migration ran, or the bug is tiny and a fix is quicker |
| Risk | Old version meets data written by the new version | Shipping an untested fix under pressure |
- Default to rollback for application changes: it is the fastest way to stop the bleeding, and with expand/contract it is always available until the contract step.
- Plan for roll-forward when data is involved: destructive migrations and one-way data conversions cannot be undone by redeploying code.
- Whichever you choose, decide before the rollout (the release checklist in chapter 9 asks for it) and rehearse it. Deciding under incident pressure is how mistakes happen.
- Keep the previous artefact and its configuration available, and keep build artefacts immutable: roll back to the same image digest you ran before, not "rebuild v1".
Progressive Delivery: Argo Rollouts, Flagger, and AWS Options
Progressive delivery automates canary and blue/green: a controller shifts traffic in steps, queries your metrics at each step, and promotes or aborts without a human watching. Two widely used Kubernetes controllers do this.
| Argo Rollouts | Flagger | |
|---|---|---|
| Part of | Argo project | Flux project (CNCF) |
| How you use it | Replace Deployment with a Rollout resource (argoproj.io/v1alpha1), or reference an existing Deployment | Keep your Deployment; add a Canary resource (flagger.app/v1beta1) that targets it |
| Strategies | Canary (with setWeight, pause, analysis steps) and BlueGreen (activeService, previewService, autoPromotionEnabled) | Canary releases, A/B testing (headers and cookies), blue/green (traffic switching), blue/green mirroring |
| Traffic shaping | Pod-ratio weights on its own; precise weights through ingress or mesh integrations | Needs a supported mesh, ingress or Gateway API implementation for weighted canaries; its Kubernetes CNI option supports blue/green only (see the Flagger deployment-strategies table for the current matrix) |
| Metric gates | AnalysisTemplate / AnalysisRun with providers such as Prometheus | metrics in the Canary spec: built-in request-success-rate and request-duration, plus custom Prometheus queries and webhooks |
| CLI / UI | kubectl argo rollouts plugin and dashboard | Works with Flux and Grafana dashboards |
Read the steps top to bottom: send 10 percent of traffic to the new version, wait five minutes, run the analysis (five measurements one minute apart, tolerating at most one failure), then go to 50 percent, wait ten minutes, then 100 percent. If the analysis fails, the controller aborts and returns traffic to the stable version. The result[0] syntax is how Argo Rollouts reads the first element of a Prometheus vector result. The query is the same error-ratio pattern from page 07, parameterised to the application label. Without a traffic-routing integration Argo Rollouts approximates the weights using the ratio of canary to stable pods.
Flagger raises the canary weight by stepWeight every interval until maxWeight, checking the metrics each time; after threshold failed checks it rolls back. The thresholds above mean "success rate at least 99 percent, request duration at most 500" (Flagger documents the duration threshold in milliseconds for its built-in metric).
- A metric query that returns no data because the label is wrong, or a gate checked over a window so wide that a bad canary is diluted, passes silently. What you see: a broken version is promoted "successfully". Fix: test the gate by deliberately deploying a bad version in staging, make "no data" a failure, require a minimum request count, and compare the canary with the baseline over the same period.
| Option | How it shifts traffic | Notes (AWS documentation) |
|---|---|---|
| ALB weighted target groups | A listener rule with a forward action listing several target groups, each with a weight from 0 to 999; requests are distributed in proportion to the weights | Primitive for blue/green and canary on EC2, ECS and EKS: change the weights step by step, from your pipeline or a script |
| AWS CodeDeploy | Deployment types for EC2/on-premises (in-place and blue/green), Lambda and ECS (traffic shifting) | Predefined ECS configurations include CodeDeployDefault.ECSCanary10Percent5Minutes (10 percent first, the rest five minutes later), CodeDeployDefault.ECSLinear10PercentEvery1Minutes and CodeDeployDefault.ECSAllAtOnce; Lambda has analogous canary, linear and all-at-once configurations. Attach CloudWatch alarms to trigger automatic rollback |
| ECS / EKS rolling | ECS service rolling updates (minimum healthy percent and maximum percent) and Kubernetes RollingUpdate | Same trade-offs as chapter 2 |
| Lambda aliases | Alias with weighted routing between two versions | CodeDeploy automates the weight shifts |
- Whatever the tool, a traffic shift is only as safe as the health signal behind it. Use a readiness check that proves the app can serve (dependencies reachable, caches warm if needed), not a liveness-style "process is up" check; on the ALB set the health check path and thresholds, and the deregistration delay so that connections drain before an instance is removed.
Choosing a Strategy, Release Checklist, Verification and Rollback
| Strategy | Exposure of a bad version | Downtime | Rollback speed | Extra capacity | Complexity | Best for |
|---|---|---|---|---|---|---|
| Recreate | All users, with an outage | Yes | Slow (redeploy) | None | Lowest | Dev/test, single-instance, incompatible versions, maintenance windows |
| Rolling | Grows as pods roll; no metric gate | No | Medium (roll back the same way) | Small (maxSurge) | Low | Default for stateless services with compatible versions |
| Blue/green | All users at cutover | No | Fastest (flip back) | Up to 2x for the window | Medium | Big-bang switches, risky releases needing pre-release testing, strict rollback needs |
| Canary | Small, chosen share | No | Fast (weight to 0) | Small | Medium-high (weighted routing, metric gates) | High-traffic services with good metrics |
| Shadow | None (responses discarded) | No | n/a (nothing released) | Roughly 2x for mirrored load | High (side-effect isolation) | Validating rewrites and migrations before real exposure |
| A/B test | A cohort, for an experiment | No | Fast (flag/route) | Small | High (experiment platform, statistics) | Product experiments |
| Feature flag | Whoever the flag targets | No | Seconds (flag off) | None | Medium (flag platform, hygiene) | Decoupling release from deploy; combine with any of the above |
This table compares qualitative trade-offs and is not a benchmark: real cost and speed depend on your platform and tooling. Strategies combine well: a common mature setup is a rolling or canary deploy with the new code behind a feature flag, over an expand/contract schema, gated by SLO metrics.
| If you need... | Choose |
|---|---|
| Simplicity, stateless service, backwards-compatible change | Rolling update with probes |
| Instant, tested rollback, and you can afford the capacity | Blue/green |
| Minimal blast radius with data to prove health | Canary with automated analysis |
| To test a rewrite on real traffic without risk | Shadow, then canary |
| To start a risky feature for a few users and stop it instantly | Feature flag |
| To change the schema | Expand/contract, whatever the strategy |
| Before | Check |
|---|---|
| Change | Backward compatible with the previous version (API, messages, database)? Migrations are expand-only? |
| Artefact | Built once, immutable, tagged with the commit; the same image runs in staging and prod |
| Tests | CI green, including integration tests against the migrated schema |
| Configuration | New settings and secrets exist in the target environment; defaults are safe |
| Observability | Dashboard and alerts for the service are working; the new code emits the metrics you will check |
| Capacity | Room for surge or the second environment; autoscaling limits checked |
| Rollback plan | Written down: which command, who decides, what the data impact is. Previous version still deployable |
| Timing | Not Friday evening; people available; no conflicting release or maintenance |
| Communication | Support and dependent teams informed; status page ready if risk is high |
- Automated smoke tests against the new version immediately: login, one read, one write, one dependency call.
- Watch the golden signals (page 07) for the new version for a defined bake time: error ratio, p95/p99 latency, saturation, and business KPIs, compared with the baseline.
- Check logs for new error signatures and exceptions that did not exist before; check traces for new slow spans.
- Confirm the deploy is real: an exposed build-info endpoint (
/actuator/infowith git commit) or a version metric proves which version answers. - Verify dependencies: queue depth, consumer lag, database connection and slow-query metrics.
- Close the loop: only then promote, scale down the old version, and remove the flag or old environment.
- Changing code and schema incompatibly in one release; no readiness probe; rolling out without a rollback plan; a gate that never fails; mirroring requests with side effects; leaving release flags in forever; deploying on Friday evening; running long migrations at app start; different artefacts in staging and production; "fixing forward" blindly during an incident when a rollback would be faster.
Questions You Should Be Able to Answer
Crisp answers for deployment-strategy questions.
- Q: Compare rolling, blue/green and canary deployments.
Rolling replaces instances gradually using the same capacity but with no metric-based gate and mixed versions for a while. Blue/green runs a full second environment and flips traffic at once: fastest rollback, higher cost. Canary sends a small, growing share of traffic to the new version and promotes based on metrics: smallest blast radius, needs weighted routing and good observability. - Q: What is the difference between deployment and release?
Deploy puts new code into production; release exposes it to users. Feature flags and traffic routing let you separate them, so a risky feature can ship dark, be enabled gradually and be switched off in seconds. - Q: How does a Kubernetes rolling update work, and which settings control it?
The Deployment scales a new ReplicaSet up and the old one down, governed bymaxSurgeandmaxUnavailable(both default to 25 percent), and new pods only count once their readiness probe passes (plusminReadySeconds).kubectl rollout undoreturns to the previous ReplicaSet. - Q: A rolling update caused errors for a few seconds even though the pods were healthy. Why?
Typical causes: no or weak readiness probe, no graceful shutdown (in-flight requests dropped when SIGTERM arrives), load balancers still sending traffic to a terminating pod (fix with apreStopdelay andserver.shutdown=graceful), or too aggressivemaxUnavailable. - Q: What is expand and contract?
A pattern for making breaking schema or API changes safely: expand by adding the new structure alongside the old, migrate by dual-writing, backfilling and switching reads, then contract by removing the old structure. Every step is compatible with the running version and the previous one, so rollbacks stay safe until the final destructive step. - Q: How would you rename a database column with zero downtime?
Add the new column (nullable); deploy code that writes both; backfill existing rows in batches; deploy code that reads the new column; once no version uses the old one, drop it in a later release. Never rename in place while two app versions run. - Q: Why can blue/green rollback be unsafe?
Both environments share the database. If green ran an incompatible migration, flipping back to blue runs old code against a new schema. Rollback is only instant if the schema stays compatible with both versions. - Q: Flyway vs Liquibase?
Both version schema changes and track applied ones in a history table. Flyway is SQL-first with versioned files; Liquibase uses changesets in XML/YAML/JSON/SQL, with preconditions and rollback blocks. In practice teams roll forward rather than roll back. - Q: Rollback or roll forward?
Roll back application changes first when it is safe, because it is fastest. Roll forward when data or schema has moved on irreversibly or when a tiny fix is quicker. Decide the plan before the release. - Q: How do you make API and message changes backward compatible?
Additive only: new optional fields, tolerant readers that ignore unknown fields, deprecate before removing, version when you must break, and schema-registry compatibility checks for events. Deploy providers before consumers for additions. - Q: What do Argo Rollouts and Flagger do?
They automate progressive delivery on Kubernetes: shift traffic in steps, evaluate metrics (typically from Prometheus) at each step, and promote or abort. Argo Rollouts replaces Deployment with a Rollout resource; Flagger watches an existing Deployment through a Canary resource. - Q: How do you do canary on AWS?
ALB listener rules with weighted target groups that your pipeline adjusts step by step, or CodeDeploy traffic-shifting configurations such asCodeDeployDefault.ECSCanary10Percent5Minutesfor ECS and canary configurations for Lambda, with CloudWatch alarms triggering automatic rollback. - Q: Canary vs A/B test?
A canary checks the health of a new version on a share of requests. An A/B test compares business outcomes between sticky user cohorts. Similar routing, different goal and duration. - Q: What is shadow traffic and what is the danger?
Copying production requests to a new version while discarding its responses, to test it on real traffic with no user impact. The danger is side effects (payments, emails, writes), so the shadow must be isolated. - Q: What would you check right after a deploy?
Smoke tests, version endpoint, golden signals versus baseline for a bake time, new error signatures in logs, dependency and queue health, and business KPIs, before promoting and cleaning up.
Cheat Sheet and Recap
| Strategy / concept | One line | Watch out for |
|---|---|---|
| Recreate | Stop old, start new | Downtime |
| Rolling update | Replace gradually; maxSurge/maxUnavailable (default 25%) | Mixed versions; needs readiness probe |
| Blue/green | Two environments, flip the router | Shared database; 2x capacity |
| Canary | Small share first, metrics gate each step | Needs weighted routing and enough traffic |
| Shadow | Mirror requests, drop responses | Side effects |
| A/B test | Sticky cohorts, business metric | Statistics, duration |
| Feature flag | Deploy dark, release by config | Flag debt; safe defaults |
| Expand/contract | Add, migrate, remove: three compatible steps | Never rename or drop in one step |
| Flyway / Liquibase | Versioned schema as code | Do not migrate on 20 pods at once |
| Rollback vs roll-forward | Rollback app, roll forward data | Decide before the release |
| Argo Rollouts / Flagger | Automated canary with metric analysis | Gates that cannot fail |
| AWS | ALB weighted target groups, CodeDeploy traffic shifting | Health check path and deregistration delay |
- Deploy and release are different events. Separating them with flags and routing is the biggest safety lever you have.
- Every gradual strategy runs two versions at once, so code, APIs, messages and the database must be compatible with both.
- Use expand/contract for any breaking schema change, one deployable step at a time; the contract step is the only irreversible one.
- Pick the simplest strategy that fits the risk: rolling by default, canary for high-traffic or risky changes, blue/green for fast rollback.
- Automate the verdict: gate each step on error ratio, latency and business metrics compared with the baseline, and make gates able to fail.
- Rollback first, diagnose second during an incident, while it is still possible; plan roll-forward for data changes.
Checked on 2026-10-31: Kubernetes Deployment documentation (strategy types, maxSurge and maxUnavailable defaults of 25 percent, Recreate behaviour); Argo Rollouts documentation (canary steps, BlueGreen fields, AnalysisTemplate with Prometheus provider); Flagger documentation (Canary resource, supported strategies, metric thresholds); AWS documentation (ALB weighted target groups with weights 0 to 999, CodeDeploy predefined configurations). The expand/contract statements were run against SQLite 3.45 only. The Kubernetes, Argo Rollouts and Flagger manifests were parsed as YAML but not applied: no cluster was available. Controller versions move quickly; check the current CRD versions before use.
- 07 Observability - the metrics and burn-rate alerts that gate a rollout.
- 05 CI/CD Pipelines - the pipeline that runs these strategies.
- 09 Interview Q&A - the zero-downtime-with-DB-changes scenario in full.
- AWS - ALB, ECS and CodeDeploy in depth.