← Blog

Canary releases and dark launches, explained

A canary release ships a new version to a small, random slice of real traffic, then widens it once the metrics hold. A dark launch runs the new code in production too, but hides it from users entirely, often to load-test it before the UI ever appears. Both reduce the risk of a full deploy; they just gate different things.

The name “canary release” comes from the canary miners carried into coal shafts: if toxic gas leaked in, the bird died first and warned the crew. A canary release puts a small cohort of users in that same position, deliberately, so a bad change shows up in metrics before it reaches everyone.

What follows is the real difference between the two, a dark launch that actually shipped at scale, and the cost both carry that a plain deploy never does.

Key Takeaways

  • A canary release ships to a small, random slice of real traffic (often 1-5%) and widens it once metrics hold. The term comes from the coal-mine canary, per Martin Fowler’s definition.
  • A dark launch runs the new code path in production, invisible to users. Facebook used this to test Chat’s backend on real traffic for months before switching the UI on for 70 million users on May 13, 2008.
  • A canary release gates what users see. A dark launch gates what they don’t see. They solve different problems and are often combined.
  • Done with feature flags, a canary release is a weighted variant that widens as metrics hold, and a validator checks the weights sum to 100 before the rollout can merge.
  • The real cost is operational, not risk: staged routing and shadow traffic both add infrastructure a plain deploy never needed.

What is a canary release?

A canary release deploys a new version alongside the old one and routes a small, random sample of traffic to it, measures the result, then grows that sample until the new version carries everyone or gets pulled. Nothing about it requires a feature flag specifically; it’s a traffic-routing strategy that works at the load balancer, the service mesh, or the application layer.

The three moving parts, per Fowler’s description: deploy the new version to infrastructure that takes no traffic yet, route a small cohort to it (random, internal, or by some attribute), then widen the cohort while watching metrics, rolling back by rerouting traffic if something breaks. Multiple versions run at once for the length of the rollout, which is the part that differs from a blue-green deploy, where every request flips to the new version in one cutover.

A canary release answers one question: does this change behave the same way for real users as it did in staging? Staging rarely has production’s traffic shape, data skew, or scale, so some bugs only show up once real requests hit the new code. A canary release finds them on 5% of traffic instead of 100%.

What is a dark launch?

A dark launch runs new code in production on real traffic, but never shows the result to a user. The code path executes, the system absorbs the load, and the team watches for errors and latency, with nothing rendered that a user would notice.

The clearest real case is Facebook’s. Before shipping Chat, Facebook’s engineers wired production pages to open connections to the chat backend, query presence, and simulate message sends, all with no chat UI drawn on the page. Real users’ browsers generated real load on the new infrastructure for months before a single person saw a chat window. When the UI finally switched on for all users on May 13, 2008, the backend had already absorbed production-scale traffic, so the team wasn’t finding out how the system behaved under load at the same moment 70 million people were.

A dark launch is a load test you can’t buy: it’s the only way to find out how new infrastructure handles real traffic shape without waiting for users to find the broken edges for you.

Canary release vs. dark launch: the difference

A canary release changes what some users see. A dark launch changes nothing a user sees at all.

  • What changes. A canary moves a slice of traffic onto new, visible behavior. A dark launch runs new code whose result never reaches a response.
  • What it tests. A canary tests whether the new version is correct and safe for users. A dark launch tests whether new infrastructure survives real production load.
  • Rollback. A canary rolls back by rerouting traffic to the old version. A dark launch rolls back by stopping the shadow call.

The two aren’t mutually exclusive. A common sequence dark-launches a new service to prove it survives production load, then canary-releases the user-facing switch that routes real traffic to it, so the infrastructure risk and the behavior risk get tested separately instead of both landing on one rollout.

Running a canary release with feature flags

A feature flag is one way to implement the routing a canary release needs, without touching the load balancer. The flag’s weight is the cohort size, and widening the canary is editing a number.

---
id: new-pricing-engine
status: active
owner: sam@acme.com
surface: checkout
hypothesis: >
  The new pricing engine returns the same totals without lowering completed checkouts.
variants:
  - id: "off"
    weight: 95
  - id: "on"
    weight: 5
metrics:
  primary: completed_checkout
created: 2026-09-29
---

## Brief

