Co to jest feature flag: Przewodnik dla Engineering Teamów

Summary

Feature flag to warunki w kodzie decydujące w runtime czy funkcjonalność jest włączona. Zespoły używają ich do merging niedokończonej pracy, stopniowych rolloutów i szybkich kill switchy. Jednak 75% flag żyje zbyt długo - przewodnik wyjaśnia typ, właściciela i cleanup proces.

Biurko inżyniera z rzędem fizycznych przełączników obok laptopa

Feature flag to warunki w kodzie, które decydują w runtime, czy dana funkcjonalność jest włączona lub wyłączona bez nowego deployu. To cała idea.

Co to jest feature flag w praktyce? Instrukcja if, która bierze odpowiedź z konfiguracji, wiersza w bazie danych lub serwisu flag zamiast być na stałe wkodowana.

Koncepcja jest prosta. Żyć ze 200 takimi flagami w repozytorium to nie jest, a właśnie ta druga część to jest temat tego przewodnika.

Jak wygląda feature flag w kodzie?

Oto najmniejsza użyteczna wersja:

if (flags.isEnabled("new-checkout", { userId })) {
  return renderNewCheckout();
}
return renderOldCheckout();

Oba warianty kodu wysyłacie razem w tym samym buildie. Wartość flaga żyje gdzieś, gdzie możecie ją zmienić bez redeploy: w zmiennej środowiskowej, pliku JSON, tabeli lub hostowanym serwisie. Zmieńcie wartość, a zachowanie zmienia się przy następnej ewaluacji.

To rozdzielenie to właśnie punkt. Deployment to umieszczanie kodu na serwerach. Release to pozwolenie użytkownikom go widzieć. Flagi dzielą te dwa momenty, więc merge do main już nie oznacza "wszyscy to dostaną teraz".

Ręka przerzucająca przełącznik z zielonym wskaźnikiem

Dlaczego zespoły w ogóle używają feature flagów?

Trzy powody pojawiają się w kółko w zespołach 5-50 inżynierów.

Żaden z tych przypadków nie wymaga vendora. Flaga może być booleanem w tabeli konfiguracyjnej. Pomijajcie platformę, dopóki nie będziecie potrzebować rolloutów procentowych, audit trail lub nieinżynierów przełączających rzeczy.

Jakie są cztery typy feature flagów?

Szeroko cytowany artykuł Pete'a Hodgsona Feature Toggles na stronie Martina Fowlera soruje flagi według tego, jak długo żyją i jak często decyzja się zmienia. Kategorie są wciąż najbardziej przejrzystym modelem mentalnym.

Kolumna lifespan jest najważniejsza. Flaga release, która przetrwa swoją release to bug, który nie znaleźliście. Flaga permission, która zostanie usunięta w sprint czyszczenia to outage, która nie miała jeszcze miejsca.

Nazwijcie i zaetykietujcie typ kiedy tworzysz flagę. Sześć miesięcy później, nikt nie pamięta która kategoria to była "new-nav-v2".

Zespół planuje siatkę lepkich notatek pogrupowanych po kategorii

Jak puszczacie flagę bez skrzywdzenia użytkowników?

Nudna sekwencja działa najlepiej.

  1. Puszczcie kod z flagą wyłączoną. Zweryfikujcie, że nic się nie zmieniło.

  2. Włączcie dla własnego zespołu w produkcji. Używajcie przez jeden dzień.

  3. Włączcie dla 1-5% użytkowników, sticky po user ID żeby nikt nie przerzucał między wariantami mid-session.

  4. Obserwujcie wskaźnik błędów, latency i jedną metrykę biznesu. Zdecydujcie próg zanim zaczynacie, nie patrząc na dashboarda.

  5. Podnieście do 100%, czekajcie określony czas, potem usuńcie flagę.

Krok piąty to ten który zespoły pomijają. Dobrze, że dojdziemy do tego dlaczego to kosztuje więcej niż myślicie.

Praktyczna uwaga na temat ewaluacji: ceńcie odczyt flagów tanio i fail-safe. Jeśli serwis flagów nie działa, kod potrzebuje wartości domyślnej. Wybierzcie domyślną na flagi, celowo. Kill switch powinien failować na "bezpieczne", podczas gdy nowa funkcjonalność powinna failować na "off".

Dlaczego feature flagi stają się technical debt?

