4 min read
Feature Flags Example
The real feature flag system powering directive.run. Eight flags, one dependency constraint, maintenance mode, and change logging.
Overview
This example mirrors the actual module running on the doc site:
- 8 feature toggles – chat, search, playground, vote API, brand switcher, theme selector, version selector, onboarding toast
- Maintenance mode – one flag disables four interactive features via derivations
- Dependency constraint – onboarding toast requires brand switcher (auto-enabled by the runtime)
- Change logging – effect logs every flag toggle to the console
The Module
import { createModule, t } from "@directive-run/core";
const featureFlags = createModule("feature-flags", {
schema: {
facts: {
chatEnabled: t.boolean(),
searchEnabled: t.boolean(),
playgroundEnabled: t.boolean(),
brandSwitcherEnabled: t.boolean(),
themeSelectorEnabled: t.boolean(),
onboardingToastEnabled: t.boolean(),
versionSelectorEnabled: t.boolean(),
shareButtonEnabled: t.boolean(),
maintenanceMode: t.boolean(),
},
derivations: {
canUseChat: t.boolean(),
canUseSearch: t.boolean(),
canUsePlayground: t.boolean(),
canUseBrandSwitcher: t.boolean(),
canUseThemeSelector: t.boolean(),
canShowOnboardingToast: t.boolean(),
canUseVersionSelector: t.boolean(),
canUseShareButton: t.boolean(),
enabledCount: t.number(),
allFeaturesEnabled: t.boolean(),
},
events: {
toggleFlag: { flag: t.string(), enabled: t.boolean() },
setMaintenanceMode: { enabled: t.boolean() },
resetAll: {},
},
requirements: {
ENABLE_BRAND_SWITCHER: {},
},
},
init: (facts) => {
facts.chatEnabled = true;
facts.searchEnabled = true;
facts.playgroundEnabled = true;
facts.brandSwitcherEnabled = true;
facts.themeSelectorEnabled = true;
facts.onboardingToastEnabled = true;
facts.versionSelectorEnabled = true;
facts.shareButtonEnabled = true;
facts.maintenanceMode = false;
},
derive: {
canUseChat: (facts) =>
facts.chatEnabled && !facts.maintenanceMode,
canUseSearch: (facts) =>
facts.searchEnabled && !facts.maintenanceMode,
canUsePlayground: (facts) =>
facts.playgroundEnabled && !facts.maintenanceMode,
canUseBrandSwitcher: (facts) =>
facts.brandSwitcherEnabled,
canUseThemeSelector: (facts) =>
facts.themeSelectorEnabled,
canShowOnboardingToast: (facts) =>
facts.onboardingToastEnabled && facts.brandSwitcherEnabled,
canUseVersionSelector: (facts) =>
facts.versionSelectorEnabled,
canUseShareButton: (facts) =>
facts.shareButtonEnabled,
enabledCount: (facts) => {
let count = 0;
if (facts.chatEnabled) count++;
if (facts.searchEnabled) count++;
if (facts.playgroundEnabled) count++;
if (facts.brandSwitcherEnabled) count++;
if (facts.themeSelectorEnabled) count++;
if (facts.onboardingToastEnabled) count++;
if (facts.versionSelectorEnabled) count++;
if (facts.shareButtonEnabled) count++;
return count;
},
allFeaturesEnabled: (facts) =>
facts.chatEnabled &&
facts.searchEnabled &&
facts.playgroundEnabled &&
facts.brandSwitcherEnabled &&
facts.themeSelectorEnabled &&
facts.onboardingToastEnabled &&
facts.versionSelectorEnabled &&
facts.shareButtonEnabled,
},
constraints: {
onboardingRequiresBrandSwitcher: {
when: (facts) => facts.onboardingToastEnabled && !facts.brandSwitcherEnabled,
require: { type: "ENABLE_BRAND_SWITCHER" },
},
},
resolvers: {
enableBrandSwitcher: {
requirement: "ENABLE_BRAND_SWITCHER",
resolve: async (req, context) => {
context.facts.brandSwitcherEnabled = true;
},
},
},
});
Constraint-Driven Dependencies
The constraint onboardingRequiresBrandSwitcher is the heart of this example:
constraints: {
onboardingRequiresBrandSwitcher: {
when: (facts) => facts.onboardingToastEnabled && !facts.brandSwitcherEnabled,
require: { type: "ENABLE_BRAND_SWITCHER" },
},
},
When a user enables onboardingToastEnabled without brandSwitcherEnabled, the constraint fires. The ENABLE_BRAND_SWITCHER resolver automatically sets brandSwitcherEnabled = true. No manual coordination needed – the runtime enforces the dependency.
Maintenance Mode
Four derivations are gated by maintenanceMode:
canUseChat: (facts) => facts.chatEnabled && !facts.maintenanceMode,
canUseSearch: (facts) => facts.searchEnabled && !facts.maintenanceMode,
canUsePlayground: (facts) => facts.playgroundEnabled && !facts.maintenanceMode,
canUseShareButton: (facts) => facts.shareButtonEnabled,
One setMaintenanceMode dispatch flips all four to false. The three UI flags (brand switcher, theme selector, version selector) are not gated – they should still work when interactive features are down.
React Hooks
Thin wrappers around useDerived for each composite flag:
import { useDerived } from "@directive-run/react";
import { getFeatureFlagSystem } from "./config";
export function useCanUseChat() {
return useDerived(getFeatureFlagSystem(), "canUseChat");
}
export function useCanShowOnboardingToast() {
return useDerived(getFeatureFlagSystem(), "canShowOnboardingToast");
}
Components read derivations, not raw facts. Toggling maintenanceMode re-renders only the four gated components.
Runtime Controls
Disable a constraint or effect at runtime without touching the flag values:
// Disable the auto-enable constraint for debugging
system.constraints.disable("onboardingRequiresBrandSwitcher");
// Disable change logging during tests
system.effects.disable("logChanges");
Run It
cd examples/feature-flags
pnpm install
pnpm dev
Toggle flags in the left panel and watch the preview update in real time. Enable onboarding toast, turn off brand switcher, and watch the constraint auto-enable it. Toggle maintenance mode and see four features disable at once.
Related
- Feature Flags Without a Feature Flag Service – full blog post with production configuration and comparison table
- Constraints – how
when/requireworks - Derivations – computed flags that replace conditionals
- Effects – side effects like change logging
- Persistence Plugin – localStorage integration

