docs: add feature flags system documentation
Document feature flag architecture, usage, admin commands, and best practices for beta testing features in production.
This commit is contained in:
168
docs/feature-flags.md
Normal file
168
docs/feature-flags.md
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
# 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 <name> [description]` | Create a new flag (disabled by default) |
|
||||||
|
| `/featureflags delete <name>` | Delete a flag and all access records |
|
||||||
|
| `/featureflags enable <name>` | Enable a flag globally |
|
||||||
|
| `/featureflags disable <name>` | Disable a flag globally |
|
||||||
|
| `/featureflags grant <name> [user\|role]` | Grant access to a user or role |
|
||||||
|
| `/featureflags revoke <id>` | Revoke access by record ID |
|
||||||
|
| `/featureflags access <name>` | 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 |
|
||||||
Reference in New Issue
Block a user