Dark launches: ship it before you turn it on
A dark launch ships a feature to production while keeping it invisible to every real user, so the team can watch the code run under real load before deciding whether anyone should see it. The deploy happens on the normal schedule. The feature does not, because nothing about shipping the code requires turning it on.
The term comes from Facebook, which dark launched Chat to its entire userbase weeks before the feature went visible, to find out whether the backend could carry the load. Most teams run the same move today without the name: a feature flag set to off in production. This post covers how a dark launch differs from a canary release, how to build one with a flag, and the cost it does not advertise.
Key Takeaways
- A dark launch ships the full code path to production with nobody able to see it, so a team can watch real infrastructure load before flipping anything on.
- Facebook coined the term in 2008, dark launching Chat to its entire userbase weeks before the feature went visible, to test whether the backend could take the load.
- The simplest dark launch is a feature flag with
weight: 0on the “on” variant: the code deploys and runs, but no real user is ever assigned it.- GitHub’s open source
scientistlibrary formalizes the same pattern: it runs the new code path on every request, compares it to the old path’s result, and ships only the old result to the user.- The tradeoff: a dark-launched path still queries real data and calls real infrastructure, so it can cause a real incident even though no interface changed.
What a dark launch is
A dark launch deploys the real code for a feature to production and keeps it running with no real user seeing the result. The code is live, and the traffic hitting it can be real. The only thing missing is exposure: nobody is assigned the “on” experience.
That is a different move than hiding code behind a long-lived branch. A branch never reaches production, so it never finds out how the code behaves under a real database, real latency, and real concurrent load. A dark launch does, which is the point: you learn whether the backend holds up before a single person’s screen changes.
Facebook coined the term by shipping a feature nobody could see
Facebook engineer Eugene Letuchy documented the original case in 2008: Chat shipped to every user’s browser weeks before the feature was visible to anyone. The JavaScript ran on every page load, opened real connections, and generated real load on the backend, all while rendering nothing a user could see or click. The team wanted to know if the infrastructure would survive the entire userbase hitting it at once, and the only way to find out was to make it happen for real.
That is the test a staging environment cannot run. Staging does not have your production traffic shape, your production data skew, or your production network topology. A dark launch borrows all three without touching the product surface.
Dark launch vs canary release
A dark launch and a canary release both ship code before fully committing to it, but they test different things. A canary release shows the new code to a small slice of real users (1%, 5%, whatever the team is comfortable with) and watches their metrics: error rate, latency, conversion. A dark launch shows the new code to nobody and watches the system instead: CPU, query count, queue depth.
The two compose. Dark launch a feature at weight: 0 to confirm the backend survives contact with production, then widen the rollout to a canary slice once the system side looks clean, then ramp the rest of the way once the user-facing metrics hold too.
Ship it dark with a feature flag
The mechanism behind a dark launch is almost always the same one behind any staged rollout: a flag where the “on” variant has nobody assigned to it.
---
id: new-feed-ranking
status: active
owner: priya@acme.com
surface: feed
variants:
- id: "off"
weight: 100
- id: "on"
weight: 0
metrics:
primary: feed_load_time_p95
---
## Brief
Dark launch the new ranking service. Everyone stays on "off"; watch
feed_load_time_p95 and the ranking service's error rate for a week
before widening to a canary.
The code behind new-feed-ranking runs in production from the moment this file merges. dif build compiles it into the typed client, and the call site looks the same as any other flag:
if (dif("new-feed-ranking") === "on") {
return rankWithNewService(feed);
}
return rankWithLegacyScoring(feed);
Nobody gets "on" while the weight stays at 0, so the dark launch is just the first rung of the same ramp a canary and a full rollout use later. The .md format and CLI reference cover the rest of what the frontmatter can hold.
Compare results without showing them
A flag at weight: 0 tells you the new path can run. It does not tell you whether the new path is correct. For that, GitHub’s open source scientist library runs both the old and the new code path on every request, compares the two results, logs any mismatch, and returns only the old path’s result to the user. The new path executes for real, against real data, and nobody’s response ever depends on it until the comparison looks clean.
That pairs well with a dark-launched flag: let the “on” variant’s dark traffic run while scientist (or an equivalent comparison you write yourself) checks that the new path agrees with the old one, before you ever let a real response come from it.
The tradeoff: dark code still spends real infrastructure
A dark launch is not free just because nobody sees it. The hidden code path still runs queries, calls downstream services, and consumes CPU and memory on every request it touches. If the new ranking service in the example above makes three extra database calls per request, those calls happen at full production volume the moment the flag merges, whether or not a single user variant is “on.”
That means a dark launch can cause a real incident: a slow query, an exhausted connection pool, a third-party rate limit hit, all while the product looks unchanged to every user-facing dashboard. The fix is to watch system metrics, not user metrics, during the dark phase, and to have a fast way to pull the dark path entirely if it misbehaves. Editing the weight back to 0 does nothing, since it is already there; reverting the merge or setting the flag’s status to paused is what actually stops the code from running.
Getting started
A dark launch answers one question before you ask any other: does this survive production. Ship the code behind a flag at weight: 0, watch the system it touches instead of the users it doesn’t yet reach, and widen to a canary once the backend holds. The term is from 2008; the mechanism, a flag nobody is assigned to, has not changed since.
Install the CLI and gate your next backend change behind a dark launch instead of guessing from staging:
npm install -g @dif.sh/cli
dif init
See feature flags in git for how the rollback and review workflow works once the flag is live, or release trains for how dark launches fit a team that no longer waits for a scheduled ship date.
FAQ
What is a dark launch? A dark launch is a deploy where the new code runs in production but no real user is shown the result. The team watches how the system behaves under real load before deciding whether to expose the feature at all.
Is a dark launch the same as a feature flag? A dark launch is usually implemented with a feature flag, but the two are not identical. The flag is the mechanism; the dark launch is the specific choice to set the “on” variant’s weight to 0 so nobody is assigned it while the code still runs.
How is a dark launch different from a canary release? A dark launch exposes the new code to no real users and measures the system. A canary release exposes it to a small percentage of real users and measures their metrics. Teams often run a dark launch first and move to a canary once the system side looks stable.
Did Facebook actually invent dark launching? Facebook popularized the term in a well known 2008 write-up about shipping Chat to every browser weeks before the feature was visible. The underlying idea, deploying code without releasing the feature, predates the name and is the same thing Martin Fowler calls decoupling deploy from release.
Can a dark launch cause an outage even though nobody can see the feature? Yes. The hidden code path still queries real data and calls real infrastructure, so a slow query or an exhausted connection pool can cause a real incident while every user-facing metric looks normal. Watch system metrics, not just user metrics, during the dark phase.
How do you dark launch something without a dedicated flag tool? Add a flag to your existing config that nobody is assigned to, even one as simple as a hardcoded false behind an environment check, and deploy it. The flag tool adds validation, gradual rollout, and a record of who widened it and when, but the dark launch itself just needs a code path that runs with zero real exposure.