Overrides
Force a gate on or off for specific user IDs — for QA, dogfood, demos, and customer-specific repros. Bypasses targeting and rollout.
An override is a per-user forced answer. It bypasses everything — targeting rules, rollout,
killswitch, even enabled = false. Use it for the four cases below; don't reach for it as a
general-purpose targeting tool.
When to use overrides
- QA accounts. Force the new path on for QA users regardless of rollout state. You can fully test a feature at 0% rollout because the QA accounts are always-on.
- Internal dogfood. Your team always sees the latest version of every gate, ahead of customers.
- Customer demos. A specific demo account sees the brand-new feature for the screenshare, while everyone else stays on the stable path.
- Bug reproduction. A single customer reports a bug behind a feature flag. Override them to
onso you can reproduce against their account without ramping the gate up.
When not to use overrides
If you're adding more than ~50 user IDs, you want a targeting rule on an attribute, not a literal list of IDs. Examples of things that should be rules, not overrides:
- "All users on the
betaplan." → Targeting rule onplan eq "beta". - "All users from a specific company." → Targeting rule on
email contains "@company.com". - "All users in a country." → Targeting rule on
country in [...].
Overrides are a row-per-user lookup in KV; rules are O(1) regardless of population size. Rules scale; overrides don't.
Adding an override
In the dashboard: expand the gate, scroll to Overrides, add user IDs under Always on or Always off.
From the CLI:
shipeasy flags override add checkout-v2 \
--user u_qa1 --user u_qa2 --user u_demo \
--decision true
shipeasy flags override add checkout-v2 \
--user u_legacy_partner \
--decision falseRemoving:
shipeasy flags override remove checkout-v2 --user u_qa1What overrides bypass
For a call gate(name, ctx) where ctx.userId is in the overrides list:
1. killswitch ON → STILL returns override decision ← bypassed
2. enabled OFF → STILL returns override decision ← bypassed
3. userId ∈ overrides[true] → true ← here
4. userId ∈ overrides[false] → false ← here
5. rules → not evaluated
6. rollout → not evaluatedThe override sits at step 3–4 in evaluation order and short-circuits everything below. Crucially, it also bypasses the killswitch — which is what you want for QA (you can still test a killed feature) but is dangerous for production users.
Don't override a real customer to always_on for a feature that isn't ready. The override ignores
the killswitch — if you hit "kill" during an incident, the overridden customer still sees the
broken path. Use rules with a customer_email predicate instead.
Override scope
- Overrides are keyed on
ctx.userIdonly — literal string equality, no operators. - No attribute matching.
plan = procannot be expressed as an override; that's a rule. - The list is scoped per-gate. A user being on the
always_onlist forgate-asays nothing aboutgate-b.
Dashboard JSON shape
The override block is part of the gate's stored config:
{
"name": "checkout-v2",
"enabled": true,
"killswitch": false,
"rolloutPct": 2500,
"overrides": {
"always_on": ["u_qa1", "u_qa2", "u_demo_alpha"],
"always_off": ["u_legacy_partner_b"]
}
}You can GET and PATCH this shape directly via the admin API.
Limits
| Plan | Overrides per gate |
|---|---|
| Free | 100 |
| Pro | 10,000 |
| Enterprise | Unlimited |
If you're approaching the limit, you're almost certainly using overrides where you should be using rules. Audit the list — most overrides are old QA accounts that should have been removed when the gate was launched.
Cleaning up after launch
Once a gate is at 100% and the feature is fully shipped, the override list is dead weight. Two common patterns:
- Delete the gate. Once you remove the conditional from code and clean up the dead path, delete the gate itself. The override list goes with it.
- Clear overrides. Keep the gate (if you might want to reuse the name for a follow-up
feature) but clear out the override list:
shipeasy flags override clear checkout-v2
The dashboard's Cleanup view surfaces gates with stale overrides (overrides on a gate that's been at 100% for >30 days) so you don't have to remember.
Audit
Every override change is recorded — who added it, who removed it, when, from what surface (dashboard, CLI, API). Find the audit trail under Gate → History. Particularly useful when a customer reports a feature they're "always seeing" — the history tells you whether they were overridden, by whom, and when.