Overview

Prev Next

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 NoopFeatureProvider ensures 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 FeatureRegistry object 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 default value 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 Permission class)
  • 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