Każda flaga to fork w kodzie. Dwie flagi robią cztery możliwe ścieżki, dziesięć flag robi 1024, i prawie na pewno przetestowaliście kilka z nich. Engineering guide to flag debt od GrowthBook powołuje się na badania pokazujące że około 75% komponentów toggle były wciąż w codebase do 49 tygodni po wprowadzeniu, choć większość developerów powiedziała że planowała je usunąć.

Hodgson mówi to dobrze w tym samym artykule Fowlera: rozsądne zespoły traktują toggley jako inventory z carrying cost, i pracują żeby to inventory trzymać nisko.

Klasyczna horror story to Knight Capital z 2012. Flaga przemarłej funkcjonalności została ponownie użyta dla nowego zachowania podczas gdy stary kod wciąż siedział na jednym serwerze. Ta niezgodność przyczyniła się do około 460 milionów dolarów straty w poniżej godziny. Wasza stała flaga prawdopodobnie tego nie zrobi. Zrobi coś cichszego: refactor, który łamie gałąź którą nikt nie wiedział że istnieje, lub nowy hire spędzający popołudnie figuring out który z dwóch checkout paths jest rzeczywisty.

Zapylona półka zapomnianych pudełek i starych przełączników

Jak znajdujecie każdą flagę w istniejącym repozytorium?

To jest pytanie które docs vendora pomijają, i to jest gdzie większość zespołów się zacinają. Dashboard flag mówi co jest skonfigurowane. Nie mówi gdzie każda flaga jest czytana w kodzie, lub czy ścieżka kodu za flagą "off" jest wciąż osiągalna.

Zacznijcie z tanią metodą:

# każdy literal flag key czytany przez wrapper
rg -n 'isEnabled\("' src/ | sort

# flagi zdefiniowane ale nigdy nie referencjonowane
comm -23 <(jq -r 'keys[]' flags.json | sort) \
         <(rg -o 'isEnabled\("([^"]+)"' -r '$1' src/ | sort -u)

To rozpada się szybko. Flag keys zbudowane z string concatenation nie pokazują się w grep. Flagi przekazane przez helper functions ukrywają call sites. Monorepo i multi-repo setup mnożą problem, bo ten sam key może być czytany przez trzy serwisy.

To jest gdzie czytanie kodu z toolingiem pomaga. Code search tools, które rozumieją repo mogą odpowiedzieć "gdzie new-checkout jest ewaluowany, i co zależy od wyniku?" w jednym query zamiast pół dnia grep. Cursor i GitHub Copilot radzą to rozumnie w jednym repo. Przez kilka repo potrzebujecie index, który ich obejmuje, który jest case że budujemy codebasechat.

Jak wygląda rozsądny proces czyszczenia flagów?

Traktujcie usunięcie jako część pracy, nie jako zadanie na później.

Static analysis pomaga tutaj też. Tool jak CodeScene może pokazać które pliki przenoszą najbardziej zagmatwaną conditional logikę, która zwykle jest gdzie stare flagi się gromadzą. SonarQube flaguje unreachable i dead code po usunięciu check.

Jak testujecie kod, który siedzi za flagą?

Testing to jest część którą nikt nie budżetuje. Z dwoma ścieżkami na flagi, suite testów potrzebuje pokrycia obu, co najmniej dla flag, które guardy risky behavior.

Trzymajcie to praktyczne. Testujcie on i off states każdej release flagi w testach unit, przez injecting flaga value zamiast czytania live service. Uruchamiajcie jeden end-to-end suite przeciwko domyślnej konfiguracji produkcji, bo to jest co użytkownicy dostają dzisiaj. Potem uruchamiajcie drugi pass z flagą włączoną dla funkcjonalności którą собираетесь release.

Nie próbujcie testować każdej kombinacji. Z dziesięcioma flagami nie możecie. Zamiast tego, trzymajcie flagi niezależne: flaga która zmienia zachowanie tylko kiedy inna flaga jest też on to design smell, i to jest pierwsza rzecz do rozpletania.

Co nowi inżynierowie źle rozumieją na temat flag?

Juniory robią ten sam trzy błędy, i każdy jest tani żeby zapobiec w review.

Pierwsze dwa tygodnie nowego hire to dokładnie kiedy biegną w stare flagi bez właściciela. Krótki registry flag, z typem, właścicielem i datą usunięcia dla każdego wpisu, oszczędza kilka godzin pytania wokół.

