← maurobernal.com.ar

Etiqueta: FeatureGate

  • Feature Management en .NET (Parte 2): Filtros Dinámicos, Hot Reload y Feature Flags vs. Versionado

    En el post anterior vimos los conceptos base de Feature Flags con Microsoft.FeatureManagement. Acá vamos a profundizar en tres capacidades que marcan la diferencia entre usar flags como simples condicionales y usarlos como una herramienta real de arquitectura: filtros dinámicos con Minimal APIs, recarga en caliente como Kill Switch, y la diferencia conceptual con el versionado de APIs. También incluyo la comparativa completa entre ambos enfoques, que es algo que me preguntan frecuentemente.

    Feature Flags Estáticas en Minimal APIs

    El caso más directo: un interruptor booleano evaluado en tiempo de ejecución. Lo interesante con Minimal APIs es que la integración es aún más limpia que en controllers tradicionales — IFeatureManager se inyecta directamente en el handler del endpoint.

    // appsettings.json
    {
      "FeatureManagement": {
        "NewCheckout": false
      }
    }
    
    // Program.cs
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddFeatureManagement();
    var app = builder.Build();
    
    app.MapGet("/checkout", async (IFeatureManager fm) => 
    {
        if (await fm.IsEnabledAsync("NewCheckout"))
        {
            return Results.Ok("Procesando con el NUEVO flujo de pago.");
        }
        
        return Results.Ok("Procesando con el flujo de pago CLÁSICO.");
    });
    
    app.Run();

    La evaluación ocurre en cada petición. Cambiar false por true en el archivo de configuración actualiza el comportamiento sin reiniciar el proceso.

    Filtros Dinámicos: Canary Releases y Pruebas A/B

    Los flags booleanos globales resuelven muchos casos, pero en producción real necesitás más granularidad. Los Feature Filters permiten activar características basándose en reglas contextuales evaluadas en cada petición. .NET incluye dos filtros nativos listos para usar:

    • PercentageFilter: activa el flag para un porcentaje del tráfico. Ideal para Canary releases.
    • TimeWindowFilter: activa el flag solo durante una ventana temporal. Ideal para lanzamientos programados o mantenimientos.

    Ejemplo con PercentageFilter + FeatureGate en un controller:

    // appsettings.json — 50% del tráfico ve el motor Beta
    {
      "FeatureManagement": {
        "BetaSearch": {
          "EnabledFor": [
            {
              "Name": "Percentage",
              "Parameters": { "Value": 50 }
            }
          ]
        }
      }
    }
    
    // Program.cs — registrar el filtro
    builder.Services.AddFeatureManagement()
        .AddFeatureFilter<PercentageFilter>();
    
    // SearchController.cs
    [ApiController]
    [Route("api/[controller]")]
    public class SearchController : ControllerBase
    {
        [HttpGet]
        [FeatureGate("BetaSearch")] // 404 automático para el 50% que no aplica
        public IActionResult Get()
        {
            return Ok("Resultados del motor de búsqueda Beta.");
        }
    }

    Lo que me gusta de este enfoque es que el [FeatureGate] hace que el endpoint directamente no exista para quien no aplica al filtro. No hay lógica de «si no entra acá, redirigí allá» — el framework lo maneja solo.

    Hot Reload y Kill Switches: El Poder Real de los Feature Flags

    Esta es la capacidad que más valoro en producción. El sistema de Feature Management se integra con IOptionsSnapshot, el mecanismo reactivo de configuración de .NET. Cuando modificás la fuente de configuración — ya sea el appsettings.json, una variable de entorno o Azure App Configuration — el estado del flag se actualiza en memoria automáticamente en la siguiente petición. Sin reinicio, sin downtime.

    El caso de uso más crítico: el Kill Switch. Si una feature nueva empieza a generar errores en producción, en lugar de hacer rollback del deploy entero (proceso que puede tomar minutos), simplemente cambiás el flag a false y la próxima petición ya usa el código anterior.

    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddFeatureManagement();
    var app = builder.Build();
    
    app.MapGet("/health", async (IFeatureManager fm) => 
    {
        // Un operador cambia "MaintenanceMode" a true en appsettings.json.
        // La SIGUIENTE petición refleja el cambio al instante, sin reiniciar el proceso.
        bool isMaintenance = await fm.IsEnabledAsync("MaintenanceMode");
        
        if (isMaintenance)
        {
            return Results.StatusCode(503); // Service Unavailable
        }
        
        return Results.Ok("Sistema Operativo");
    });
    
    app.Run();

    En proyectos donde integramos con Azure App Configuration, esto va un paso más allá: podés cambiar el flag desde un panel web, sin acceso al servidor, y el efecto es inmediato. Para equipos de guardia nocturna o sistemas de alta disponibilidad, esto es invaluable.

    Feature Flags vs. Versionado de API: La Confusión Más Frecuente

    Es la pregunta que más aparece cuando presento este tema. Son herramientas distintas para problemas distintos, aunque superficialmente parecen hacer lo mismo (exponer diferentes comportamientos según contexto).

    CriterioFeature FlagsVersionado de API
    Problema que resuelveExposición controlada: gestiona quién y cuándo accede a un cambioCompatibilidad de contratos: clientes antiguos no se rompen ante cambios destructivos
    Tiempo de vidaEfímero. Una vez estable, la flag y el código viejo deben eliminarse (deuda técnica)Prolongado. v1 y v2 coexisten por años
    Mecanismo de controlConfiguración externa, reglas de negocio, evaluación en runtimeRutas estáticas (/api/v1/), headers HTTP o referencias de ensamblado
    InfraestructuraUn solo binario con todos los caminos lógicosPueden ser múltiples binarios o deploys paralelos

    La regla que uso: si el cambio es interno y temporal (lo vas a limpiar cuando sea estable), usá Feature Flags. Si el cambio rompe el contrato público con consumidores externos que necesitan tiempo para migrar, usá versionado. En proyectos complejos, ambos conviven sin problema.

    Próximos pasos

    Estos tres patrones cubren la gran mayoría de los casos de uso cotidianos. El siguiente nivel son los Custom Feature Filters — filtros personalizados que habilitan flags según el tenant de la base de datos, el rol del usuario, la región geográfica o cualquier lógica de negocio propia. Y después de eso, la integración con Azure App Configuration para centralizar todos los flags en un servicio externo con targeting por usuario o segmento.

    ¿Te interesa alguno de esos dos temas para el próximo artículo? Dejalo en los comentarios.

  • Feature Flags en .NET: Separando el Deploy del Release con Microsoft.FeatureManagement

    Uno de los cambios más importantes que tuve en mi forma de trabajar en los últimos años fue aprender a separar el despliegue del lanzamiento. Hasta ese momento, cada vez que terminaba una feature la subía a producción directamente. Si algo salía mal, había que hacer rollback de todo. Con los Feature Flags, eso cambió completamente: el código puede estar en producción, pero inactivo. Lo activás cuando querés, para quien querés, sin tocar el deploy. En .NET esto se implementa con Microsoft.FeatureManagement, y en este artículo te muestro cómo funciona.

    ¿Qué son los Feature Flags y por qué importan?

    Los Feature Flags (también llamados Feature Toggles) son interruptores que habilitan o deshabilitan funcionalidades en tiempo de ejecución. Su valor principal no es técnico sino estratégico: permiten que el equipo de desarrollo haga merge continuo a la rama principal (Trunk-based development) sin bloquear a nadie, mientras el código de la nueva feature espera desactivado hasta que esté listo para salir.

    Esto los diferencia del versionado de APIs, que apunta a mantener contratos estables para distintos consumidores. Los Feature Flags apuntan a estrategias de CI/CD: lanzamientos progresivos (Canary releases), pruebas A/B y acceso gradual a nuevas funcionalidades. Y lo más importante: su estado se modifica en caliente, cambiando el appsettings.json o variables de entorno, sin recompilar ni redesplegar.

    1. Evaluación Básica: El Interruptor On/Off

    La forma más simple de Feature Management. Un flag booleano que habilita o deshabilita una ruta de código. Existe principalmente para resolver el problema de los feature branches de larga duración: en lugar de mantener una rama separada durante semanas y sufrir un merge doloroso, el código convive en la rama principal desactivado, esperando su momento.

    Gracias a la recarga dinámica de IConfiguration en .NET, cambiar el valor en el archivo de configuración actualiza el comportamiento de la app instantáneamente, sin reiniciarla.

    Configuración en appsettings.json:

    {
      "FeatureManagement": {
        "NewPaymentGateway": false
      }
    }

    Implementación en C#:

    using Microsoft.FeatureManagement;
    
    // Registro en Program.cs:
    // builder.Services.AddFeatureManagement();
    
    public class PaymentService
    {
        private readonly IFeatureManager _featureManager;
    
        public PaymentService(IFeatureManager featureManager)
        {
            _featureManager = featureManager;
        }
    
        public async Task ProcessPaymentAsync(Order order)
        {
            // Se evalúa en tiempo de ejecución.
            // Cambiar el flag en appsettings.json actualiza este comportamiento sin reiniciar la app.
            if (await _featureManager.IsEnabledAsync("NewPaymentGateway"))
            {
                await ProcessWithStripeAsync(order); // Nueva pasarela
            }
            else
            {
                await ProcessWithLegacySystemAsync(order); // Sistema anterior
            }
        }
    }

    La primera vez que usé esto en un proyecto real, cambié el comportamiento de la pasarela de pago en producción cambiando un false por true en un archivo de configuración. Sin deploy, sin downtime, sin nervios.

    2. FeatureGate: Bloqueando Endpoints Completos

    A veces no alcanza con un condicional dentro de la lógica de negocio. Si la feature expone un endpoint nuevo, queremos que ese endpoint directamente no exista mientras el flag esté apagado. El atributo [FeatureGate] intercepta la petición HTTP en el pipeline de ASP.NET Core y devuelve automáticamente un 404 Not Found si el flag está desactivado. La lógica de negocio queda limpia, sin condicionales.

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.FeatureManagement.Mvc;
    
    [ApiController]
    [Route("api/[controller]")]
    public class ReportsController : ControllerBase
    {
        // Este endpoint solo existe si "AdvancedReporting" es true.
        // Si está apagado, responde 404 automáticamente.
        [HttpGet("advanced")]
        [FeatureGate("AdvancedReporting")]
        public IActionResult GetAdvancedReport()
        {
            return Ok(new { Message = "Reporte avanzado generado exitosamente." });
        }
    }

    Útil para exponer funcionalidades premium, features en beta o endpoints que todavía están en desarrollo pero ya mergeados a main.

    3. Feature Filters: Reglas Dinámicas y Canary Releases

    Los flags booleanos globales son el punto de partida, pero las situaciones reales suelen ser más complejas. ¿Qué pasa si querés activar una feature solo para el 10% del tráfico? ¿O solo durante ciertos horarios? ¿O solo para usuarios internos? Ahí entran los Feature Filters.

    El más directo para Canary releases es el filtro de porcentaje. En lugar de un booleano, definís una regla:

    {
      "FeatureManagement": {
        "RedesignedUI": {
          "EnabledFor": [
            {
              "Name": "Percentage",
              "Parameters": {
                "Value": 25
              }
            }
          ]
        }
      }
    }

    El 25% de las peticiones verán la nueva UI. El 75% restante, la versión anterior. Sin tocar el código.

    using Microsoft.FeatureManagement;
    using Microsoft.FeatureManagement.FeatureFilters;
    
    // Registro en Program.cs incluyendo el filtro:
    // builder.Services.AddFeatureManagement().AddFeatureFilter<PercentageFilter>();
    
    public class UIController : Controller
    {
        private readonly IFeatureManager _featureManager;
    
        public UIController(IFeatureManager featureManager)
        {
            _featureManager = featureManager;
        }
    
        public async Task<IActionResult> Index()
        {
            if (await _featureManager.IsEnabledAsync("RedesignedUI"))
            {
                return View("Index_v2"); // 25% del tráfico
            }
    
            return View("Index_v1"); // 75% del tráfico
        }
    }

    Con este patrón podés hacer un lanzamiento progresivo real: empezás con el 5%, observás métricas, subís a 25%, luego a 50%, y cuando estás seguro activás al 100%. Todo sin un solo redeploy.

    ¿Cuándo usar Feature Flags vs. versionado de API?

    Es una confusión frecuente. La regla práctica que uso:

    • Feature Flags → cuando el cambio es interno o progresivo y querés controlar cuándo se activa en producción sin cambiar el contrato público de la API.
    • Versionado de API → cuando el cambio rompe el contrato y tenés consumidores externos que necesitan tiempo para adaptarse.

    No son excluyentes: podés usar ambos en el mismo proyecto para propósitos distintos.

    Próximos pasos

    Lo que vimos acá es la base. El ecosistema de Microsoft.FeatureManagement va más lejos:

    • Custom Feature Filters: habilitar features según el Tenant de la base de datos, el rol del usuario o cualquier lógica de negocio propia.
    • Azure App Configuration: centralizar todos los flags en un servicio externo, modificarlos desde un panel sin tocar archivos de configuración y con soporte para targeting (activar por usuario específico, grupo o región).

    ¿Querés que profundice en alguno de estos dos temas en un próximo artículo? Dejalo en los comentarios.

Tags

tsql (27)mssql (26)devops (21)sql (20)dotnet (18)docker (16)performance (14)contenedores (11)dotnet10 (10)linux (9)csharp (8)microservicios (8)angular (8)angular21 (7)sql server (6)issabel (6)kubernetes (6)docker-compose (6)typescript (6)aot (6)