Hola a todos! Voy a comenzar el post con una pregunta: ¿Has tenido que revertir uno o más commits en producción por una nueva funcionalidad que trajo problemas?

Si la respuesta es No, siento un poco de envidia! Pero si tu respuesta es Si (como es mi caso), este post es para vos. Vamos a ver cómo podemos resolver ese problema utilizando Feature Flags.

Contexto

Seguramente has escuchado hablar alguna vez de la idea de desplegar código frecuentemente y en forma incremental, iteraciones cortas con retroalimentación (feedback) inmediato, fallar rápido (fail fast), etc.

Seguramente cuando tuviste que revertir ese commit conflictivo te hubiera gustado poder “apagar” esa funcionalidad sin revertir commits.

Quizás te gustaría desplegar una nueva funcionalidad pero no activarla, o activarla para un grupo reducido de usuarios.

Todas las respuestas conducen a Feature Flags!

Feature Flags

Feature Flags es una práctica que nos permite controlar total o parcialmente como se comporta una funcionalidad o grupo de funcionalidades mediante configuración de una forma estándar y simple.

Dicho de otro modo, los Features Flags nos van a permitir “encender” o “apagar” funcionalidades mediante configuración.

Antes de comenzar

Antes de comenzar a escribir código, veamos un poco como luce la solución actual., donde tenemos una aplicación Web MVC muy simple con la siguientes 3 características:

  1. Una entrada en el menú principal llamada "Nueva Funcionalidad".
Home
Home
<header>
    <nav class="...">
        <ul class="...">
            <li class="nav-item">
                <a asp-controller="Home" asp-action="Index">Home</a>
            </li>
            <li class="nav-item">
                <a asp-controller="Home" asp-action="Privacy">Privacy</a>
            </li>
            <li class="nav-item">
                <a asp-controller="Home" asp-action="NuevaFuncionalidad">Nueva Funcionalidad</a>
            </li>
        </ul>
    </nav>
</header>
  1. Una acción llamada NuevaFuncionalidad en el controlador Home que nos dirige una vista.
public class HomeController : Controller
{
    public IActionResult Index() => View();

    public IActionResult Privacy() => View();

    public IActionResult NuevaFuncionalidad() =>
        View();
}
  1. Una vista llamada NuevaFuncionalidad.
@{
    ViewData["Title"] = "Nueva Funcionalidad";
}

<div class="text-center">
    <h1 class="display-4">Nueva Funcionalidad</h1>
</div>

Requerimientos

¿Qué pasaría si esta nueva funcionalidad tiene una falla y necesitamos desactivarla? ¿Cómo podemos incorporar esta nueva funcionalidad si todavía esta incompleta?

El requerimiento que vamos a atacar es simple: necesitamos poder desactivar o activar esta nueva funcionalidad en forma simple y rápida, sin necesidad de crear nuevos commits y de una forma estandarizada.

Manos a la obra

Para poder comenzar a utilizar Features Flags vamos a necesitar instalar una librería llamada Microsoft.FeatureManagement.AspNetCore.

dotnet add package Microsoft.FeatureManagement.AspNetCore

Luego tendremos que implementar 3 aspectos:

  1. Configuración e inyección de servicios
  2. Vistas
  3. Controladores

Configuración e inyección de servicios

Comenzaremos por configurar los servicios necesarios mediante Inyección de Dependencias.

// ...
using Microsoft.FeatureManagement;

namespace FacuTheRock.FeaturesFlags.Web
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            // ...
            services.AddFeatureManagement();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // ...
        }
    }
}

Una vez configurados los servicios, debemos proceder con las configuraciones necesarias. Por suerte para nosotros, solo necesitamos agregar una sección llamada FeatureManagement dentro del archivo appsettings.json.

En esta sección listaremos todas las funcionalidades e indicaremos si esta activa o inactiva. Agregaremos una nueva funcionalidad a la lista a la cual llamaremos NuevaFuncionalidad cuyo valor dejaremos en false o inactivo.

{
  // ...
  "FeatureManagement": {
    "NuevaFuncionalidad": false,
    "Funcionalidad1": true,
    // ...
  }
}

Vistas

Ahora necesitamos mostrar y ocultar la opción del menú Nueva Funcionalidad en función del Feature Flag que configuramos anteriormente. Para eso tenemos disponible un tag helper llamado feature al cual le indicaremos el nombre del Feature Flag que queremos observar.

Es importante incluir en el código de la vista la referencia a Microsoft.FeatureManagement.AspNetCore:

@*_Layout.cshtml*@

@addTagHelper *, Microsoft.FeatureManagement.AspNetCore

<header>
    <nav class="...">
        <ul class="...">
            <li class="nav-item">
                <a asp-controller="Home" asp-action="Index">Home</a>
            </li>
            <li class="nav-item">
                <a asp-controller="Home" asp-action="Privacy">Privacy</a>
            </li>
            <feature name="NuevaFuncionalidad">
                <li class="nav-item">
                    <a asp-controller="Home" asp-action="NuevaFuncionalidad">Nueva Funcionalidad</a>
                </li>
            </feature>
        </ul>
    </nav>
</header>

El tag helper se ocupa de verificar si la funcionalidad se encuentra activa, y de ser así renderiza los elementos contenidos dentro el.

En este caso y debido a la mediante configuramos indicamos que la funcionalidad se encuentra inactiva, la opción de menú correspondiente no estará disponible.

Controlador

Si bien ya no vemos la opción de menú disponible, todavía es posible acceder al controlador si utilizamos la URL directamente, por ejemplo http://localhost:5000/Home/NuevaFuncionalidad. En el caso de que el Feature Flag este desactivado, el controlador no debería ser accesible y debería retornar respuesta un error 404 - NotFound.

Por suerte para nosotros, solo basta con agregar el atributo FeatureGate con el nombre de la funcionalidad a verificar:

public class HomeController : Controller
{
    public IActionResult Index() => View();

    public IActionResult Privacy() => View();

    [FeatureGate("NuevaFuncionalidad")]
    public IActionResult NuevaFuncionalidad() =>
        View();
}

Testing

Lo único que nos queda por hacer es jugar con la configuración de nuestro Feature Flag y ver como impacta tanto a nivel de Vista como a nivel de controlador.

Menu - Feature Flag true
Menu – Feature Flag true
Menu - Feature Flag false
Menu – Feature Flag false
Controlador - Feature Flag true
Controlador – Feature Flag true
Controlador - Feature Flag false
Controlador – Feature Flag false

Conclusión

Hoy vimos como gracias a la implementación de Features Flags podemos no solo activar o desactivar funcionalidades sin escribir código adicional y solo mediante configuración, sino que además nos permite desplegar funcionalidades parciales y activarlas en el momento que consideremos conveniente.

Pero esto es solo el comienzo, en los próximos artículos veremos como podemos verificar sistemas externos o nuestra propia base de datos para validar los flags, como activar filtros MVC, middlewares y mucho mas.