# ADR-024 Implementation Plan: Feature Flagging Strategy

**Date**: 2026-01-28
**Type**: Technical Implementation Plan
**Related**: [ADR-024: Feature Flagging Strategy](../adr/0024-feature-flagging-strategy.md), [ADR-007: Configuration and Secrets Management](../adr/0007-configuration-and-secrets-management.md)
**Status**: Ready for Implementation

---

## Project Overview

Implement a simple, configuration-based feature flag system that integrates with the existing Zod-validated configuration in `src/config/env.ts`. The system will support both backend and frontend feature flags through environment variables, with type-safe access patterns and helper utilities.

### Key Success Criteria

1. Feature flags accessible via type-safe API on both backend and frontend
2. Zero runtime overhead when flag is disabled (compile-time elimination where possible)
3. Consistent naming convention (environment variables and code access)
4. Graceful degradation (missing flag defaults to disabled)
5. Easy migration path to external service (Flagsmith/LaunchDarkly) in the future
6. Full test coverage with mocking utilities

### Estimated Total Effort

| Phase                             | Estimate       |
| --------------------------------- | -------------- |
| Phase 1: Backend Infrastructure   | 3-5 hours      |
| Phase 2: Frontend Infrastructure  | 2-3 hours      |
| Phase 3: Documentation & Examples | 1-2 hours      |
| **Total**                         | **6-10 hours** |

---

## Current State Analysis

### Backend Configuration (`src/config/env.ts`)

- Zod-based schema validation at startup
- Organized into logical groups (database, redis, auth, smtp, ai, etc.)
- Helper exports for service availability (`isSmtpConfigured`, `isAiConfigured`, etc.)
- Environment helpers (`isProduction`, `isTest`, `isDevelopment`)
- Fail-fast on invalid configuration

### Frontend Configuration (`src/config.ts`)

- Uses `import.meta.env` (Vite environment variables)
- Organized into sections (app, google, sentry)
- Boolean parsing for string env vars
- Type declarations in `src/vite-env.d.ts`

### Existing Patterns to Follow

```typescript
// Backend - service availability check pattern
export const isSmtpConfigured =
  !!config.smtp.host && !!config.smtp.user && !!config.smtp.pass;

// Frontend - boolean parsing pattern
enabled: import.meta.env.VITE_SENTRY_ENABLED !== 'false',
```

---

## Task Breakdown

### Phase 1: Backend Feature Flag Infrastructure

#### [1.1] Define Feature Flag Schema in env.ts

**Complexity**: Low
**Estimate**: 30-45 minutes
**Dependencies**: None
**Parallelizable**: Yes

**Description**: Add a new `featureFlags` section to the Zod schema in `src/config/env.ts`.

**Acceptance Criteria**:

- [ ] New `featureFlagsSchema` Zod object defined
- [ ] Schema supports boolean flags with defaults to `false` (opt-in model)
- [ ] Schema added to main `envSchema` object
- [ ] Type exported as part of `EnvConfig`

**Implementation Details**:

```typescript
// src/config/env.ts

/**
 * Feature flags configuration schema (ADR-024).
 * All flags default to false (disabled) for safety.
 * Set to 'true' in environment to enable.
 */
const featureFlagsSchema = z.object({
  // Example flags - replace with actual feature flags as needed
  newDashboard: booleanString(false),      // FEATURE_NEW_DASHBOARD
  betaRecipes: booleanString(false),       // FEATURE_BETA_RECIPES
  experimentalAi: booleanString(false),    // FEATURE_EXPERIMENTAL_AI
  debugMode: booleanString(false),         // FEATURE_DEBUG_MODE
});

// In loadEnvVars():
featureFlags: {
  newDashboard: process.env.FEATURE_NEW_DASHBOARD,
  betaRecipes: process.env.FEATURE_BETA_RECIPES,
  experimentalAi: process.env.FEATURE_EXPERIMENTAL_AI,
  debugMode: process.env.FEATURE_DEBUG_MODE,
},
```

**Risks/Notes**:

- Naming convention: `FEATURE_*` prefix for all feature flag env vars
- Default to `false` ensures features are opt-in, preventing accidental exposure

---

#### [1.2] Create Feature Flag Service Module

**Complexity**: Medium
**Estimate**: 1-2 hours
**Dependencies**: [1.1]
**Parallelizable**: No (depends on 1.1)

**Description**: Create a dedicated service module for feature flag access with helper functions.

