Pierwszy raz kiedy spotkałem się z 'flagami funkcjonalności'... Powiem szczerze - wyglądało to jak czarna magia. Jednoczesna możliwość dynamicznego zmieniania zachowań systemu napawała mnie obawą o zawodność rozwiązania. Przecież nie wiadomo, co się wydarzy kiedy konfiguracja funkcjonalności nie zostanie zaciągnięta. Istnieje możliwość przypadkowej zmiany flagi, co wpłynie na użyteczność systemu. Jak widać wiele może zawieść. Warto jednak zaryzykować, ponieważ korzyści jakie dają Feature Toggles
są ogromne. Przede wszystkim możliwość rozdzielenia deploy-u od wdrożenia, testy A/B a nawet testy na produkcji dla ograniczonej ilości użytkowników.
Ucieszyłem się, kiedy zobaczyłem, że Gitlab udostępnił możliwość zarządzania flagami funkcjonalności dla wszystkich kont (konta free obecnie mają ograniczenia do 50 flag na projekt). Natchniony odkryciem postanowiłem w dzisiejszym artykule przybliżyć koncept Feature Toggle
, a także pokazać jak w kilku prosty krokach skonfigurować i zintegrować naszą aplikację z API wystawionym przez serwis Gitlab.
Feature Toggles (Feature Flag)
Feature Toggles
, jak pisze Pete Hodgson w swoim artykule na stronie Martin-a Fowler-a, to zestaw pattern-ów pomagających zespołom IT w dostarczaniu nowych funkcjonalności, w szybki i bezpieczny sposób. Nazewnictwo czasem się różni, napotkałem terminy jak: Feature Flags
, Feature Bits
, Feature Flipers
. W bardzo dużym uproszczeniu jest to po prostu if
w kodzie, który uruchamia funkcjonalność albo nie. Moglibyśmy to zachowanie zaprezentować w pseudokodzie w sposób następujący:
if (funcionality.IsOn())
serve.NewFuncionality();
else
serve.OldFuncionality();
Oczywiście to tylko najprostszy przypadek użycia. Feature Flags
możemy podzielić na 4 rodzaje w zależności od strategii użycia.
Release Toggles
Są to przełączniki pozwalające na dogrywanie jeszcze nie dokończonej implementacji do naszego głównego branch-a. Pozwala to nawet na wgranie nie ukończonej implementacji na produkcję. Dopóki ktoś w panelu administracyjnym nie zmieni stanu przełącznika, kod nigdy nie powinien się uruchomić. Taki rodzaj Toggles jest bardzo często spotykany przy tzw. Trunk-based development
w zespołach, które kładą bardzo duży nacisk na Continuous Delivery
.
Experiment Toggles
Ten rodzaj przełączników pozwala na eksperymenty na użytkownikach aplikacji. Jest często wykorzystywany przy okazji testów A/B na prawdziwym użytkownikach. W ten sposób możemy zebrać dane potrzebne do optymalizacji, czy to w procesach, czy też w warstwie UI.
Ops Toggles
Ten rodzaj przełączników jest używany do kontrolowania operacyjnych aspektów oprogramowania. Zazwyczaj jest wprowadzany, kiedy chcemy dodać, do naszego systemu, nową funkcjonalność, ale nie jesteśmy pewni czy ruch produkcyjny nie będzie dla niej za dużym opóźnieniem. Jeżeli zachowamy ten przełącznik, może on robić w naszym systemie jako "Kill Switch" aby w sytuacjach kryzysowych szybko wyłączyć pewne funkcjonalności. Dla przykładu załóżmy, że jesteśmy odpowiedzialni za duży sklep internetowy i nadchodzi nasze największe święto w roku - Black Friday
. Kiedy zauważymy, że ilość użytkowników jest zbyt duża dla naszych serwerów, możemy wyłączyć pewne moduły jak np. komentarze na stronie produktu czy też lista rekomendacji, aby przekierować wolne zasoby na procesowanie ważniejszych request-ów.
Permissioning Toggles
Jest to chyba najczęściej używany rodzaj przełączników stosowanych przeze mnie. Pozwala on zdefiniować grono użytkowników, dla których funkcjonalność ma być włączona. W przeciwieństwie do Experiment Toggles
możemy tutaj dosłownie zdefiniować, że użytkownik o ID 123 będzie miał aktywną funkcjonalność. Pozwala nam to na testowanie nowych rzeczy na produkcji bez wiedzy faktycznych użytkowników. To po prostu mega sposób aby być pewnym, że kiedy będziemy gotowi aby wypuścić funkcjonalność, będzie ona poprawnie przetestowana.
Gitlab Feature Flags
To by było na tyle jeżeli chodzi o teorię, przejdźmy do tego co lubimy najbardziej - dużych frytek z polewą serową... znaczy praktyki. Zacznijmy od zdefiniowania przykładowej flagi funkcjonalności w panelu Gitlab-a. Aby to zrobić musimy posiadać swój projekt, przejść do sekcji Operations
, a tam do pod sekcji Feature Flags
. Zostaniemy przeniesieni na listę zdefiniowanych flag. Nad listą będzie przycisk New feature flag
. Po kliknięciu w niego zostaniemy przekierowani na poniższy formularz.
Na start polecam uzupełnienie tylko pola Name
, ewentualnie Description
. Jako, że ten artykuł i tak już jest długi, lepiej nie zaczynajmy zabawy strategiami. Po kliknięciu przycisku Create feature flag
powinniśmy zostać przekierowaniu na listę zdefiniowanych flag.
Z automatu utworzona flaga będzie aktywna (osobiście uważam, że nie powinna, pamiętajcie o przestawieniu jej na początku). Najważniejsze, by na tym ekranie zwrócić uwagę na przycisk Configure
, pod którym kryje się konfiguracja, którą będziemy musieli wykorzystać później w projekcie.
Obsługa w aplikacji .NET
Gitlab poprawnie zaimplementował wsparcie Feature Flag
, ponieważ jest to całkowicie open source-wa implementacja. Serwer, który mamy dostępny do naszej dyspozycji, nazywa się Unleash. Cieszy mnie, że powstał oficjalny klient dostępny jak paczka Nuget - Unleash.Client
. Ma to swoje dodatkowe korzyści. Gdybyśmy chcieli odejść od gitlaba mamy możliwość samemu postawić sobie serwer Unleash. Wystarczy tylko node.js i baza danych PostgreSql (niestety na chwile obecną to jedyny wspierany typ bazy danych).
Sama podstawowa konfiguracja klienta jest bardzo prosta, wystarczy że do pliku Startup.cs
w sekcji ConfigureServices
dodamy poniższy kod:
public void ConfigureServices(IServiceCollection services)
{
var settings = new UnleashSettings
{
AppName = "dotnet-test",
InstanceTag = "kqNfCeMpAZyq5xE8Ykxz",
UnleashApi = new Uri("https://gitlab.com/api/v4/feature_flags/unleash/22769163"),
};
var unleash = new DefaultUnleash(settings);
services.AddSingleton<IUnlesh>(unleash);
}
Specjalnie zostawiłem wartości z sekcji Configure abyście wiedzieli dokładnie w które miejsca je wstawić.
Taka mała uwaga z mojej strony - powyższy kod działa według mechanizmu cold start
. Oznacza to, że na starcie aplikacji wszystkie flagi będą ustawione na wartości domyślne. Dopiero po 30 sekundach klient pobierze konfigurację z serwera. Oczywiście istnieje też konfiguracja tzw. hot start
, gdzie unleash będzie próbowało pobrać dane w momencie uruchamiania się aplikacji, ale to już zapewne opiszę w innym artykule.
Przejdźmy do wykorzystania flagi. Dla przykładu pobierzmy, w naszym kontrolerze, instancję obiektu IUnleash
z naszego kontenera dependency injection. Następnie, za pomocą api tego obiektu, sprawdźmy, czy funkcjonalność o wcześniej zdefiniowanej nazwie my_feature
jest uruchomiona.
public class MyController : ControllerBase {
private readonly IUnleash _unleash;
public MyController(IUnleash unleash) {
_unleash = unleash;
}
public IActionResult Method(){
if (_unleash.IsEnabled("my_feature")) return Ok("feature on!");
return Ok("feature off!");
}
}
Na koniec ostatnia uwaga: w standardowej konfiguracji klient Unleash odpytuje serwer co 30 sekund, więc na zmiany wprowadzone w panelu Gitlab-a będzięmy czekać maksymalnie pół minuty.
Podsumowanie
Tak oto mamy działające Feature Flags
z zewnętrznym miejscem do ich konfiguracji. To naprawdę potężne narzędzie, które warto jest mieć w swoim arsenale. Jak zwykle mam nadzieje, że artykuł się podobał, jeżeli chcecie być na bieżąco zapraszam do zapisania się na newsletter lub do obserwowania mnie w social mediach. Do Następnego!