Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Feature Flags i A/B Testing w React -- Kontrolowane wdrozenia

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.

Czym sa Feature Flags?

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}

Feature Flag Provider

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}

Komponent Feature -- deklaratywne flagi

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 -- warianty dla roznych uzytkownikow

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}

Percentage Rollout -- stopniowe wdrozenia

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}

Dynamiczne flagi z serwera

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}

Kiedy uzywac Feature Flags?

| 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!

Vai a CodeWorlds