Feature Flags System
Overview
The FishingLog backend includes a comprehensive feature flag system similar to LaunchDarkly, allowing you to control feature availability across different environments (Development, Staging, Production). This enables you to:
- Turn features on/off per environment
- Gradually roll out features using percentage-based rollouts
- Target specific users for beta testing
- Control features through the admin panel without code deployments
Architecture
Components
- FeatureFlag Entity - Database entity storing feature flag configuration
- FeatureFlagService - Service for checking feature availability
- AdminFeatureFlagController - Admin API for managing feature flags
- RequireFeatureAttribute - Authorization attribute for protecting endpoints
Key Features
- Environment-Specific Control: Enable/disable features per environment (Development, Staging, Production)
- Percentage Rollouts: Gradually release features to a percentage of users
- User Targeting: Target specific users for beta testing
- Caching: Feature flags are cached for 5 minutes to reduce database queries
- Audit Logging: All changes are logged for compliance and debugging
Usage
Protecting Controllers/Endpoints
Use the [RequireFeature] attribute to protect entire controllers or individual endpoints:
[ApiController]
[Route("api/[controller]")]
[Authorize]
[RequireFeature("Circles")] // Feature must be enabled
public class CircleController : ControllerBase
{
// All endpoints require Circles feature to be enabled
}
Or protect individual endpoints:
[HttpPost]
[RequireFeature("Tournaments")]
public async Task<ActionResult> CreateTournament(...)
{
// Only accessible if Tournaments feature is enabled
}
Checking Features in Code
Inject FeatureFlagService and check features programmatically:
public class MyService
{
private readonly FeatureFlagService _featureFlagService;
public MyService(FeatureFlagService featureFlagService)
{
_featureFlagService = featureFlagService;
}
public async Task DoSomething(Guid userId)
{
var isEnabled = await _featureFlagService.IsFeatureEnabledAsync("Circles", userId);
if (isEnabled)
{
// Feature is enabled for this user/environment
}
}
}
Synchronous Check (Uses Cache)
For performance-critical code that runs frequently:
// Uses cached value (must have been checked async first)
var isEnabled = _featureFlagService.IsFeatureEnabled("Circles", userId);
Admin Panel API
Get All Feature Flags
GET /api/admin/feature-flags
GET /api/admin/feature-flags?category=Social
Get Specific Feature Flag
GET /api/admin/feature-flags/{key}
Get Current Environment Flags
GET /api/admin/feature-flags/current-environment
Create Feature Flag
POST /api/admin/feature-flags
Content-Type: application/json
{
"key": "NewFeature",
"name": "New Feature",
"description": "Description of the feature",
"category": "Social",
"isEnabledInDevelopment": true,
"isEnabledInStaging": true,
"isEnabledInProduction": false,
"rolloutPercentage": null,
"targetUserIds": null,
"isActive": true,
"reason": "Initial creation"
}
Update Feature Flag
PUT /api/admin/feature-flags/{key}
Content-Type: application/json
{
"isEnabledInProduction": true,
"rolloutPercentage": 25,
"reason": "Rolling out to 25% of users"
}
Delete Feature Flag
DELETE /api/admin/feature-flags/{key}
Feature Flag Configuration
Basic Configuration
- Key: Unique identifier (e.g., "Circles", "Tournaments")
- Name: Human-readable name
- Description: What the feature does
- Category: Grouping (e.g., "Social", "Tournaments", "Gear")
Environment Flags
- IsEnabledInDevelopment: Enable in Development environment
- IsEnabledInStaging: Enable in Staging environment
- IsEnabledInProduction: Enable in Production environment
Advanced Features
- RolloutPercentage: 0-100, gradually roll out to percentage of users
- TargetUserIds: Array of user IDs for beta testing (overrides rollout percentage)
- IsActive: Can disable the feature flag entirely
Example: Circles Feature
The Circles feature is configured as follows:
- Development: ✅ Enabled
- Staging: ✅ Enabled
- Production: ❌ Disabled
This allows testing in dev/staging while keeping it disabled in production until there are enough users.
Cache Management
Feature flags are cached for 5 minutes. The cache is automatically invalidated when:
- A feature flag is created
- A feature flag is updated
- A feature flag is deleted
You can manually invalidate the cache:
_featureFlagService.InvalidateCache(); // All flags
_featureFlagService.InvalidateCache("Circles"); // Specific flag
Best Practices
- Use Descriptive Keys: Use clear, consistent naming (e.g., "Circles", "Tournaments", "Charters")
- Document Features: Always provide a description explaining what the feature does
- Use Categories: Group related features together
- Gradual Rollouts: Use percentage rollouts for new features
- Beta Testing: Use TargetUserIds for early access testing
- Monitor Changes: Review audit logs regularly
- Clean Up: Remove feature flags for features that are fully rolled out
Migration and Seeding
Feature flags are automatically seeded on database initialization. The initial seed includes:
- Circles: Enabled in Dev/Staging, disabled in Production
To add more feature flags, update FeatureFlagSeeder.cs or use the admin API.
Security
- Feature flag management requires
AdminOnlyauthorization - All changes are logged in the audit log
- Feature flags cannot be modified by regular users
- The
RequireFeatureattribute returns 404 (not 403) to hide disabled features
Future Enhancements
Potential future improvements:
- Scheduled enable/disable dates
- A/B testing support
- Feature flag analytics
- Frontend integration for conditional UI rendering
- Webhook notifications on flag changes