Kiedy powinniście pokonać feature flagi?

Flagi nie są darmowe, więc pominąć je kiedy:

I używajcie je bez wahania kiedy zmiana jest risky, user-facing i hard to reverse przez redeploy. Payment flows, auth changes i nic co ma data migration behind to all qualify.

Co naprawdę byśmy robili w zespole dziesięciu osób

Zacznijcie z booleanem w config i jedną wrapper function, więc każdy read flag przechodzi przez jedno miejsce. Ten pojedynczy choke point sprawia flag list greppable, auditable i łatwy do migracji do hosted service później.

Zaetykietujcie każdą flagę po typie, dajcie jej właściciela i archiwujcie ticket usunięcia w dzień jeden. Przejrzyjcie listę miesięcznie przez dziesięć minut. Usuńcie flagi które były na 100% przez dwa tygodnie.

Feature flag to pożyczka. Weźcie ją kiedy ratuje riskyą release, i zwróćcie ją zanim interest pojawi się w następnym refactor.

Jak wdrażacie nowe inżynierów w temacie flag?

Kiedy nowy junior dołącza do zespołu, nie powinien spędzić pół dnia szukając dokumentacji flag. Utrzymujcie prosty registry - spreadsheet lub dokument - z trzema polami: nazwa flagi, typ, właściciel i data ważności.

To zajmuje 30 minut do ustawienia i oszczędza godziny pytań. Registry w Git gałęzi nie dyskontuje się - flagi żyją w serwisach i konfiguracjach runtime, więc bardziej stabilne miejsce to config management system.

Szczególnie praktyczne: dokumentujcie flag behavior razem z kodem. Komentarz nad instrukcją if (flags.isEnabled()) zmienia znaczenie: przyszli maintainers widzą dlaczego flag istnieje, nie tylko że istnieje.

Skalowanie praktyk flag w rosnącym zespole

W zespole do 5 osób, flagi są proste - wszyscy znają je. W 20 osobach, chaos. W 50, katastrofa jeśli nie macie systemu.

Rozsądny flow: ktoś tworzy flagę w serwisie, automatycznie wysyła notif do Slacka. Co 90 dni, job runuje - lista stałych flag. Zespół przegląda - których nie potrzebujemy. Usunięcie trafia do backlog.

Audyt co miesiąc to nie złoto, ale trzyma to pod kontrolą. Monitorowanie też pomaga - jeśli flaga nie zmienia się przez X dni, alert jej właścicielowi.

Frequently asked questions

Czy mogę bezpiecznie użyć feature flagi zamiast branchy?
Tak - flaga pozwala mergować kod do main bez natychmiastowego release. To rdzeń trunk-based development. Gałąź krótkotrwała i bezpieczna do mergowania, bo kod za flagą jest wyłączony.
Co się stanie jeśli moja flaga nie failuje gracefully?
Kod musi mieć default. Jeśli lookup serwisu flag zawiedzie, wybierzcie domyślne zachowanie per flagę: fail-safe dla kill switchy (zachowaj stare zachowanie), fail-closed dla nowych funkcji (trzymaj je wyłączone).
Ile flag to za dużo?
Nie ma magicznej liczby, ale 75% teamów ma za dużo. Sygnal alarmowy: jeśli nowy engineer pytają co robi flaga, a ty nie pamiętasz - to flaga jest za stara. Cap na 40-50 aktywnych jeśli macie 20+ developerów.
Czy flagi wpłają na performance?
Minimalnie jeśli zrobione dobrze. Lookup flagi to czytanie z cache lub local config - pojedyncze millisekundy. Problem to nie lookup - to kod path complexity gdy macie 200 flag.
Które tools pomagają w flag debt management?
GrowthBook, LaunchDarkly i CloudBees - ale nie są wymagane. Code search tools (Cursor, GitHub Copilot) pomagają znaleźć gdzie flagi żyją. SonarQube i CodeScene identyfikują tangled conditional logic gdzie flagi się gromadzą.
Czy testing flag jest obowiązkowy?
Tak dla release flag. Unit tests dla on/off state, end-to-end test dla production default config. Nie trzeba testować wszystkie kombinacje - 10 flag = 1024 paths, niemożliwe. Zamiast tego: flagi niezależne, każda testowana pojedynczo.