**File**: `src/services/featureFlags.server.ts`

**Acceptance Criteria**:

- [ ] `isFeatureEnabled(flagName)` function for checking flags
- [ ] `getAllFeatureFlags()` function for debugging/admin endpoints
- [ ] Type-safe flag name parameter (union type or enum)
- [ ] Exported helper booleans for common flags (similar to `isSmtpConfigured`)
- [ ] Logging when feature flag is checked in development mode

**Implementation Details**:

```typescript
// src/services/featureFlags.server.ts
import { config, isDevelopment } from '../config/env';
import { logger } from './logger.server';

export type FeatureFlagName = keyof typeof config.featureFlags;

/**
 * Check if a feature flag is enabled.
 * @param flagName - The name of the feature flag to check
 * @returns boolean indicating if the feature is enabled
 */
export function isFeatureEnabled(flagName: FeatureFlagName): boolean {
  const enabled = config.featureFlags[flagName];

  if (isDevelopment) {
    logger.debug({ flag: flagName, enabled }, 'Feature flag checked');
  }

  return enabled;
}

/**
 * Get all feature flags and their current states.
 * Useful for debugging and admin endpoints.
 */
export function getAllFeatureFlags(): Record<FeatureFlagName, boolean> {
  return { ...config.featureFlags };
}

// Convenience exports for common flag checks
export const isNewDashboardEnabled = config.featureFlags.newDashboard;
export const isBetaRecipesEnabled = config.featureFlags.betaRecipes;
export const isExperimentalAiEnabled = config.featureFlags.experimentalAi;
export const isDebugModeEnabled = config.featureFlags.debugMode;
```

**Risks/Notes**:

- Keep logging minimal to avoid performance impact
- Convenience exports are evaluated once at startup (not dynamic)

---

#### [1.3] Add Admin Endpoint for Feature Flag Status

**Complexity**: Low
**Estimate**: 30-45 minutes
**Dependencies**: [1.2]
**Parallelizable**: No (depends on 1.2)

**Description**: Add an admin/health endpoint to view current feature flag states.

