Feature Flags Module
The Feature Flags module provides a safe, code-owned mechanism to enable, disable, and roll out features across the platform without redeployments.
It is designed as infrastructure, not as ad-hoc UI toggles.
Feature flags in this system are:
- explicitly declared in code (
core/registry.ts) - resolved server-side (
runtime/resolver.ts) - mapped to UI intent before delivery (
runtime/uiMapper.ts) - optional at runtime — a
NoopFeatureProviderensures safe fallback - resilient to misconfiguration or outages
If a feature flag is not declared in the registry, it does not exist for the platform — even if it exists in GrowthBook or any other remote tool.
Source Layout
src/lib/external/FeatureFlag/
├── core/
│ ├── registry.ts # Allow-list of all flags + defaults
│ └── types.ts # FeatureFlagValue, FeatureFlagDefinition, etc.
├── providers/
│ ├── provider.interface.ts # FeatureProvider adapter contract
│ ├── growthbook/
│ │ ├── client.ts # GrowthBook SDK init (server-only)
│ │ └── provider.ts # GrowthBookFeatureProvider
│ └── noop/
│ └── provider.ts # NoopFeatureProvider (always returns default)
└── runtime/
├── resolver.ts # resolveFeatures() — enforcement point
├── uiMapper.ts # mapToUIFeatures() — intent projection
└── index.ts # Re-exports
Supporting integration points:
| File | Role |
|---|---|
src/app/api/platform/features/route.ts |
API route — resolves and delivers UI flags |
src/services/FeatureFlagService.ts |
Service class — fetches flags via Fetch.json |
src/context/FeatureFlagContext.tsx |
React context + useFeatureFlagContext() hook |
src/app/layout.tsx |
Root layout — fetches flags and wraps app in provider |
Why This Module Exists
Feature flags are often a source of long-term complexity when:
- remote tools become the source of truth
- flags are created without code ownership
- flags accumulate and are never deleted
- misconfiguration can break the application
This module exists to enforce the opposite defaults:
- Code owns the feature surface — the
FeatureRegistryobject is the single source of truth - Remote tools are advisory — GrowthBook can only influence flags already declared in code
- Defaults are explicit and safe — every flag has a
defaultvalue used when providers fail - Deletion is easy and non-disruptive — remove from registry, remove usages, done
High-Level Guarantees
| Guarantee | Enforced By |
|---|---|
| Only allow-listed flags affect the app | resolveFeatures() iterates only over registry keys |
| Feature flags can never break the app | Provider errors are caught and fall back to definition.default |
| Missing infrastructure falls back to defaults | NoopFeatureProvider returns fallback for every key |
| UI never consumes raw infrastructure flags | mapToUIFeatures() projects to UIFeatureFlags |
| Kill switches always win | Resolver forces kill lifecycle flags to value === true |
| Resolved values are immutable | Object.freeze(resolved) on the result |
| Type mismatches are caught at runtime | If provider returns wrong type, resolver falls back to default |
What Feature Flags Are Used For
Feature flags are intended for:
- gradual rollouts
- environment-based enablement
- beta or experimental features
- non-critical behavior changes
- UI and UX variations
- performance or infrastructure toggles (e.g.,
enable-parallel-chunk-processing)
What Feature Flags Are Not Used For
Feature flags must not be used for:
- authorization or permission checks (use
Permissionclass) - billing or plan enforcement
- data integrity rules
- security decisions
- bypassing backend validation
Feature flags influence behavior and presentation, not access or correctness.
Documentation
- Architecture— How flags are declared, resolved, and delivered
- Usage — How to add, expose, and delete feature flags
- Troubleshooting — Common failure modes and diagnosis