Files
gridpilot.gg/docs/FEATURE_ARCHITECTURE.md
2026-01-07 16:20:19 +01:00

6.4 KiB

Feature Architecture: Modes vs Feature Flags

The Problem

Your current system has two overlapping concepts that conflict:

  • Feature Flags (features.config.ts) - Controls individual features
  • App Modes (NEXT_PUBLIC_GRIDPILOT_MODE) - Controls overall platform visibility

This creates confusion because:

  1. Development shows only landing page but feature config says everything is enabled
  2. It's unclear which system controls what
  3. Teams don't know when to use mode vs feature flag

The Solution: Clear Separation

Two-Tier System

┌─────────────────────────────────────────┐
│           APP MODE (Tier 1)             │
│  Controls WHAT the platform shows       │
│  - pre-launch: Landing page only        │
│  - alpha: Full platform access          │
│  - beta: Production-ready features      │
└─────────────────────────────────────────┘
           ↓
┌─────────────────────────────────────────┐
│       FEATURE FLAGS (Tier 2)            │
│  Controls WHICH features are enabled    │
│  - Individual feature toggles           │
│  - Rollout control                      │
│  - A/B testing                          │
└─────────────────────────────────────────┘

Mode = Platform Scope

App Mode defines what the entire platform can do:

  • pre-launch: "We're not ready yet"

    • Shows: Landing page, Discord CTA, FAQ
    • Hides: All navigation, dashboard, leagues, teams, races
    • Purpose: Marketing/teaser phase
  • alpha: "Early access for testers"

    • Shows: Everything + alpha badges
    • Purpose: Internal testing, early adopters
    • All features enabled by default
  • beta: "Public beta"

    • Shows: Production features only
    • Purpose: Gradual rollout to real users
    • Features controlled individually

Feature Flags = Feature Control

Feature flags control individual features within a mode:

// In alpha mode, all features are ON by default
// But you can still disable specific ones for testing
{
  "platform.dashboard": "enabled",
  "platform.leagues": "enabled", 
  "platform.teams": "disabled", // Testing without teams
  "sponsors.portal": "enabled",
  "admin.dashboard": "enabled"
}

Simple Mental Model

For Developers: "The Restaurant Analogy"

APP MODE = Restaurant State
├── "Closed" (pre-launch) → Only show entrance/menu
├── "Soft Opening" (alpha) → Full menu, everything available
└── "Grand Opening" (beta) → Full menu, but some items may be 86'd

FEATURE FLAGS = Menu Items
├── Each dish can be: Available / 86'd / Coming Soon
├── Works within whatever restaurant state you're in
└── Lets you control individual items precisely

Decision Tree

Question: "What should I use?"
│
├─ "Is the platform ready for ANY users?"
│  ├─ No → Use APP MODE = pre-launch
│  └─ Yes → Continue...
│
├─ "Are we in testing or production?"
│  ├─ Testing → Use APP MODE = alpha
│  └─ Production → Use APP MODE = beta
│
└─ "Do I need to control a specific feature?"
   └─ Yes → Use FEATURE FLAGS (regardless of mode)

Implementation Rules

Rule 1: Mode Controls Visibility

// ❌ WRONG: Using feature flags to hide entire platform
{
  "platform": {
    "dashboard": "disabled",
    "leagues": "disabled",
    "teams": "disabled"
  }
}

// ✅ CORRECT: Use mode for platform-wide visibility
// NEXT_PUBLIC_GRIDPILOT_MODE=pre-launch

Rule 2: Feature Flags Control Granularity

// ✅ CORRECT: Feature flags for fine-grained control
{
  "platform": {
    "dashboard": "enabled",
    "leagues": "enabled",
    "teams": "enabled",  // But specific team features...
    "teams.create": "disabled",  // ...can be toggled
    "teams.delete": "coming_soon"
  }
}

Rule 3: Alpha Mode = Auto-Enable

// In alpha mode, ALL features are enabled automatically
// Feature flags can only DISABLE, not enable
// This eliminates configuration complexity

// Feature flag service in alpha mode:
if (mode === 'alpha') {
  return new FeatureFlagService([
    'all', 'features', 'enabled', 'by', 'default'
  ]);
}

Migration Path

Current State (Confusing)

Development:
- Mode: pre-launch (default)
- Features: All enabled in config
- Result: Shows landing page only ❌

Why? Because mode overrides feature config

Target State (Clear)

Development:
- Mode: alpha (explicit)
- Features: All auto-enabled
- Result: Full platform with alpha badges ✅

Production:
- Mode: beta
- Features: Controlled individually
- Result: Gradual rollout ✅

Configuration Examples

Simple Mode Config

// apps/website/.env.development
NEXT_PUBLIC_GRIDPILOT_MODE=alpha

// apps/website/.env.production  
NEXT_PUBLIC_GRIDPILOT_MODE=beta

Feature Flags (Only When Needed)

// Only override defaults when necessary
// apps/api/src/config/features.config.ts
{
  production: {
    platform: {
      dashboard: 'enabled',
      leagues: 'enabled',
      teams: 'enabled',
      races: 'enabled',
      leaderboards: 'enabled'
    },
    sponsors: {
      portal: 'enabled',
      dashboard: 'enabled',
      management: 'disabled' // Not ready yet
    }
  }
}

Benefits

  1. No Confusion: Clear separation of concerns
  2. Less Config: Alpha mode needs zero feature config
  3. Easy Onboarding: New devs just set mode=alpha
  4. Powerful Control: Feature flags still available when needed
  5. Backward Compatible: Existing code works with new concept

Quick Reference

Scenario Mode Feature Flags Result
Local dev alpha None needed Full platform
CI/CD tests alpha None needed Full platform
Staging beta Some disabled Controlled rollout
Production beta Gradual enable Public launch
Marketing site pre-launch N/A Landing page only

This eliminates the contradiction and gives you clear power: Mode for scope, flags for granularity.