**File**: `src/routes/admin.routes.ts` (or `stats.routes.ts` if admin routes don't exist)

**Acceptance Criteria**:

- [ ] `GET /api/v1/admin/feature-flags` endpoint (admin-only)
- [ ] Returns JSON object with all flags and their states
- [ ] Requires admin authentication
- [ ] Endpoint documented in Swagger

**Implementation Details**:

```typescript
// In appropriate routes file
router.get('/feature-flags', requireAdmin, async (req, res) => {
  const flags = getAllFeatureFlags();
  sendSuccess(res, { flags });
});
```

**Risks/Notes**:

- Ensure endpoint is protected (admin-only)
- Consider caching response if called frequently

---

#### [1.4] Backend Unit Tests

**Complexity**: Medium
**Estimate**: 1-2 hours
**Dependencies**: [1.1], [1.2]
**Parallelizable**: Yes (can start after 1.1, in parallel with 1.3)

**Description**: Write unit tests for feature flag configuration and service.

**Files**:

- `src/config/env.test.ts` (add feature flag tests)
- `src/services/featureFlags.server.test.ts` (new file)

**Acceptance Criteria**:

- [ ] Test default values (all false)
- [ ] Test parsing 'true'/'false' strings
- [ ] Test `isFeatureEnabled()` function
- [ ] Test `getAllFeatureFlags()` function
- [ ] Test type safety (TypeScript compile-time checks)

**Implementation Details**:

```typescript
// src/config/env.test.ts - add to existing file
describe('featureFlags configuration', () => {
  it('should default all feature flags to false', async () => {
    setValidEnv();
    const { config } = await import('./env');

    expect(config.featureFlags.newDashboard).toBe(false);
    expect(config.featureFlags.betaRecipes).toBe(false);
  });

  it('should parse FEATURE_NEW_DASHBOARD as true when set', async () => {
    setValidEnv({ FEATURE_NEW_DASHBOARD: 'true' });
    const { config } = await import('./env');

    expect(config.featureFlags.newDashboard).toBe(true);
  });
});

// src/services/featureFlags.server.test.ts - new file
describe('featureFlags service', () => {
  describe('isFeatureEnabled', () => {
    it('should return false for disabled flags', () => {
      expect(isFeatureEnabled('newDashboard')).toBe(false);
    });

    // ... more tests
  });
});
```

---

### Phase 2: Frontend Feature Flag Infrastructure

#### [2.1] Add Frontend Feature Flag Config

**Complexity**: Low
**Estimate**: 30-45 minutes
**Dependencies**: None (can run in parallel with Phase 1)
**Parallelizable**: Yes

**Description**: Add feature flags to the frontend config module.

**Files**:

- `src/config.ts` - Add featureFlags section
- `src/vite-env.d.ts` - Add type declarations

**Acceptance Criteria**:

- [ ] Feature flags section added to `src/config.ts`
- [ ] TypeScript declarations updated in `vite-env.d.ts`
- [ ] Boolean parsing consistent with existing pattern
- [ ] Default to false when env var not set

**Implementation Details**:

```typescript
// src/config.ts
const config = {
  // ... existing sections ...

  /**
   * Feature flags for conditional feature rendering (ADR-024).
   * All flags default to false (disabled) when not explicitly set.
   */
  featureFlags: {
    newDashboard: import.meta.env.VITE_FEATURE_NEW_DASHBOARD === 'true',
    betaRecipes: import.meta.env.VITE_FEATURE_BETA_RECIPES === 'true',
    experimentalAi: import.meta.env.VITE_FEATURE_EXPERIMENTAL_AI === 'true',
    debugMode: import.meta.env.VITE_FEATURE_DEBUG_MODE === 'true',
  },
};

// src/vite-env.d.ts
interface ImportMetaEnv {
  // ... existing declarations ...
  readonly VITE_FEATURE_NEW_DASHBOARD?: string;
  readonly VITE_FEATURE_BETA_RECIPES?: string;
  readonly VITE_FEATURE_EXPERIMENTAL_AI?: string;
  readonly VITE_FEATURE_DEBUG_MODE?: string;
}
```

---

#### [2.2] Create useFeatureFlag React Hook

**Complexity**: Medium
**Estimate**: 1-1.5 hours
**Dependencies**: [2.1]
**Parallelizable**: No (depends on 2.1)

**Description**: Create a React hook for checking feature flags in components.

**File**: `src/hooks/useFeatureFlag.ts`

**Acceptance Criteria**:

- [ ] `useFeatureFlag(flagName)` hook returns boolean
- [ ] Type-safe flag name parameter
- [ ] Memoized to prevent unnecessary re-renders
- [ ] Optional `FeatureFlag` component for conditional rendering

**Implementation Details**:

```typescript
// src/hooks/useFeatureFlag.ts
import { useMemo } from 'react';
import config from '../config';

export type FeatureFlagName = keyof typeof config.featureFlags;

/**
 * Hook to check if a feature flag is enabled.
 *
 * @param flagName - The name of the feature flag to check
 * @returns boolean indicating if the feature is enabled
 *
 * @example
 * const isNewDashboard = useFeatureFlag('newDashboard');
 * if (isNewDashboard) {
 *   return <NewDashboard />;
 * }
 */
export function useFeatureFlag(flagName: FeatureFlagName): boolean {
  return useMemo(() => config.featureFlags[flagName], [flagName]);
}

/**
 * Get all feature flags (useful for debugging).
 */
export function useAllFeatureFlags(): Record<FeatureFlagName, boolean> {
  return useMemo(() => ({ ...config.featureFlags }), []);
}
```

---

#### [2.3] Create FeatureFlag Component

**Complexity**: Low
**Estimate**: 30-45 minutes
**Dependencies**: [2.2]
**Parallelizable**: No (depends on 2.2)

**Description**: Create a declarative component for feature flag conditional rendering.

**File**: `src/components/FeatureFlag.tsx`

**Acceptance Criteria**:

- [ ] `<FeatureFlag name="flagName">` component
- [ ] Children rendered only when flag is enabled
- [ ] Optional `fallback` prop for disabled state
- [ ] TypeScript-enforced flag names

**Implementation Details**:

```typescript
// src/components/FeatureFlag.tsx
import { ReactNode } from 'react';
import { useFeatureFlag, FeatureFlagName } from '../hooks/useFeatureFlag';

interface FeatureFlagProps {
  /** The name of the feature flag to check */
  name: FeatureFlagName;
  /** Content to render when feature is enabled */
  children: ReactNode;
  /** Optional content to render when feature is disabled */
  fallback?: ReactNode;
}

/**
 * Conditionally renders children based on feature flag state.
 *
 * @example
 * <FeatureFlag name="newDashboard" fallback={<OldDashboard />}>
 *   <NewDashboard />
 * </FeatureFlag>
 */
export function FeatureFlag({ name, children, fallback = null }: FeatureFlagProps) {
  const isEnabled = useFeatureFlag(name);
  return <>{isEnabled ? children : fallback}</>;
}
```

---

#### [2.4] Frontend Unit Tests

**Complexity**: Medium
**Estimate**: 1-1.5 hours
**Dependencies**: [2.1], [2.2], [2.3]
**Parallelizable**: No (depends on previous frontend tasks)

**Description**: Write unit tests for frontend feature flag utilities.

**Files**:

- `src/config.test.ts` (add feature flag tests)
- `src/hooks/useFeatureFlag.test.ts` (new file)
- `src/components/FeatureFlag.test.tsx` (new file)

**Acceptance Criteria**:

- [ ] Test config structure includes featureFlags
- [ ] Test default values (all false)
- [ ] Test hook returns correct values
- [ ] Test component renders/hides children correctly
- [ ] Test fallback rendering

**Implementation Details**:

```typescript
// src/hooks/useFeatureFlag.test.ts
import { renderHook } from '@testing-library/react';
import { useFeatureFlag, useAllFeatureFlags } from './useFeatureFlag';

describe('useFeatureFlag', () => {
  it('should return false for disabled flags', () => {
    const { result } = renderHook(() => useFeatureFlag('newDashboard'));
    expect(result.current).toBe(false);
  });
});

// src/components/FeatureFlag.test.tsx
import { render, screen } from '@testing-library/react';
import { FeatureFlag } from './FeatureFlag';

describe('FeatureFlag', () => {
  it('should not render children when flag is disabled', () => {
    render(
      <FeatureFlag name="newDashboard">
        <div data-testid="new-feature">New Feature</div>
      </FeatureFlag>
    );
    expect(screen.queryByTestId('new-feature')).not.toBeInTheDocument();
  });

  it('should render fallback when flag is disabled', () => {
    render(
      <FeatureFlag name="newDashboard" fallback={<div>Old Feature</div>}>
        <div>New Feature</div>
      </FeatureFlag>
    );
    expect(screen.getByText('Old Feature')).toBeInTheDocument();
  });
});
```

---

### Phase 3: Documentation & Integration

#### [3.1] Update ADR-024 with Implementation Status

**Complexity**: Low
**Estimate**: 30 minutes
**Dependencies**: [1.1], [1.2], [2.1], [2.2]
**Parallelizable**: Yes (can be done after core implementation)

**Description**: Update ADR-024 to mark it as implemented and add implementation details.

**File**: `docs/adr/0024-feature-flagging-strategy.md`

**Acceptance Criteria**:

- [ ] Status changed from "Proposed" to "Accepted"
- [ ] Implementation status section added
- [ ] Key files documented
- [ ] Usage examples included

---

#### [3.2] Update Environment Documentation

**Complexity**: Low
**Estimate**: 30 minutes
**Dependencies**: [1.1], [2.1]
**Parallelizable**: Yes

**Description**: Add feature flag environment variables to documentation.

**Files**:

- `docs/getting-started/ENVIRONMENT.md`
- `.env.example`

**Acceptance Criteria**:

- [ ] Feature flag variables documented in ENVIRONMENT.md
- [ ] New section "Feature Flags" added
- [ ] `.env.example` updated with commented feature flag examples

**Implementation Details**:

```bash
# .env.example addition
# ===================
# Feature Flags (ADR-024)
# ===================
# All feature flags default to disabled (false) when not set.
# Set to 'true' to enable a feature.
#
# FEATURE_NEW_DASHBOARD=false
# FEATURE_BETA_RECIPES=false
# FEATURE_EXPERIMENTAL_AI=false
# FEATURE_DEBUG_MODE=false
#
# Frontend equivalents (prefix with VITE_):
# VITE_FEATURE_NEW_DASHBOARD=false
# VITE_FEATURE_BETA_RECIPES=false
```

---

#### [3.3] Create CODE-PATTERNS Entry

**Complexity**: Low
**Estimate**: 30 minutes
**Dependencies**: All implementation tasks
**Parallelizable**: Yes

**Description**: Add feature flag usage patterns to CODE-PATTERNS.md.

**File**: `docs/development/CODE-PATTERNS.md`

**Acceptance Criteria**:

- [ ] Feature flag section added with examples
- [ ] Backend usage pattern documented
- [ ] Frontend usage pattern documented
- [ ] Testing pattern documented

---

#### [3.4] Update CLAUDE.md Quick Reference

**Complexity**: Low
**Estimate**: 15 minutes
**Dependencies**: All implementation tasks
**Parallelizable**: Yes

**Description**: Add feature flags to the CLAUDE.md quick reference tables.

**File**: `CLAUDE.md`

**Acceptance Criteria**:

- [ ] Feature flags added to "Key Patterns" table
- [ ] Reference to featureFlags service added

---

## Implementation Sequence

### Phase 1 (Backend) - Can Start Immediately

```text
[1.1] Schema ──────────┬──> [1.2] Service ──> [1.3] Admin Endpoint
                       │
                       └──> [1.4] Backend Tests (can start after 1.1)
```

### Phase 2 (Frontend) - Can Start Immediately (Parallel with Phase 1)

```text
[2.1] Config ──> [2.2] Hook ──> [2.3] Component ──> [2.4] Frontend Tests
```

### Phase 3 (Documentation) - After Implementation

```text
All Phase 1 & 2 Tasks ──> [3.1] ADR Update
                     ├──> [3.2] Env Docs
                     ├──> [3.3] Code Patterns
                     └──> [3.4] CLAUDE.md
```

---

## Critical Path

The minimum path to a working feature flag system:

1. **[1.1] Schema** (30 min) - Required for backend
2. **[1.2] Service** (1.5 hr) - Required for backend access
3. **[2.1] Frontend Config** (30 min) - Required for frontend
4. **[2.2] Hook** (1 hr) - Required for React integration

**Critical path duration**: ~3.5 hours

Non-critical but recommended:

- Admin endpoint (debugging)
- FeatureFlag component (developer convenience)
- Tests (quality assurance)
- Documentation (maintainability)

---

## Scope Recommendations

### MVP (Minimum Viable Implementation)

Include in initial implementation:

- [1.1] Backend schema with 2-3 example flags
- [1.2] Feature flag service
- [2.1] Frontend config
- [2.2] useFeatureFlag hook
- [1.4] Core backend tests
- [2.4] Core frontend tests

### Enhancements (Future Iterations)

Defer to follow-up work:

- Admin endpoint for flag visibility
- FeatureFlag component (nice-to-have)
- Dynamic flag updates without restart (requires external service)
- User-specific flags (A/B testing)
- Flag analytics/usage tracking
- Gradual rollout percentages

### Explicitly Out of Scope

- Integration with Flagsmith/LaunchDarkly (future ADR)
- Database-stored flags (requires schema changes)
- Real-time flag updates (WebSocket/SSE)
- Flag inheritance/hierarchy
- Flag audit logging

---

## Testing Strategy

### Backend Tests

| Test Type         | Coverage Target                          | Location                                   |
| ----------------- | ---------------------------------------- | ------------------------------------------ |
| Schema validation | Parse true/false, defaults               | `src/config/env.test.ts`                   |
| Service functions | `isFeatureEnabled`, `getAllFeatureFlags` | `src/services/featureFlags.server.test.ts` |
| Integration       | Admin endpoint (if added)                | `src/routes/admin.routes.test.ts`          |

### Frontend Tests

| Test Type           | Coverage Target             | Location                              |
| ------------------- | --------------------------- | ------------------------------------- |
| Config structure    | featureFlags section exists | `src/config.test.ts`                  |
| Hook behavior       | Returns correct values      | `src/hooks/useFeatureFlag.test.ts`    |
| Component rendering | Conditional children        | `src/components/FeatureFlag.test.tsx` |

### Mocking Pattern for Tests

```typescript
// Backend - reset modules to test different flag states
beforeEach(() => {
  vi.resetModules();
  process.env.FEATURE_NEW_DASHBOARD = 'true';
});

// Frontend - mock config module
vi.mock('../config', () => ({
  default: {
    featureFlags: {
      newDashboard: true,
      betaRecipes: false,
    },
  },
}));
```

---

## Risk Assessment

| Risk                                        | Impact | Likelihood | Mitigation                                                    |
| ------------------------------------------- | ------ | ---------- | ------------------------------------------------------------- |
| Flag state inconsistency (backend/frontend) | Medium | Low        | Use same env var naming, document sync requirements           |
| Performance impact from flag checks         | Low    | Low        | Flags cached at startup, no runtime DB calls                  |
| Stale flags after deployment                | Medium | Medium     | Document restart requirement, consider future dynamic loading |
| Feature creep (too many flags)              | Medium | Medium     | Require ADR for new flags, sunset policy                      |
| Missing flag causes crash                   | High   | Low        | Default to false, graceful degradation                        |

---

## Files to Create

| File                                       | Purpose                      |
| ------------------------------------------ | ---------------------------- |
| `src/services/featureFlags.server.ts`      | Backend feature flag service |
| `src/services/featureFlags.server.test.ts` | Backend tests                |
| `src/hooks/useFeatureFlag.ts`              | React hook for flag access   |
| `src/hooks/useFeatureFlag.test.ts`         | Hook tests                   |
| `src/components/FeatureFlag.tsx`           | Declarative flag component   |
| `src/components/FeatureFlag.test.tsx`      | Component tests              |

## Files to Modify

| File                                         | Changes                            |
| -------------------------------------------- | ---------------------------------- |
| `src/config/env.ts`                          | Add featureFlagsSchema and loading |
| `src/config/env.test.ts`                     | Add feature flag tests             |
| `src/config.ts`                              | Add featureFlags section           |
| `src/config.test.ts`                         | Add feature flag tests             |
| `src/vite-env.d.ts`                          | Add VITE*FEATURE*\* declarations   |
| `.env.example`                               | Add feature flag examples          |
| `docs/adr/0024-feature-flagging-strategy.md` | Update status and details          |
| `docs/getting-started/ENVIRONMENT.md`        | Document feature flag vars         |
| `docs/development/CODE-PATTERNS.md`          | Add usage patterns                 |
| `CLAUDE.md`                                  | Add to quick reference             |

---

## Verification Commands

After implementation, run these commands in the dev container:

```bash
# Type checking
podman exec -it flyer-crawler-dev npm run type-check

# Backend unit tests
podman exec -it flyer-crawler-dev npm run test:unit -- --grep "featureFlag"

# Frontend tests (includes hook and component tests)
podman exec -it flyer-crawler-dev npm run test:unit -- --grep "FeatureFlag"

# Full test suite
podman exec -it flyer-crawler-dev npm test
```

---

## Example Usage (Post-Implementation)

### Backend Route Handler

```typescript
// src/routes/flyers.routes.ts
import { isFeatureEnabled } from '../services/featureFlags.server';

router.get('/dashboard', async (req, res) => {
  if (isFeatureEnabled('newDashboard')) {
    // New dashboard logic
    return sendSuccess(res, { version: 'v2', data: await getNewDashboardData() });
  }
  // Legacy dashboard
  return sendSuccess(res, { version: 'v1', data: await getLegacyDashboardData() });
});
```

### React Component

```tsx
// src/pages/Dashboard.tsx
import { FeatureFlag } from '../components/FeatureFlag';
import { useFeatureFlag } from '../hooks/useFeatureFlag';

// Option 1: Declarative component
function Dashboard() {
  return (
    <FeatureFlag name="newDashboard" fallback={<LegacyDashboard />}>
      <NewDashboard />
    </FeatureFlag>
  );
}

// Option 2: Hook for logic
function DashboardWithLogic() {
  const isNewDashboard = useFeatureFlag('newDashboard');

  useEffect(() => {
    if (isNewDashboard) {
      analytics.track('new_dashboard_viewed');
    }
  }, [isNewDashboard]);

  return isNewDashboard ? <NewDashboard /> : <LegacyDashboard />;
}
```

---

## Implementation Notes

### Naming Convention

| Context          | Pattern                   | Example                            |
| ---------------- | ------------------------- | ---------------------------------- |
| Backend env var  | `FEATURE_SNAKE_CASE`      | `FEATURE_NEW_DASHBOARD`            |
| Frontend env var | `VITE_FEATURE_SNAKE_CASE` | `VITE_FEATURE_NEW_DASHBOARD`       |
| Config property  | `camelCase`               | `config.featureFlags.newDashboard` |
| Type/Hook param  | `camelCase`               | `isFeatureEnabled('newDashboard')` |

### Flag Lifecycle

1. **Adding a flag**: Add to both schemas, set default to `false`, document
2. **Enabling a flag**: Set env var to `'true'`, restart application
3. **Removing a flag**: Remove conditional code first, then remove flag from schemas
4. **Sunset policy**: Flags should be removed within 3 months of full rollout

---

Last updated: 2026-01-28
