Files
discord-rpg-concept/docs/feature-flags.md
syntaxbullet 1ff24b0f7f docs: add feature flags system documentation
Document feature flag architecture, usage, admin commands, and best practices for beta testing features in production.
2026-02-12 14:54:51 +01:00

5.4 KiB

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:

// 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:

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:

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