Na statku kosmicznym, zanim aktywujesz nowy system nawigacyjny dla calej floty, testujesz go na jednym statku. Jesli dziala -- wdrazasz na reszcie. Jesli nie -- wyłączasz jednym przełącznikiem. To wlasnie Feature Flags -- mechanizm pozwalajacy wlaczac i wylaczac funkcjonalnosci bez deploymentu nowego kodu.
Feature Flag (flaga funkcjonalnosci) to wartosc boolean lub konfiguracja, ktora decyduje, czy dana funkcja jest dostepna:
1// Najprostsza forma -- obiekt z flagami
2const featureFlags = {
3 newDashboard: true,
4 darkMode: true,
5 experimentalNav: false,
6 betaSearch: false,
7};
8
9// Uzycie w komponencie
10function App() {
11 return (
12 <div>
13 {featureFlags.newDashboard
14 ? <NewDashboard />
15 : <OldDashboard />
16 }
17 {featureFlags.betaSearch && <BetaSearchBar />}
18 </div>
19 );
20}W prawdziwej aplikacji flagi przechowujemy w kontekscie, zeby byly dostepne wszedzie:
1const FeatureFlagContext = createContext({});
2
3function FeatureFlagProvider({ flags, children }) {
4 return (
5 <FeatureFlagContext.Provider value={flags}>
6 {children}
7 </FeatureFlagContext.Provider>
8 );
9}
10
11function useFeatureFlag(flagName) {
12 const flags = useContext(FeatureFlagContext);
13 return flags[flagName] ?? false;
14}
15
16// Uzycie
17function NavigationBar() {
18 const showNewNav = useFeatureFlag('newNavigation');
19
20 return showNewNav ? <NewNavBar /> : <ClassicNavBar />;
21}Zamiast pisac if/else w kazdym komponencie, mozemy stworzyc deklaratywny komponent:
1function Feature({ name, children, fallback = null }) {
2 const isEnabled = useFeatureFlag(name);
3 return isEnabled ? children : fallback;
4}
5
6// Uzycie -- czytelne i deklaratywne
7function MissionControl() {
8 return (
9 <div>
10 <h1>Mission Control</h1>
11
12 <Feature name="advancedRadar">
13 <AdvancedRadarPanel />
14 </Feature>
15
16 <Feature
17 name="newStatusBar"
18 fallback={<LegacyStatusBar />}
19 >
20 <ModernStatusBar />
21 </Feature>
22
23 <Feature name="experimentalWarpDrive">
24 <WarpDriveControls />
25 </Feature>
26 </div>
27 );
28}A/B Testing to rozszerzenie feature flags, gdzie rozni uzytkownicy widza rozne wersje interfejsu:
1function useABTest(testName, variants) {
2 const [variant, setVariant] = useState(null);
3
4 useEffect(() => {
5 // Sprawdz czy uzytkownik juz ma przypisany wariant
6 const stored = localStorage.getItem(`ab_${testName}`);
7 if (stored && variants.includes(stored)) {
8 setVariant(stored);
9 } else {
10 // Losowo przypisz wariant
11 const randomVariant = variants[
12 Math.floor(Math.random() * variants.length)
13 ];
14 localStorage.setItem(`ab_${testName}`, randomVariant);
15 setVariant(randomVariant);
16 }
17 }, [testName, variants]);
18
19 return variant;
20}
21
22// Uzycie
23function LandingPage() {
24 const variant = useABTest('landing_cta', ['control', 'variant_a', 'variant_b']);
25
26 if (variant === 'control') {
27 return <ClassicHero ctaText="Rozpocznij misje" />;
28 }
29 if (variant === 'variant_a') {
30 return <ClassicHero ctaText="Dolacz do floty!" />;
31 }
32 return <ModernHero ctaText="Startuj teraz" />;
33}Zamiast wlaczac flage dla wszystkich naraz, mozna wdrazac stopniowo -- np. 10% uzytkownikow, potem 50%, potem 100%:
1function usePercentageRollout(featureName, percentage) {
2 const [isEnabled, setIsEnabled] = useState(false);
3
4 useEffect(() => {
5 // Deterministyczny hash na podstawie userId + featureName
6 const stored = localStorage.getItem(`rollout_${featureName}`);
7 if (stored !== null) {
8 setIsEnabled(stored === 'true');
9 } else {
10 const random = Math.random() * 100;
11 const enabled = random < percentage;
12 localStorage.setItem(`rollout_${featureName}`, String(enabled));
13 setIsEnabled(enabled);
14 }
15 }, [featureName, percentage]);
16
17 return isEnabled;
18}
19
20// Uzycie
21function Dashboard() {
22 const showNewChart = usePercentageRollout('new_chart_v2', 30); // 30% uzytkownikow
23
24 return (
25 <div>
26 {showNewChart ? <NewInteractiveChart /> : <LegacyChart />}
27 </div>
28 );
29}W produkcji flagi czesto przychodzą z serwera (LaunchDarkly, Flagsmith, Unleash):
1function useRemoteFlags() {
2 const [flags, setFlags] = useState({});
3 const [loading, setLoading] = useState(true);
4
5 useEffect(() => {
6 fetch('/api/feature-flags')
7 .then(res => res.json())
8 .then(data => {
9 setFlags(data);
10 setLoading(false);
11 })
12 .catch(() => setLoading(false));
13 }, []);
14
15 return { flags, loading };
16}
17
18// Uzycie z FeatureFlagProvider
19function App() {
20 const { flags, loading } = useRemoteFlags();
21
22 if (loading) return <Spinner />;
23
24 return (
25 <FeatureFlagProvider flags={flags}>
26 <Dashboard />
27 </FeatureFlagProvider>
28 );
29}| Sytuacja | Typ flagi | |----------|-----------| | Nowa funkcja w trakcie developmentu | Boolean flag (
true/false) |
| Testowanie roznych wersji UI | A/B test (warianty) |
| Stopniowe wdrozenie dla uzytkownikow | Percentage rollout |
| Szybkie wylaczenie problematycznej funkcji | Kill switch |
| Rozne funkcje dla roznych planow | User-based flag |Feature Flags to jak centralny panel kontroli na statku kosmicznym. Mozesz wlaczyc lub wylaczyc dowolny system jednym przyciskiem, przetestowac nowy silnik na jednym statku zanim wdrozysz go na calej flocie, i natychmiast cofnac zmiany gdy cos poszlo nie tak -- bez koniecznosci ladowania na stacji i wymiany czesci!