What is the difference between a dark launch and a kill switch?
A dark launch and a kill switch run on the same underlying tool: a feature flag that turns code on or off at runtime without a redeploy. That shared foundation is why teams mix them up. But they solve different problems, they live on different timelines, and they get retired in very different ways.
The short version:
- A dark launch puts new code into production while keeping it hidden from most or all users. It answers the question, "does this hold up under real traffic before anyone depends on it?"
- A kill switch is a flag you keep in place so you can disable a feature or a dependency in seconds when it starts causing problems. It answers the question, "how fast can we stop the damage?"
What a dark launch does
In a dark launch, you deploy the code to production and leave the flag off for everyone except the audience you choose. That audience might be your own team, a set of internal accounts, or a small group of customers who never get told the feature is new.
The point is to test against production conditions: real data volumes, real latency, real edge cases in user records that never show up in staging. You can also run a dark launch with no user-facing exposure at all. A common pattern is to route production traffic through new code, throw away the result, and compare it against the output of the old code to see whether the two agree and how much slower the new path runs.
Once the results look good, you widen exposure with a gradual rollout, where you enable the feature for 1% of users, then 10%, then 50%, checking metrics at each step. In Unleash, that targeting is handled with activation strategies and constraints, so you can go from "just my team" to "everyone in the EU" to "all users" by editing the flag, not the code.
Dark launching is closely related to a canary release, and the terms get used loosely. The rough distinction: a canary release exposes a small slice of users to a new version of the service, while a dark launch hides a specific feature behind a flag inside the version everyone is already running.
What a kill switch does
A kill switch is an operational flag, and it usually works backwards from a release flag. You wrap a component that has known weak spots, and your application assumes the component is working as long as the flag stays off. When the flag goes on, the feature turns off or degrades to a fallback.
Why invert the logic? Because it makes the failure mode safe. If your flag service becomes unreachable and your SDK falls back to the default value, an inverted flag defaults to "everything is normal" instead of "kill everything." Unleash SDKs cache flag configuration locally for the same reason, so your application keeps running even if it cannot reach the Unleash instance.
Typical uses:
- Turning off a recommendation engine when the model service starts timing out
- Disabling a third-party integration during a vendor outage
- Dropping an expensive personalization step during a traffic spike so the rest of the page still loads
- Shutting down a payment method that a processor has flagged for fraud
Every one of these gives an on-call engineer a way to reduce blast radius without waiting on a build, a deploy, and a rollback. Unleash treats this as a first-class use case, and kill-switch is one of the flag types you can assign when you create a flag.
Side by side
| Dark launch | Kill switch | |
|---|---|---|
| Purpose | Validate new code in production before release | Disable working code that has started to fail |
| Default state | Off, then gradually turned on | Off, meaning the feature is running normally |
| Who it targets | Internal users, a test segment, or nobody | Everyone, usually all at once |
| Lifespan | Short. Days or weeks | Long. It stays as long as the risk does |
| Who flips it | Product or engineering, on a planned schedule | On-call, during an incident |
| Cleanup | Remove the flag after full rollout | Keep it, and review it periodically |
| Unleash flag type | Release or experiment | Kill switch or operational |
They work well together
A careful release usually uses both.
Say you are replacing a search backend. You dark launch the new backend to your internal team, compare result quality and response times against the old one, and fix what you find. Then you start a gradual rollout: 5% of users, then 25%, then everyone. Along the way you watch error rates and conversion.
Now the feature is fully released, the release flag has served its purpose, and you remove it. But search is a dependency that can fail in ways you cannot predict, so you leave a separate kill switch in place that falls back to the old backend, or to a simpler query path, if the new one starts throwing errors.
Trying to make one flag do both jobs is where teams get into trouble, because a release flag that never gets removed turns into technical debt, and a kill switch that gets deleted after launch leaves you with no fast recovery option.
Where teams get this wrong
Leaving release flags in the codebase. A dark launch flag has a job with an end date, and most other flags should be short-lived too. We recommend giving every flag an owner and an expected lifetime when you create it, and treating stale flag cleanup the same way you treat any other technical debt.
Building one giant parent kill switch. Grouping many flags under a single parent so you can disable everything at once sounds tidy. In practice it adds complexity, because now every rollout decision depends on both the parent and the child, and targeting rules on both sides can interact in ways nobody predicted.
No clear ownership. A kill switch is useless if the person paged at 3 a.m. does not know it exists or is not permitted to flip it. One good approach is to organize flags so operational flags live in a project owned by the platform or SRE team, separate from the day-to-day release flags owned by product teams.
Over-engineering the code. For most short-lived release flags, a plain if/else is the cleanest option. The heavier patterns are worth the overhead mainly for long-lived flags like kill switches that toggle a core component.
Getting started
If you are setting up both patterns in Unleash, the sequence looks like this:
- Create the release flag for the feature you are dark launching, and give it a release type, an owner, and an expiration date.
- Use an activation strategy to target internal users first, then a percentage rollout as confidence grows.
- Watch metrics at each step, and be ready to set the rollout back to 0%.
- Once the feature is at 100% and stable, remove the flag and the conditional code.
- Separately, create a kill-switch flag for any dependency in that feature that could fail, wire it with inverted logic, document it in your runbook, and leave it alone.
The distinction comes down to time and intent. A dark launch is how you find out whether something works. A kill switch is how you survive the day it stops working.
