En procesos modernos como en desarrollo ágil es común que se hagan iteraciones para entregar nuevas funcionalidades a la aplicación, muchas veces es posible entregar un sección completa pero otras veces solo se realiza una parte, quedando incompleto, por ejemplo:
Supongamos que tenemos una tarea dedicada a construir la base de una página pero el resto de elementos por su complejidad serán implementadas en otra tarea, dejándola así incompleta para el usuario final. Continuando con las buenas prácticas, nos gustaría agregar el código al branch principal ("continuous integration" y "continuos deployment") ¿pero que vamos hacer con esta página que está incompleta? Aquí es donde entra en juego los feature flags.
🤔 ¿Qué son?
Son simples valores booleanos que nos permite utilizar con algún condicional para mostrar o esconder una sección, así de simple 😁.
✏️ ¿Cómo los puedo definir?
Algunas de las opciones que me vienen a la mente son:
- Utilizar un servicio de features flags, ejemplo Bullet Train o Launch Darkly.
- Utilizar un lenguaje de back-end y crear un servicio, en el que el app pueda consumir y obtener los valores de los flags. (En caso que esté pensando en crear un servicio, existen proyectos open-source que permiten hacer la administración de flags, solo debemos encargarnos de configurarlo y hostearlo, ejemplo bullet train es open source así que podemos montar nuestro propio servicio).
- Utilizar archivos locales dentro el app. Está opción no es recomendable porque cada vez que modifiques el archivo donde definas los flags, tendrás que correr un nuevo build. Así que pierde dinamismo.
🔨 ¿Para qué más lo puedo utilizar?
Los feature flags también son utilizados en estrategias de A/B testing en el que puedes mostrar cierta funcionalidad(feature/característica) a una parte de la población de usuarios y a otra no. Esto es una estrategia de marketing que permite descubrir que es más atractivo/visitado/utilizado por el usuario.
🤨 Recomendaciones
- Intenta solo declarar en el proveedor del servicio los flags que estés utilizando. Esto te ayudará a ser más ordenado y a ser más escalable.
- Toma el tiempo de analizar si debes definir un valor inicial a la variable que va almacenar el flag. Generalmente no va ser un problema que el flag pase de
false
(valor inicial) atrue
(respuesta del servicio) porque independientemente del tiempo que tome en retornar el servicio, solo se muestra cuando seatrue
pero no va a pasar lo mismo si definimos comotrue
el valor inicial y pasa afalse
, en esta situación puede llegar a pasar un “flash” en el que se despliega una sección y luego se esconde por el tiempo que toma la respuesta del servicio en volver, introduciendo un comportamiento inesperado en el app. - Intenta utilizar pocos flags por página, ente más granular seas, más dependes del servicio.
💡Ejemplo
Construiremos una aplicación de Angular con un componente toggle, que nos permitirá representar el valor definido en Bullet Train(servicio que utilizaremos para definir los flags).
- Debemos crearnos una cuenta en Bullet Train, una vez que creamos un
environment
copiamos él ID (lo necesitaremos luego), y creamos un flag que se llametoggle_status
.
- Agregamos la librería de Bullet Train que nos facilitará el trabajo:
npm i bullet-train-client --save
- Definimos un servicio que nos permitirá inicializar Bullet Train y obtener los flags. Reemplaza la constante
environmentID
con el ID que copiaste en el paso 1.
import { Injectable } from '@angular/core';
import bulletTrain from 'bullet-train-client';
const environmentID = 'XXXXXXXX';
@Injectable({ providedIn: 'root' })
export class FeatureFlagService {
public getFlags() {
return bulletTrain.getAllFlags();
}
}
export function preloadFlags() {
return async function() {
return bulletTrain.init({
environmentID
});
};
}
- En este caso, quiero que los valores de los flags se carguen antes de que la aplicación inicie, así que usaremos el token
APP_INITIALIZER
para inyectar los valores en el app,
@NgModule({
imports: [ BrowserModule, FormsModule ],
declarations: [ AppComponent, ToggleComponent ],
bootstrap: [ AppComponent ],
providers: [
{
provide: APP_INITIALIZER,
multi: true,
useFactory: preloadFlags
}
]
})
export class AppModule { }
¿Qué es lo que esta pasando acá? 🔝
- Estamos utilizando el token
APP_INITIALIZER
que permite decirle a Angular que ejecute la función factory que definíamos (la cual debe ser una promesa) antes de iniciar. -
multi
le indica que se debe agregar este evento junto a otros que hayan sido definidos en alguna otra parte del app.
Por último, debemos consumir los valores del servicio y pasárselos al componente toggle
import { Component } from '@angular/core';
import { FeatureFlagService } from './feature-flag.service';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
public isActive: boolean;
constructor(private featureFlags: FeatureFlagService ) {
const { toggle_status } = this.featureFlags.getFlags()
this.isActive = !!toggle_status.enabled;
}
}
<h2>Ejemplo</h2>
<app-toggle [isActive]="isActive"></app-toggle>
Si marcamos como enabled
el flag en Bullet Train, el componente se mostrará como activo desde el inicio del app.
Puedes encontrar el ejemplo completo acá
https://stackblitz.com/edit/feature-flags-angular-bullet-train
👀Conclusión
Los feature flags es una herramienta poderosa que nos permite poder seguir integrando nuestro código con el de otros y a su vez esconder o mostrar funcionalidades al usuario según la condición que definamos.
—
¿Quieres invitarme a un cafecito?
Top comments (2)
¡Excelente ejemplo!
Creo que yo lo aplicaría con algo como Google Tag Manager o directamente Firebase Remote Config para poder usar A/B Testing y otras cositas.
¡Es una exc idea! Son buenas opciones a considerar!