6.4 KiB
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:
- Development shows only landing page but feature config says everything is enabled
- It's unclear which system controls what
- 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
- No Confusion: Clear separation of concerns
- Less Config: Alpha mode needs zero feature config
- Easy Onboarding: New devs just set mode=alpha
- Powerful Control: Feature flags still available when needed
- 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.