feat: Implement feature flag system with helpers, service, and loader
- Added feature flag helpers and decorators for easy usage in commands. - Created a feature flag service for managing flags, including initialization, caching, and evaluation strategies. - Implemented a loader to initialize the feature flag service on bot startup. - Defined types for feature flags, including configurations, contexts, evaluations, and statistics. - Provided examples of feature flag usage in commands, demonstrating various patterns such as A/B testing, gradual rollouts, and access control.
This commit is contained in:
300
README/FEATURE_FLAGS_QUICKSTART.md
Normal file
300
README/FEATURE_FLAGS_QUICKSTART.md
Normal file
@@ -0,0 +1,300 @@
|
||||
# 🎮 Feature Flags - Guía Rápida de Instalación
|
||||
|
||||
## 📦 Lo que se creó
|
||||
|
||||
1. **Schema de Prisma** (`prisma/schema.prisma`)
|
||||
- Modelo `FeatureFlag` con todos los campos necesarios
|
||||
|
||||
2. **Servicio Principal** (`src/core/services/FeatureFlagService.ts`)
|
||||
- Singleton con caché en memoria
|
||||
- Evaluación de flags con contexto
|
||||
- Estrategias de rollout (percentage, whitelist, blacklist, gradual, random)
|
||||
- Sistema de estadísticas
|
||||
|
||||
3. **Tipos TypeScript** (`src/core/types/featureFlags.ts`)
|
||||
- Tipos completos para el sistema
|
||||
- Interfaces para configuración y evaluación
|
||||
|
||||
4. **Helpers y Decoradores** (`src/core/lib/featureFlagHelpers.ts`)
|
||||
- `@RequireFeature` - Decorador para proteger métodos
|
||||
- `featureGuard` - Guard con respuesta automática
|
||||
- `isFeatureEnabled` - Check básico
|
||||
- `abTest` - A/B testing
|
||||
- Y más...
|
||||
|
||||
5. **Comando de Administración** (`src/commands/admin/featureflags.ts`)
|
||||
- Crear, listar, actualizar, eliminar flags
|
||||
- Configurar rollouts
|
||||
- Ver estadísticas
|
||||
|
||||
6. **Loader** (`src/core/loaders/featureFlagsLoader.ts`)
|
||||
- Inicialización automática del servicio
|
||||
|
||||
7. **Documentación** (`README/FEATURE_FLAGS_SYSTEM.md`)
|
||||
- Guía completa con ejemplos
|
||||
|
||||
8. **Scripts** (`scripts/setupFeatureFlags.ts`)
|
||||
- Setup inicial con flags de ejemplo
|
||||
|
||||
9. **Ejemplos** (`src/examples/featureFlagsUsage.ts`)
|
||||
- 11 patrones de uso diferentes
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Instalación (Paso a Paso)
|
||||
|
||||
### 1. Genera el cliente de Prisma
|
||||
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
Esto creará los tipos TypeScript para el nuevo modelo `FeatureFlag`.
|
||||
|
||||
### 2. Ejecuta la migración
|
||||
|
||||
```bash
|
||||
npx prisma migrate dev --name add_feature_flags
|
||||
```
|
||||
|
||||
Esto creará la tabla en tu base de datos.
|
||||
|
||||
### 3. (Opcional) Crea flags de ejemplo
|
||||
|
||||
```bash
|
||||
npx tsx scripts/setupFeatureFlags.ts
|
||||
```
|
||||
|
||||
Esto creará 8 feature flags de ejemplo para que puedas probar el sistema.
|
||||
|
||||
### 4. Integra el loader en tu bot
|
||||
|
||||
Abre tu archivo principal donde cargas los servicios (probablemente `src/main.ts` o similar) y añade:
|
||||
|
||||
```typescript
|
||||
import { loadFeatureFlags } from './core/loaders/featureFlagsLoader';
|
||||
|
||||
// Antes de iniciar el bot
|
||||
await loadFeatureFlags();
|
||||
```
|
||||
|
||||
O si tienes un sistema de loaders centralizado, simplemente impórtalo ahí.
|
||||
|
||||
### 5. ¡Listo! Empieza a usar feature flags
|
||||
|
||||
---
|
||||
|
||||
## 💡 Uso Rápido
|
||||
|
||||
### En un comando nuevo o existente:
|
||||
|
||||
```typescript
|
||||
import { featureGuard } from '@/core/lib/featureFlagHelpers';
|
||||
|
||||
export async function execute(interaction: CommandInteraction) {
|
||||
// Check si el flag está habilitado
|
||||
if (!await featureGuard('new_feature', interaction)) {
|
||||
return; // Automáticamente responde al usuario si está disabled
|
||||
}
|
||||
|
||||
// Tu código aquí (solo se ejecuta si el flag está habilitado)
|
||||
await interaction.reply('✨ Feature habilitada!');
|
||||
}
|
||||
```
|
||||
|
||||
### Crear un flag desde Discord:
|
||||
|
||||
```
|
||||
/featureflags create name:new_feature status:disabled target:global description:"Mi nueva feature"
|
||||
```
|
||||
|
||||
### Habilitarlo:
|
||||
|
||||
```
|
||||
/featureflags update flag:new_feature status:enabled
|
||||
```
|
||||
|
||||
### Rollout progresivo (25% de usuarios):
|
||||
|
||||
```
|
||||
/featureflags rollout flag:new_feature strategy:percentage percentage:25
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Casos de Uso Comunes
|
||||
|
||||
### 1. Desplegar una feature nueva gradualmente
|
||||
|
||||
```bash
|
||||
# Día 1: Deshabilitada, en desarrollo
|
||||
/featureflags create name:pvp_system status:disabled target:global
|
||||
|
||||
# Día 5: Beta testing con usuarios específicos
|
||||
/featureflags rollout flag:pvp_system strategy:whitelist
|
||||
# (añade IDs mediante código o actualiza la config)
|
||||
|
||||
# Día 10: 10% de usuarios
|
||||
/featureflags rollout flag:pvp_system strategy:percentage percentage:10
|
||||
|
||||
# Día 15: 50% de usuarios
|
||||
/featureflags update flag:pvp_system
|
||||
/featureflags rollout flag:pvp_system strategy:percentage percentage:50
|
||||
|
||||
# Día 20: 100% de usuarios
|
||||
/featureflags update flag:pvp_system status:enabled
|
||||
|
||||
# Día 30: Eliminar el flag y el código del check
|
||||
/featureflags delete flag:pvp_system
|
||||
```
|
||||
|
||||
### 2. A/B Testing
|
||||
|
||||
```typescript
|
||||
import { abTest, extractContext } from '@/core/lib/featureFlagHelpers';
|
||||
|
||||
const context = extractContext(interaction);
|
||||
|
||||
await abTest('new_algorithm', context, {
|
||||
variant: async () => {
|
||||
// 50% de usuarios ven esto
|
||||
return newAlgorithm();
|
||||
},
|
||||
control: async () => {
|
||||
// 50% ven esto
|
||||
return oldAlgorithm();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Kill Switch (emergencias)
|
||||
|
||||
Si hay un bug crítico en una feature:
|
||||
|
||||
```bash
|
||||
/featureflags update flag:problematic_feature status:maintenance
|
||||
```
|
||||
|
||||
Esto la deshabilitará inmediatamente sin necesidad de redeploy.
|
||||
|
||||
### 4. Eventos temporales
|
||||
|
||||
```typescript
|
||||
// Configurar con fechas
|
||||
await featureFlagService.setFlag({
|
||||
name: 'christmas_event',
|
||||
status: 'enabled',
|
||||
target: 'global',
|
||||
startDate: new Date('2025-12-15'),
|
||||
endDate: new Date('2025-12-31')
|
||||
});
|
||||
|
||||
// El flag se auto-deshabilitará el 1 de enero
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Monitoreo
|
||||
|
||||
### Ver estadísticas de uso:
|
||||
|
||||
```
|
||||
/featureflags stats flag:nombre_flag
|
||||
```
|
||||
|
||||
Te mostrará:
|
||||
- Total de evaluaciones
|
||||
- Cuántas veces se habilitó
|
||||
- Cuántas veces se deshabilitó
|
||||
- Tasa de habilitación (%)
|
||||
|
||||
### Ver todos los flags:
|
||||
|
||||
```
|
||||
/featureflags list
|
||||
```
|
||||
|
||||
### Refrescar caché:
|
||||
|
||||
```
|
||||
/featureflags refresh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Los flags no se aplican
|
||||
|
||||
1. Verifica que el servicio está inicializado:
|
||||
```typescript
|
||||
await loadFeatureFlags();
|
||||
```
|
||||
|
||||
2. Refresca el caché:
|
||||
```
|
||||
/featureflags refresh
|
||||
```
|
||||
|
||||
3. Verifica que el flag existe:
|
||||
```
|
||||
/featureflags info flag:nombre_flag
|
||||
```
|
||||
|
||||
### Errores de TypeScript
|
||||
|
||||
Si ves errores tipo "Property 'featureFlag' does not exist":
|
||||
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
Esto regenerará los tipos de Prisma.
|
||||
|
||||
### La migración falla
|
||||
|
||||
Si la migración falla, verifica tu conexión a la base de datos en `.env`:
|
||||
|
||||
```env
|
||||
XATA_DB="postgresql://..."
|
||||
XATA_SHADOW_DB="postgresql://..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Recursos
|
||||
|
||||
- **Documentación completa**: `README/FEATURE_FLAGS_SYSTEM.md`
|
||||
- **Ejemplos de uso**: `src/examples/featureFlagsUsage.ts`
|
||||
- **Tipos**: `src/core/types/featureFlags.ts`
|
||||
- **Servicio**: `src/core/services/FeatureFlagService.ts`
|
||||
- **Helpers**: `src/core/lib/featureFlagHelpers.ts`
|
||||
|
||||
---
|
||||
|
||||
## 🎉 ¡Listo!
|
||||
|
||||
Ahora tienes un sistema completo de Feature Flags. Puedes:
|
||||
|
||||
✅ Desplegar features sin miedo a romper producción
|
||||
✅ Hacer rollouts progresivos
|
||||
✅ A/B testing
|
||||
✅ Kill switches para emergencias
|
||||
✅ Eventos temporales
|
||||
✅ Beta testing con usuarios específicos
|
||||
✅ Monitorear el uso de cada feature
|
||||
|
||||
---
|
||||
|
||||
**Tip Pro**: Combina feature flags con tus deployments. Por ejemplo:
|
||||
|
||||
1. Despliega código nuevo con flag disabled
|
||||
2. Verifica que el deploy fue exitoso
|
||||
3. Habilita el flag progresivamente (10% → 50% → 100%)
|
||||
4. Monitorea métricas/errores
|
||||
5. Si hay problemas, desactiva el flag instantáneamente
|
||||
6. Una vez estable, elimina el flag y el código antiguo
|
||||
|
||||
---
|
||||
|
||||
Creado con 🎮 para Amayo Bot
|
||||
626
README/FEATURE_FLAGS_SYSTEM.md
Normal file
626
README/FEATURE_FLAGS_SYSTEM.md
Normal file
@@ -0,0 +1,626 @@
|
||||
# 🎮 Feature Flags System
|
||||
|
||||
Sistema completo de Feature Flags para control de funcionalidades, rollouts progresivos, A/B testing y toggles dinámicos.
|
||||
|
||||
## 📋 Índice
|
||||
|
||||
- [Instalación](#instalación)
|
||||
- [Conceptos](#conceptos)
|
||||
- [Uso Básico](#uso-básico)
|
||||
- [Ejemplos Avanzados](#ejemplos-avanzados)
|
||||
- [Comando de Administración](#comando-de-administración)
|
||||
- [Estrategias de Rollout](#estrategias-de-rollout)
|
||||
- [Best Practices](#best-practices)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Instalación
|
||||
|
||||
### 1. Migración de Base de Datos
|
||||
|
||||
```bash
|
||||
npx prisma migrate dev --name add_feature_flags
|
||||
```
|
||||
|
||||
### 2. Inicialización del Servicio
|
||||
|
||||
En tu `src/loaders/` o punto de entrada principal:
|
||||
|
||||
```typescript
|
||||
import { featureFlagService } from '@/core/services/FeatureFlagService';
|
||||
|
||||
// Inicializar el servicio
|
||||
await featureFlagService.initialize();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Conceptos
|
||||
|
||||
### Estados de Flags
|
||||
|
||||
- **`enabled`**: Habilitado para todos
|
||||
- **`disabled`**: Deshabilitado para todos
|
||||
- **`rollout`**: Rollout progresivo según estrategia
|
||||
- **`maintenance`**: Deshabilitado por mantenimiento
|
||||
|
||||
### Targets
|
||||
|
||||
- **`global`**: Aplica a todo el bot
|
||||
- **`guild`**: Aplica por servidor
|
||||
- **`user`**: Aplica por usuario
|
||||
- **`channel`**: Aplica por canal
|
||||
|
||||
### Estrategias de Rollout
|
||||
|
||||
- **`percentage`**: Basado en % de usuarios
|
||||
- **`whitelist`**: Solo IDs específicos
|
||||
- **`blacklist`**: Todos excepto IDs específicos
|
||||
- **`gradual`**: Rollout gradual en el tiempo
|
||||
- **`random`**: Aleatorio por sesión
|
||||
|
||||
---
|
||||
|
||||
## 💡 Uso Básico
|
||||
|
||||
### 1. En Comandos con Decorador
|
||||
|
||||
```typescript
|
||||
import { RequireFeature } from '@/core/lib/featureFlagHelpers';
|
||||
|
||||
class ShopCommand {
|
||||
@RequireFeature('new_shop_system', {
|
||||
fallbackMessage: '🔧 El nuevo sistema de tienda estará disponible pronto'
|
||||
})
|
||||
async execute(interaction: CommandInteraction) {
|
||||
// Este código solo se ejecuta si el flag está habilitado
|
||||
await interaction.reply('Bienvenido a la nueva tienda!');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Con Guard en el Handler
|
||||
|
||||
```typescript
|
||||
import { featureGuard } from '@/core/lib/featureFlagHelpers';
|
||||
|
||||
async function handleMineCommand(interaction: CommandInteraction) {
|
||||
// Check del flag
|
||||
if (!await featureGuard('new_mining_system', interaction)) {
|
||||
return; // Automáticamente responde al usuario
|
||||
}
|
||||
|
||||
// Código del comando nuevo
|
||||
await doNewMining(interaction);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Check Manual
|
||||
|
||||
```typescript
|
||||
import { isFeatureEnabledForInteraction } from '@/core/lib/featureFlagHelpers';
|
||||
|
||||
async function execute(interaction: CommandInteraction) {
|
||||
const useNewAlgorithm = await isFeatureEnabledForInteraction(
|
||||
'improved_algorithm',
|
||||
interaction
|
||||
);
|
||||
|
||||
if (useNewAlgorithm) {
|
||||
await newAlgorithm();
|
||||
} else {
|
||||
await oldAlgorithm();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Ejemplos Avanzados
|
||||
|
||||
### A/B Testing
|
||||
|
||||
```typescript
|
||||
import { abTest, extractContext } from '@/core/lib/featureFlagHelpers';
|
||||
|
||||
async function handleShop(interaction: CommandInteraction) {
|
||||
const context = extractContext(interaction);
|
||||
|
||||
const result = await abTest('new_shop_ui', context, {
|
||||
variant: async () => {
|
||||
// Nueva UI
|
||||
return buildNewShopUI();
|
||||
},
|
||||
control: async () => {
|
||||
// UI antigua
|
||||
return buildOldShopUI();
|
||||
}
|
||||
});
|
||||
|
||||
await interaction.reply(result);
|
||||
}
|
||||
```
|
||||
|
||||
### Múltiples Flags (AND)
|
||||
|
||||
```typescript
|
||||
import { requireAllFeatures, extractContext } from '@/core/lib/featureFlagHelpers';
|
||||
|
||||
async function handlePremiumFeature(interaction: CommandInteraction) {
|
||||
const context = extractContext(interaction);
|
||||
|
||||
const hasAccess = await requireAllFeatures(
|
||||
['premium_features', 'beta_access', 'new_ui'],
|
||||
context
|
||||
);
|
||||
|
||||
if (!hasAccess) {
|
||||
await interaction.reply('No tienes acceso a esta funcionalidad');
|
||||
return;
|
||||
}
|
||||
|
||||
// Código de la feature premium
|
||||
}
|
||||
```
|
||||
|
||||
### Múltiples Flags (OR)
|
||||
|
||||
```typescript
|
||||
import { requireAnyFeature, extractContext } from '@/core/lib/featureFlagHelpers';
|
||||
|
||||
async function handleSpecialEvent(interaction: CommandInteraction) {
|
||||
const context = extractContext(interaction);
|
||||
|
||||
const hasEventAccess = await requireAnyFeature(
|
||||
['halloween_event', 'christmas_event', 'beta_events'],
|
||||
context
|
||||
);
|
||||
|
||||
if (!hasEventAccess) {
|
||||
await interaction.reply('No hay eventos activos para ti');
|
||||
return;
|
||||
}
|
||||
|
||||
// Código del evento
|
||||
}
|
||||
```
|
||||
|
||||
### Con Fallback
|
||||
|
||||
```typescript
|
||||
import { withFeature, extractContext } from '@/core/lib/featureFlagHelpers';
|
||||
|
||||
async function getData(interaction: CommandInteraction) {
|
||||
const context = extractContext(interaction);
|
||||
|
||||
const data = await withFeature(
|
||||
'new_data_source',
|
||||
context,
|
||||
async () => {
|
||||
// Fuente nueva
|
||||
return fetchFromNewAPI();
|
||||
},
|
||||
async () => {
|
||||
// Fuente antigua (fallback)
|
||||
return fetchFromOldAPI();
|
||||
}
|
||||
);
|
||||
|
||||
return data;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Comando de Administración
|
||||
|
||||
### Crear un Flag
|
||||
|
||||
```
|
||||
/featureflags create name:new_shop_system status:disabled target:global description:"Nuevo sistema de tienda"
|
||||
```
|
||||
|
||||
### Listar Flags
|
||||
|
||||
```
|
||||
/featureflags list
|
||||
```
|
||||
|
||||
### Ver Info de un Flag
|
||||
|
||||
```
|
||||
/featureflags info flag:new_shop_system
|
||||
```
|
||||
|
||||
### Actualizar Estado
|
||||
|
||||
```
|
||||
/featureflags update flag:new_shop_system status:enabled
|
||||
```
|
||||
|
||||
### Configurar Rollout Progresivo
|
||||
|
||||
```
|
||||
/featureflags rollout flag:new_shop_system strategy:percentage percentage:25
|
||||
```
|
||||
|
||||
Esto habilitará la feature para el 25% de los usuarios.
|
||||
|
||||
### Configurar Rollout Gradual
|
||||
|
||||
```typescript
|
||||
// Programáticamente
|
||||
await featureFlagService.setFlag({
|
||||
name: 'new_combat_system',
|
||||
status: 'rollout',
|
||||
target: 'user',
|
||||
rolloutStrategy: 'gradual',
|
||||
rolloutConfig: {
|
||||
gradual: {
|
||||
startPercentage: 10, // Empieza con 10%
|
||||
targetPercentage: 100, // Llega al 100%
|
||||
durationDays: 7 // En 7 días
|
||||
}
|
||||
},
|
||||
startDate: new Date()
|
||||
});
|
||||
```
|
||||
|
||||
### Ver Estadísticas
|
||||
|
||||
```
|
||||
/featureflags stats flag:new_shop_system
|
||||
```
|
||||
|
||||
### Refrescar Caché
|
||||
|
||||
```
|
||||
/featureflags refresh
|
||||
```
|
||||
|
||||
### Eliminar Flag
|
||||
|
||||
```
|
||||
/featureflags delete flag:old_feature
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Estrategias de Rollout
|
||||
|
||||
### 1. Percentage (Porcentaje)
|
||||
|
||||
Distribuye la feature a un % de usuarios de forma determinista.
|
||||
|
||||
```typescript
|
||||
await featureFlagService.setFlag({
|
||||
name: 'feature_x',
|
||||
status: 'rollout',
|
||||
target: 'user',
|
||||
rolloutStrategy: 'percentage',
|
||||
rolloutConfig: {
|
||||
percentage: 50 // 50% de usuarios
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Whitelist (Lista Blanca)
|
||||
|
||||
Solo para IDs específicos.
|
||||
|
||||
```typescript
|
||||
await featureFlagService.setFlag({
|
||||
name: 'beta_features',
|
||||
status: 'rollout',
|
||||
target: 'user',
|
||||
rolloutStrategy: 'whitelist',
|
||||
rolloutConfig: {
|
||||
targetIds: [
|
||||
'123456789', // User ID 1
|
||||
'987654321' // User ID 2
|
||||
]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Blacklist (Lista Negra)
|
||||
|
||||
Para todos excepto IDs específicos.
|
||||
|
||||
```typescript
|
||||
await featureFlagService.setFlag({
|
||||
name: 'stable_feature',
|
||||
status: 'rollout',
|
||||
target: 'guild',
|
||||
rolloutStrategy: 'blacklist',
|
||||
rolloutConfig: {
|
||||
targetIds: [
|
||||
'guild_id_problematico'
|
||||
]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Gradual (Progresivo en el Tiempo)
|
||||
|
||||
Rollout gradual durante X días.
|
||||
|
||||
```typescript
|
||||
await featureFlagService.setFlag({
|
||||
name: 'major_update',
|
||||
status: 'rollout',
|
||||
target: 'user',
|
||||
rolloutStrategy: 'gradual',
|
||||
rolloutConfig: {
|
||||
gradual: {
|
||||
startPercentage: 5, // Empieza con 5%
|
||||
targetPercentage: 100, // Termina en 100%
|
||||
durationDays: 14 // Durante 14 días
|
||||
}
|
||||
},
|
||||
startDate: new Date() // Importante: define cuándo empieza
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✨ Best Practices
|
||||
|
||||
### 1. Nombres Claros y Descriptivos
|
||||
|
||||
```typescript
|
||||
// ❌ Mal
|
||||
'flag_1'
|
||||
'test'
|
||||
'new'
|
||||
|
||||
// ✅ Bien
|
||||
'new_shop_ui_v2'
|
||||
'improved_combat_algorithm'
|
||||
'halloween_2025_event'
|
||||
```
|
||||
|
||||
### 2. Siempre con Descripción
|
||||
|
||||
```typescript
|
||||
await featureFlagService.setFlag({
|
||||
name: 'new_mining_system',
|
||||
description: 'Sistema de minería rediseñado con durabilidad de herramientas',
|
||||
status: 'disabled',
|
||||
target: 'global'
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Rollouts Graduales para Cambios Grandes
|
||||
|
||||
Para cambios importantes, usa rollout gradual:
|
||||
|
||||
1. Día 1-3: 10% de usuarios
|
||||
2. Día 4-7: 50% de usuarios
|
||||
3. Día 8-14: 100% de usuarios
|
||||
|
||||
### 4. Limpiar Flags Obsoletos
|
||||
|
||||
Una vez que una feature está 100% desplegada y estable:
|
||||
|
||||
1. Elimina el flag
|
||||
2. Elimina el código del check
|
||||
3. Mantén solo la nueva implementación
|
||||
|
||||
### 5. Usar Whitelists para Beta Testers
|
||||
|
||||
```typescript
|
||||
await featureFlagService.setFlag({
|
||||
name: 'experimental_features',
|
||||
status: 'rollout',
|
||||
target: 'user',
|
||||
rolloutStrategy: 'whitelist',
|
||||
rolloutConfig: {
|
||||
targetIds: BETA_TESTER_IDS // Array de tus beta testers
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 6. Fechas de Expiración para Eventos
|
||||
|
||||
```typescript
|
||||
await featureFlagService.setFlag({
|
||||
name: 'christmas_2025_event',
|
||||
status: 'enabled',
|
||||
target: 'global',
|
||||
startDate: new Date('2025-12-01'),
|
||||
endDate: new Date('2025-12-31')
|
||||
});
|
||||
```
|
||||
|
||||
El flag se auto-deshabilitará después del 31 de diciembre.
|
||||
|
||||
### 7. Caché y Performance
|
||||
|
||||
El servicio cachea flags en memoria por 5 minutos. Si necesitas actualizaciones inmediatas:
|
||||
|
||||
```
|
||||
/featureflags refresh
|
||||
```
|
||||
|
||||
O programáticamente:
|
||||
|
||||
```typescript
|
||||
await featureFlagService.refreshCache();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔥 Casos de Uso Reales
|
||||
|
||||
### Lanzamiento de Comando Nuevo
|
||||
|
||||
```typescript
|
||||
// Fase 1: Desarrollo - Deshabilitado
|
||||
await featureFlagService.setFlag({
|
||||
name: 'pvp_arena_command',
|
||||
status: 'disabled',
|
||||
target: 'global'
|
||||
});
|
||||
|
||||
// Fase 2: Beta Testing - Solo whitelisted
|
||||
await featureFlagService.setFlag({
|
||||
name: 'pvp_arena_command',
|
||||
status: 'rollout',
|
||||
target: 'guild',
|
||||
rolloutStrategy: 'whitelist',
|
||||
rolloutConfig: {
|
||||
targetIds: ['guild_beta_1', 'guild_beta_2']
|
||||
}
|
||||
});
|
||||
|
||||
// Fase 3: Rollout Progresivo - 25%
|
||||
await featureFlagService.setFlag({
|
||||
name: 'pvp_arena_command',
|
||||
status: 'rollout',
|
||||
target: 'user',
|
||||
rolloutStrategy: 'percentage',
|
||||
rolloutConfig: { percentage: 25 }
|
||||
});
|
||||
|
||||
// Fase 4: Habilitado para Todos
|
||||
await featureFlagService.setFlag({
|
||||
name: 'pvp_arena_command',
|
||||
status: 'enabled',
|
||||
target: 'global'
|
||||
});
|
||||
|
||||
// Fase 5: Cleanup - Eliminar flag y código del check
|
||||
await featureFlagService.removeFlag('pvp_arena_command');
|
||||
```
|
||||
|
||||
### Migración de Sistema Antiguo a Nuevo
|
||||
|
||||
```typescript
|
||||
// En el comando
|
||||
async function handleInventory(interaction: CommandInteraction) {
|
||||
const context = extractContext(interaction);
|
||||
|
||||
await abTest('inventory_system_v2', context, {
|
||||
variant: async () => {
|
||||
// Sistema nuevo
|
||||
return await newInventorySystem.show(interaction);
|
||||
},
|
||||
control: async () => {
|
||||
// Sistema antiguo
|
||||
return await oldInventorySystem.show(interaction);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Luego gradualmente aumentas el % hasta 100% y eliminas el código antiguo.
|
||||
|
||||
### Kill Switch para Emergencias
|
||||
|
||||
```typescript
|
||||
// Si hay un bug crítico en una feature:
|
||||
await featureFlagService.setFlag({
|
||||
name: 'problematic_feature',
|
||||
status: 'maintenance', // Deshabilitado inmediatamente
|
||||
target: 'global'
|
||||
});
|
||||
|
||||
// O via comando Discord:
|
||||
// /featureflags update flag:problematic_feature status:maintenance
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 API Reference
|
||||
|
||||
Ver `src/core/types/featureFlags.ts` para tipos completos.
|
||||
|
||||
### FeatureFlagService
|
||||
|
||||
```typescript
|
||||
// Inicializar
|
||||
await featureFlagService.initialize();
|
||||
|
||||
// Check si está habilitado
|
||||
const enabled = await featureFlagService.isEnabled('flag_name', context);
|
||||
|
||||
// Crear/actualizar flag
|
||||
await featureFlagService.setFlag(config);
|
||||
|
||||
// Eliminar flag
|
||||
await featureFlagService.removeFlag('flag_name');
|
||||
|
||||
// Obtener flag
|
||||
const flag = featureFlagService.getFlag('flag_name');
|
||||
|
||||
// Obtener todos los flags
|
||||
const flags = featureFlagService.getFlags();
|
||||
|
||||
// Estadísticas
|
||||
const stats = featureFlagService.getStats('flag_name');
|
||||
const allStats = featureFlagService.getAllStats();
|
||||
|
||||
// Refrescar caché
|
||||
await featureFlagService.refreshCache();
|
||||
featureFlagService.clearEvaluationCache();
|
||||
```
|
||||
|
||||
### Helpers
|
||||
|
||||
```typescript
|
||||
// Check básico
|
||||
await isFeatureEnabled(flagName, context);
|
||||
await isFeatureEnabledForInteraction(flagName, interaction);
|
||||
|
||||
// Guards
|
||||
await featureGuard(flagName, interaction, options);
|
||||
|
||||
// Decorador
|
||||
@RequireFeature('flag_name', options)
|
||||
|
||||
// A/B Testing
|
||||
await abTest(flagName, context, { variant, control });
|
||||
|
||||
// Wrapper
|
||||
await withFeature(flagName, context, fn, fallback);
|
||||
|
||||
// Múltiples flags
|
||||
await requireAllFeatures(flags, context); // AND
|
||||
await requireAnyFeature(flags, context); // OR
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎮 Integración con tu Bot
|
||||
|
||||
El sistema se integra automáticamente si añades la inicialización en tu loader:
|
||||
|
||||
```typescript
|
||||
// src/loaders/featureFlagsLoader.ts
|
||||
import { featureFlagService } from '../services/FeatureFlagService';
|
||||
import logger from '../lib/logger';
|
||||
|
||||
export async function loadFeatureFlags() {
|
||||
try {
|
||||
await featureFlagService.initialize();
|
||||
logger.info('[FeatureFlags] Sistema inicializado');
|
||||
} catch (error) {
|
||||
logger.error('[FeatureFlags] Error al inicializar:', error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Luego en tu `main.ts` o donde cargues servicios:
|
||||
|
||||
```typescript
|
||||
import { loadFeatureFlags } from './loaders/featureFlagsLoader';
|
||||
|
||||
// ...
|
||||
await loadFeatureFlags();
|
||||
// ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Creado con 🎮 para el bot Amayo
|
||||
Reference in New Issue
Block a user