Feature Toggle (Feature Switch, Feature Flag) to technika umożliwiająca modyfikowanie zachowania systemu w środowisku testowym lub produkcyjnym. W powyższym wpisie w ramach koncepcji feature toggles dzielę na dwie kategorie release toggles i bussines toggles. W sieci można znaleźć także inny bardziej szczegółowy podział na release toggles, experiment toggles, permission toggles i ops toggles.

Release Toggles

Release toggles jest alternatywą dla feature branch w systemach kontroli wersji. Rozpoczynając pracę w tradycyjnym podejściu dla funkcji tworzymy nową gałąź i implementujemy funkcje zgodnie z issue, a następnie scalamy kod. W przypadku stosowania przełączników, zespół pracuje na jednej gałęzi np. master i ukrywa aktualnie realizowane funkcje za pomocą przełącznika. W momencie zakończenia pracy nad funkcją i przekazaniem do testów, osoba z zespołu aktywuje funkcje. Czyli w praktyce stosujemy branch w kodzie zamiast fizycznego brancha w systemie kontroli wersji. Stosując to rozwiązanie odpada nam problem zarządzania, utrzymania, scalania wielu gałęzi kodu źródłowego. Wadą tego rozwiązania jest utrzymywanie nieaktywnego kodu na głównej gałęzi, aż korci by kod na szaro (martwy) usunąć. Na co dzień stosuję swoją piaskownice dla zadania, czyli tworzę w repo gałąź na funkcje, będąc świadom ewentualnych konfliktów z dużymi mergami. Chętnie w komentarzach poznam opinie osób, które na co dzień pracują z metodyką release toggle w systemie kontroli wersji. Tu nasuwa mi się pytanie, co z testami jednostkowymi dla nieaktywnego kodu? Jak u was wygląda konfiguracja feature toggle?

Business Toggles

W przypadku release toggles mówiliśmy o korzyściach dla zespołów projektowych, a business toggles dotyczą korzyści dla klienta (biznesu). Business toggles umożliwiają włączania/wyłączania funkcji w zależności od kryterium biznesowego. W praktyce możemy udostępnić funkcje w zależności od geolokalizacji, grupy użytkownika, aktywności, wieku użytkownika etc.. Na produkcji nie będziemy mieli „prostego” if-a, ale bardziej złożony warunek, który oprócz flagi określającej włączenie/wyłączenie funkcji, będzie także zawierać politykę udostępnienia funkcji.

Konfiguracja

Zarządzanie dostępnością funkcji w aplikacji może odbywać się na trzech poziomach konfiguracji:

  • Compiled configuration
  • Local configuration
  • Centralized configuration

Compiled configuration

W powyższej opcji konfiguracja jest zaszyta na sztywno w kodzie źródłowym.  W celu zmiany stanu funkcji za każdym razem należy zbudować nową aplikację. Jest to najgorsze rozwiązanie dla sterowania/synchronizacją aplikacji na produkcji. Zobaczmy jednak przykładowe implementacje dla „Compiled configuration”:

  • Flaga (true/false)
var isSuperFeatureEnabled =  true;
if (isSuperFeatureEnabled)
{
    // implementation
}

Dla czytelności można utworzyć klasę z polami typu const (flagi).

  • Dyrektywy preprocesora
#define SUPER_FEATURE
#if (SUPER_FEATURE)
//implementation
#endif

Definiujemy (#define) ciąg znaków np. SUPER_FEATURE i w dyrektywie warunkowej #if sprawdzamy zdefiniowany symbol. Jeśli wartość logiczna jest prawdziwa zostanie wykonany kod między dyrektywą #if a #endif.  Przykładowo dodając do definicji znak „_” np. _SUPER_FEATURE traktujemy, że funkcja ma być nieaktywna.

Local configuration

Wartości określające dostępność funkcji przechowywane są w plikach konfiguracyjnych aplikacji lub zmiennych środowiskowych. W porównaniu do opcji „Compiled configuration” nie trzeba w celu zmiany stanu funkcji budować nowej wersji aplikacji. Wystarczy zmienić wartość stanu (true/false) dla klucza w lokalnej konfiguracji. W przypadku, gdy aplikacja znajduje się na wielu maszynach, dochodzi problem synchronizacji konfiguracji.

  • Odczyt z pliku konfiguracyjnego np. web.config
var isSuperFeatureEnabled = bool.Parse(ConfigurationManager.AppSettings["SuperFeature"]);
  • Zmienne środowiskowe
var isSuperFeatureEnabled = Environment.GetEnvironmentVariable("SUPER_FEATURE");
if (isSuperFeatureEnabled)
{
    // implementation
}

Centralized configuration

Przy tej konfiguracji wartości odczytywane są z centralnego punktu z konfiguracją.  W tym podejściu rozwiązujemy problem synchronizacji aplikacji na wielu maszynach, oraz nie mamy potrzeby budowania nowej wersji aplikacji. Konfiguracja może być przechowywana w następujących lokalizacjach:

  • Tabela w bazie danych
  • Zewnętrzne narzędzie do przechowywania stanu aplikacji np. Consul, Feature Ops etc.

Dla wszystkich trzech konfiguracji mówiłem tylko o tym czy funkcja jest aktywna/nieaktywna. W przypadku uwzględnienia wybranego kryterium biznesowego, będziemy dodatkowo musieli w warunku sprawdzić wymaganie biznesowe np. czy funkcja dostępna tylko dla użytkowników z Wrocławia.

Do implementacji feature toggles w świecie .NET możemy skorzystać z zewnętrznych bibliotek np. FeatureSwitcher lub FeatureToggle. W przypadku własnej implementacji warto warunek wyekstraktować do zewnętrznej metody zdefiniowanej w interfejsie. W powyższej metodzie będzie sprawdzany aktualny stan funkcji na podstawie zdefiniowanej konfiguracji. Zdefiniowanie interfejsu zapewni nam możliwość zweryfikowania testami jednostkowymi kodu nowej funkcji.

Podsumowanie (Zalety vs Wady)

Feature toggle to dobra technika do rozwiązania problemu częstych, złożonych i problematycznych wdrożeń. Czy warto ją stosować w codziennej pracy? Przyjrzyjmy się na koniec zaletą i wadą feature toggle.

Zalety:

  • mamy możliwość sterowania funkcjami systemu na środowisku produkcyjnymi. W przypadku pożaru możemy szybko wyłączyć nową funkcje, lub powrócić do starej wersji funkcji.
  • minimalizacja ryzyka nowych funkcji
  • możliwość testowania pod względem biznesowym na produkcji np. testujemy na określonej liczbie użytkowników przed wdrożeniem dla wszystkich
  • wspomaga zwinne wdrażanie produktu (Continuous Deployment), który ma niedokończone funkcje (na danym moment nieaktywne)

Wady:

  • przechowujemy nieaktywny kod w repozytorium kodu
  • tworzymy dług technologiczny dla aplikacji, jeśli nie usuniemy feature toggle po zakończeniu procesu testowania/wdrożenia funkcji na produkcji
  • utrudnione testowanie (stosując feature toggles, musimy utrzymywać testy dla aktywnej i nieaktywnej ścieżki)
  • narzut na zarządzanie stanem/konfiguracją funkcji