Canary at 5%. Widen to 25% once completed_checkout holds for 48 hours.

That’s a dif flag: a Markdown file with the weights in frontmatter, the rationale in prose below it. Assignment is a hash of the user id, so the same 5% stays in the canary across requests instead of flickering. Widening the canary to 25%, then 100%, is editing the weight and merging, and dif validate checks the weights sum to 100 before that PR can land, so a rollout that only adds up to 90% fails the build instead of silently under-shipping. The .md format covers the rest of the frontmatter, and the CLI reference covers validate and build.

This is the PM-facing side of a canary: the variant and the metric are the whole decision. The engineering side is that the SDK call site doesn’t know it’s in a canary at all; it just calls dif("new-pricing-engine", { off, on }) and gets the on or off branch like any other flag.

Dark-launching a feature behind a flag

A dark launch usually isn’t gating what renders; it’s gating whether a second, unused call happens at all. The pattern is a shadow call next to the real one, with the real path never touched:

const price = calculatePriceV1(cart); // the real, served result

const shadow = dif("pricing-v2-shadow", {
  off: () => {},
  on: () => {
    calculatePriceV2(cart)
      .then((shadowPrice) => logDivergence(price, shadowPrice))
      .catch(logShadowError);
  },
});
shadow();

return price;

The flag controls whether the shadow call fires, nothing about what the user gets back. That’s the opposite framing from a canary release, where the flag decides the user-visible outcome directly. A dark launch behind a flag also makes rollback simple: set the on weight to 0, merge and deploy, and the shadow traffic stops, with zero risk to the response the user actually receives, because that response never depended on the new path.

The tradeoff

Neither technique is free. A canary release needs something that can route a stable, named slice of traffic and hold it there while widening it, which is either a feature-flag SDK, a service mesh, or load-balancer config somebody has to maintain. A dark launch doubles the work for every shadowed request: the real call, the shadow call, and the logic to compare or discard the result, which adds latency and load even though users never see it.

The honest version of this tradeoff is the same one feature flags carry generally: rollback is editing a number and merging, not a dashboard toggle. If a canary at 5% starts failing, the fix is a commit that sets the weight back to 0, so the time to roll back is however long your build and deploy take. For most rollouts that’s fine. For a canary on a payment path where you need a kill switch in under a second, a merge-and-deploy rollback is the wrong tool, and a dashboard-based flag is the right one for that specific flag.

Getting started

Both techniques exist to answer the same question before it becomes an incident: does this work on real traffic? A canary release answers it for user-facing behavior; a dark launch answers it for infrastructure load. You don’t need a platform to start either one, just a way to route or gate a slice of traffic and the discipline to widen it slowly.

With feature flags as the routing mechanism, a canary release is a weighted variant you ramp in a reviewed pull request, validated in CI before it merges. Install the CLI and scaffold a project:

npm install -g @dif.sh/cli
dif init

See what feature flags are for the fundamentals, and feature flags in git for why putting the rollout in a reviewed file, instead of a dashboard, matters once more than one person is widening the canary.

FAQ

What is a canary release in software deployment? A canary release is a deployment strategy that ships a new version to a small, random slice of real traffic, then widens the slice as metrics confirm it’s safe. It catches bugs that only show up under real production conditions, before they reach every user.

What is the difference between a canary release and a dark launch? A canary release controls what some users see; a dark launch controls what no users see. A canary release routes a cohort to new user-facing behavior. A dark launch runs new code silently, usually to test infrastructure under real load.

Is a canary release the same as a feature flag? No. A feature flag is one mechanism you can use to implement a canary release, a weighted variant that widens over time, but canary releases also happen at the load-balancer or service-mesh level with no flag involved.

How is a canary release different from blue-green deployment? Blue-green deployment switches all traffic from the old version to the new one in a single cutover. A canary release shifts traffic gradually, so only a fraction of users are on the new version at any point until the rollout completes.

How do you implement a dark launch? Run the new code path alongside the real one on production traffic, and discard or log its output instead of serving it. Facebook did this for Chat in 2008: the client opened real connections to the new backend with no UI drawn, so the infrastructure absorbed real load before anyone saw the feature.


Sources: Martin Fowler, “CanaryRelease”; Martin Fowler, “Feature Toggles (aka Feature Flags)”; Meta Engineering, “Facebook Chat”