Feature Toggle Types
Also Known As
TL;DR
Explanation
Pete Hodgent's taxonomy: Release Toggles gate incomplete features for trunk-based development — short-lived (days to weeks), removed once released. Ops Toggles control operational behaviour (kill switch for a feature under load) — potentially long-lived, owned by ops. Experiment Toggles (A/B tests) route cohorts to different code paths — medium lifetime, data-driven removal. Permission Toggles give specific users/tenants access to premium features — long-lived, permanent in the codebase. Each type has different implications: release toggles create technical debt if not cleaned up; experiment toggles require statistical significance before decision; permission toggles need admin UI. PHP libraries: flagsmith/flagsmith-php-client, unleash/client, or a simple database-backed implementation.
Diagram
flowchart TD
subgraph Release_Toggle
RT[Hide incomplete feature<br/>until ready to ship]
end
subgraph Experiment_Toggle
ET[A/B test - 50 pct see new UI<br/>50 pct see old UI]
end
subgraph Ops_Toggle
OT[Kill switch for heavy feature<br/>during incidents]
end
subgraph Permission_Toggle
PT[Beta users see feature<br/>before general release]
end
CONFIG[(Feature Flag Config)] --- RT & ET & OT & PT
style CONFIG fill:#6e40c9,color:#fff
style RT fill:#1f6feb,color:#fff
style ET fill:#238636,color:#fff
style OT fill:#f85149,color:#fff
style PT fill:#d29922,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Treating all feature flags the same — release flags should be removed after launch; ops flags should not.
- No flag inventory or ownership — flags accumulate with no one knowing if they are safe to remove.
- Toggling features mid-transaction — partial feature activation can leave data in inconsistent states.
- Hard-coding toggle checks deep in domain logic instead of at the entry point — toggles belong at boundaries.
Code Examples
// Feature flag with no expiry or owner — permanent flag debt:
if (config('features.new_checkout')) {
// 'Temporary' flag from 18 months ago — never removed
// No one knows if false branch is still tested
// No ticket, no owner, no removal plan
}
// Release toggle — hide incomplete feature from users
\$flags->isEnabled('new_billing_ui', \$user);
// Experiment toggle — A/B test
\$variant = \$flags->getVariant('checkout_flow', \$user); // 'control' or 'treatment'
// Ops toggle — kill switch for a live feature under load
if (!\$flags->isEnabled('recommendation_engine')) {
return []; // disable when it's hammering the DB
}
// Permission toggle — role-based feature access
if (\$flags->isEnabled('beta_dashboard', \$user)) { ... } // only for beta users
// Implementation approaches:
// 1. Config file / env var — simplest, needs deploy to change
// 2. Database row — live changes, no deploy
// 3. Feature flag service (LaunchDarkly, Flagsmith, Unleash) — targeting rules