Feature Flags
Feature flags (also known as feature toggles) let you control the visibility of features in your application without deploying new code.
Flag Types
RedPennon's four flag types follow Martin Fowler principles of Feature Toggles. When you create a feature, the type you pick shapes the variations, the per-environment defaults, and any starter targeting rules — see Defaults seeded on create below.
| Type | Use case |
|---|---|
| Release | Separate feature delivery from deployment. Short- to medium-lived; typically removed after the feature is fully rolled out. |
| Experiment | A/B or multivariate tests. Compares variations against a control to measure impact. |
| Permission | Long-lived entitlement gates. Grants access to specific users, audiences, or plans. |
| Ops | Operational toggles — kill switches, performance gates, maintenance mode. May live in code long-term. |
Defaults seeded on create
Picking a type in the Create New Feature modal pre-fills the new feature with sensible defaults so you don't land on a blank slate. The defaults follow two principles:
- A brand-new feature is never enabled in Production. Production targeting always starts disabled, regardless of type. You must explicitly turn it on when you're ready.
- Starter rules are concrete, not placeholders — where possible they already match you (the creator) or your organisation's email domain, so you can verify the feature works end-to-end before broadening the audience.
Variations and default served value
| Type | Seeded variations | Feature.control_variation | Default served (per environment) |
|---|---|---|---|
| Release | on, off | — | off |
| Experiment | control, variation-a, variation-b | control | control |
| Permission | configuration-1, configuration-2 | — | configuration-2 |
| Ops | configuration-1, configuration-2 | — | configuration-2 |
Experiment features seed three arms with no on/off slugs at all — the default served variation is the control baseline, and the experiment uses a random distribution to allocate users into control / variation-a / variation-b. Permission and Ops features use the neutral configuration-1/configuration-2 pair because for these types neither value is obviously "on".
Default variable type
Step 2 of the Create New Feature modal pre-selects a variable type that matches the feature type, so the most common case needs zero clicks:
| Feature type | Default variable type | Why |
|---|---|---|
| Release | boolean | Releases are toggles — true/false is the right shape. |
| Experiment | string | Experiments compare named treatments (control, variation-a, …); a boolean can't carry that. |
| Permission | boolean | Permissions gate access — granted or not. |
| Ops | boolean | Operational kill switches are on/off. |
You can still pick any of the four types (boolean, number, string, JSON object) from the dropdown — the default is just the starting point.
Targeting state per environment
| Type | Development | Testing | Production |
|---|---|---|---|
| Release | enabled | enabled | disabled |
| Experiment | enabled | disabled | disabled |
| Permission | enabled | enabled | disabled |
| Ops | enabled | disabled | disabled |
Experiment defaults to Dev only so the seeded distribution doesn't accidentally leak into Testing or Production traffic before you've reviewed it. Ops defaults to Dev only so the seeded "Self-target" rule fires immediately when the developer who created the kill switch is testing it — Testing/Prod stay off until you turn the kill switch on deliberately.
Starter targeting rules
| Type | Where seeded | Rule name | Condition | Serves |
|---|---|---|---|---|
| Release | Dev + Testing (skipped in Prod) | All Users | all_users | on |
| Experiment | Development only | All Users | all_users | 34/33/33 split across control / variation-a / variation-b |
| Permission | every environment (Dev, Testing, and Production) | Internal Users | email contains "@<organisation.email_domain>" if set, else email is <creator-email> | configuration-1 |
| Ops | Development only | Self-target | email is <creator-email> | configuration-2 (the kill path) |
Why Permission seeds the rule in Production too: the Production targeting is still disabled at create time, but the rule is in place so when you do enable Production later there's no gap — the rule is already wired up exactly as it has been in Dev/Testing.
Why Ops seeds a Dev-only rule serving configuration-2: when the developer who created the kill switch is testing in Dev, the rule matches them immediately and serves the off path so they can verify the disabled code path before flipping the kill switch in Testing or Production.
The Permission rule degrades gracefully when concrete details are missing:
- If the organisation has an
email_domainconfigured (set in the org-create / org-settings form), the rule usesemail contains "@<domain>"— anyone signing in with an org email matches. - If the org has no
email_domainbut the creator has an email, the rule falls back toemail is <creator-email>— only the creator matches. - If neither is available, the rule is still seeded but with an empty value list — it matches no-one until you fill it in.
After creation
These defaults are starting points, not policy. Once the feature exists you can:
- toggle targeting on or off per environment from the Manage Feature → Targeting view (subject to role and the production guard),
- edit, reorder, or delete the seeded starter rule like any other rule,
- change the per-environment default served variation in the variables matrix,
- add or remove variations (the seeded slugs are protected — see the Variations guide).
Flag Status
| Status | Description |
|---|---|
| Development | Flag is being built, targeting rules can be modified |
| Live | Flag is active in production |
| Complete | Flag evaluation is frozen to its current variation |
| Archived | Flag is retired and returns "off" |
Variables and Variations
Each feature can have multiple variables. Each variable has a type (boolean, number, string, or JSON object) and a set of variations (possible values).
When a feature's first variable is created, the variations seeded depend on the feature's type — see the Variations guide. You can add more variations at any time on a non-archived, non-deleted feature, and delete variations you have added (the seeded slugs are protected).
JSON variables let you ship structured configuration through a single flag — e.g. a colour palette, a feature-config object, an A/B-tunable layout. The value must be a JSON object (top-level arrays and scalars are rejected); the evaluator returns the object verbatim in the response's variables map.