# Feature Flag System The feature flag system enables controlled beta testing of new features in production without requiring a separate test environment. ## Overview Feature flags allow you to: - Test new features with a limited audience before full rollout - Enable/disable features without code changes or redeployment - Control access per guild, user, or role - Eliminate environment drift between test and production ## Architecture ### Database Schema **`feature_flags` table:** | Column | Type | Description | |--------|------|-------------| | `id` | serial | Primary key | | `name` | varchar(100) | Unique flag identifier | | `enabled` | boolean | Whether the flag is active | | `description` | text | Human-readable description | | `created_at` | timestamp | Creation time | | `updated_at` | timestamp | Last update time | **`feature_flag_access` table:** | Column | Type | Description | |--------|------|-------------| | `id` | serial | Primary key | | `flag_id` | integer | References feature_flags.id | | `guild_id` | bigint | Guild whitelist (nullable) | | `user_id` | bigint | User whitelist (nullable) | | `role_id` | bigint | Role whitelist (nullable) | | `created_at` | timestamp | Creation time | ### Service Layer The `featureFlagsService` (`shared/modules/feature-flags/feature-flags.service.ts`) provides: ```typescript // Check if a flag is globally enabled await featureFlagsService.isFlagEnabled("trading_system"); // Check if a user has access to a flagged feature await featureFlagsService.hasAccess("trading_system", { guildId: "123456789", userId: "987654321", memberRoles: ["role1", "role2"] }); // Create a new feature flag await featureFlagsService.createFlag("new_feature", "Description"); // Enable/disable a flag await featureFlagsService.setFlagEnabled("new_feature", true); // Grant access to users/roles/guilds await featureFlagsService.grantAccess("new_feature", { userId: "123" }); await featureFlagsService.grantAccess("new_feature", { roleId: "456" }); await featureFlagsService.grantAccess("new_feature", { guildId: "789" }); // List all flags or access records await featureFlagsService.listFlags(); await featureFlagsService.listAccess("new_feature"); ``` ## Usage ### Marking a Command as Beta Add `beta: true` to any command definition: ```typescript export const newFeature = createCommand({ data: new SlashCommandBuilder() .setName("newfeature") .setDescription("A new experimental feature"), beta: true, // Marks this command as a beta feature execute: async (interaction) => { // Implementation }, }); ``` By default, the command name is used as the feature flag name. To use a custom flag name: ```typescript export const trade = createCommand({ data: new SlashCommandBuilder() .setName("trade") .setDescription("Trade items with another user"), beta: true, featureFlag: "trading_system", // Custom flag name execute: async (interaction) => { // Implementation }, }); ``` ### Access Control Flow When a user attempts to use a beta command: 1. **Check if flag exists and is enabled** - Returns false if flag doesn't exist or is disabled 2. **Check guild whitelist** - User's guild has access if `guild_id` matches 3. **Check user whitelist** - User has access if `user_id` matches 4. **Check role whitelist** - User has access if any of their roles match If none of these conditions are met, the user sees: > **Beta Feature** > This feature is currently in beta testing and not available to all users. Stay tuned for the official release! ## Admin Commands The `/featureflags` command (Administrator only) provides full management: ### Subcommands | Command | Description | |---------|-------------| | `/featureflags list` | List all feature flags with status | | `/featureflags create [description]` | Create a new flag (disabled by default) | | `/featureflags delete ` | Delete a flag and all access records | | `/featureflags enable ` | Enable a flag globally | | `/featureflags disable ` | Disable a flag globally | | `/featureflags grant [user\|role]` | Grant access to a user or role | | `/featureflags revoke ` | Revoke access by record ID | | `/featureflags access ` | List all access records for a flag | ### Example Workflow ``` 1. Create the flag: /featureflags create trading_system "Item trading between users" 2. Grant access to beta testers: /featureflags grant trading_system user:@beta_tester /featureflags grant trading_system role:@Beta Testers 3. Enable the flag: /featureflags enable trading_system 4. View access list: /featureflags access trading_system 5. When ready for full release: - Remove beta: true from the command - Delete the flag: /featureflags delete trading_system ``` ## Best Practices 1. **Descriptive Names**: Use snake_case names that clearly describe the feature 2. **Document Flags**: Always add a description when creating flags 3. **Role-Based Access**: Prefer role-based access over user-based for easier management 4. **Clean Up**: Delete flags after features are fully released 5. **Testing**: Always test with a small group before wider rollout ## Implementation Files | File | Purpose | |------|---------| | `shared/db/schema/feature-flags.ts` | Database schema | | `shared/modules/feature-flags/feature-flags.service.ts` | Service layer | | `shared/lib/types.ts` | Command interface with beta properties | | `bot/lib/handlers/CommandHandler.ts` | Beta access check | | `bot/commands/admin/featureflags.ts` | Admin command |