kontrollierte Rollouts ohne Redeploy
Feature Flags in React Apps entkoppeln die Frage, wann Code deployt wird, von der Frage, wann eine Funktion tatsächlich sichtbar ist. Statt riskanter Big-Bang-Releases lassen sich neue Funktionen schrittweise, für einzelne Nutzergruppen oder sofort per Kill-Switch wieder abschaltbar ausrollen, ganz ohne neuen Build.
Inhaltsverzeichnis
- 1. Warum Feature Flags Deployment und Release entkoppeln
- 2. Einen minimalen Feature-Flag-Context selbst bauen
- 3. Feature Flags mit useContext sauber konsumieren
- 4. Managed Services: LaunchDarkly, Unleash und Flagsmith
- 5. Progressive Rollouts: prozentuale Aktivierung und Zielgruppen
- 6. Feature Flags in Tests: deterministisches Verhalten erzwingen
- 7. Feature-Flag-Debt vermeiden: Lifecycle und Aufräumen
- 8. Server-seitige Flags in React Server Components
- 9. Eigenbau gegen Managed Service im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Feature Flags Deployment und Release entkoppeln
Feature Flags in React Apps lösen ein Problem, das in klassischen Deployment-Workflows regelmäßig zu Stress führt: Code wird deployt, sobald er fertig ist, aber eine Funktion soll erst später, für bestimmte Nutzergruppen oder überhaupt nur testweise sichtbar werden. Ohne Feature Flags bleibt oft nur die Wahl zwischen langlebigen Feature-Branches mit schmerzhaften Merge-Konflikten oder riskanten Big-Bang-Releases, bei denen alles gleichzeitig live geht.
Mit Feature Flags in React Apps wird neuer Code kontinuierlich in den Hauptbranch integriert und regelmäßig deployt, bleibt aber hinter einem Schalter versteckt, bis das Produktteam die Funktion tatsächlich für Nutzer freigeben will. Diese Trennung von Deployment und Release ist der eigentliche Kern jeder Feature-Flag-Strategie und reduziert das Risiko jedes einzelnen Deployments erheblich, weil ein Rollback lediglich das Umlegen eines Schalters bedeutet, nicht das Zurückrollen eines ganzen Releases.
Für React-Anwendungen kommen zwei Wege infrage: ein selbst gebauter, leichtgewichtiger Context für einfache Anwendungsfälle, oder ein Managed Service wie LaunchDarkly, Unleash oder Flagsmith für komplexere Regeln mit Zielgruppen-Segmentierung. Die folgenden Abschnitte zeigen beide Wege mit konkretem Code.
2. Einen minimalen Feature-Flag-Context selbst bauen
Für viele Projekte reicht ein selbst gebauter Feature-Flag-Mechanismus vollkommen aus, insbesondere wenn nur wenige Flags gleichzeitig aktiv sind und keine komplexe Zielgruppen-Logik nötig ist. Die Grundidee: ein React Context hält ein Objekt mit Flag-Namen und booleschen Werten, das beim App-Start aus einer API oder statischen Konfiguration geladen wird.
// src/feature-flags/FeatureFlagProvider.tsx
import { createContext, useEffect, useState, type ReactNode } from "react";
type FlagSet = Record<string, boolean>;
interface FeatureFlagContextValue {
flags: FlagSet;
isLoading: boolean;
}
export const FeatureFlagContext = createContext<FeatureFlagContextValue>({
flags: {},
isLoading: true,
});
export function FeatureFlagProvider({ children }: { children: ReactNode }) {
const [flags, setFlags] = useState<FlagSet>({});
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
let cancelled = false;
fetch("/api/feature-flags")
.then((res) => res.json())
.then((data: FlagSet) => {
if (!cancelled) {
setFlags(data);
setIsLoading(false);
}
})
.catch(() => {
// Fail closed: on error, treat all flags as disabled
if (!cancelled) setIsLoading(false);
});
return () => {
cancelled = true;
};
}, []);
return (
<FeatureFlagContext.Provider value={{ flags, isLoading }}>
{children}
</FeatureFlagContext.Provider>
);
}
Diese Basisversion von Feature Flags in React lädt einmalig beim Start der Anwendung alle relevanten Flags von einer eigenen API-Route, die serverseitig Regeln wie Nutzerrolle oder Umgebung auswerten kann. Der Fail-Closed-Ansatz im catch-Block ist bewusst gewählt: Wenn das Laden der Flags fehlschlägt, sollen neue, potenziell unfertige Funktionen deaktiviert bleiben, nicht versehentlich für alle sichtbar werden.
3. Feature Flags mit useContext sauber konsumieren
Der Konsum von Feature Flags in React-Komponenten sollte über einen eigenen Hook laufen, statt den Context direkt zu importieren. Das kapselt die Logik, erlaubt später einen Wechsel des zugrunde liegenden Mechanismus, ohne jede Komponente anzufassen, und macht Tippfehler in Flag-Namen durch TypeScript-Autovervollständigung unwahrscheinlicher.
// src/feature-flags/useFeatureFlag.ts
import { useContext } from "react";
import { FeatureFlagContext } from "./FeatureFlagProvider";
type FlagName = "new-checkout-flow" | "dark-mode" | "ai-search-suggestions";
export function useFeatureFlag(name: FlagName): boolean {
const { flags } = useContext(FeatureFlagContext);
return flags[name] ?? false;
}
// Usage inside a component
function CheckoutPage() {
const useNewCheckout = useFeatureFlag("new-checkout-flow");
return useNewCheckout ? <NewCheckoutFlow /> : <LegacyCheckoutFlow />;
}
Der Typ FlagName als String Union sorgt dafür, dass beim Aufruf von useFeatureFlag nur bekannte Flag-Namen erlaubt sind, ein Tippfehler wird sofort vom TypeScript-Compiler erkannt statt erst zur Laufzeit unbemerkt als false durchzugehen. Für Feature Flags in React-Projekten mit vielen Flags lohnt sich diese zentrale Typdefinition, weil sie gleichzeitig als Dokumentation aller aktiven Flags dient.
4. Managed Services: LaunchDarkly, Unleash und Flagsmith
Sobald Feature Flags in React Apps komplexere Anforderungen erfüllen müssen, etwa Zielgruppen-Segmentierung nach Land oder Abonnement-Typ, Echtzeit-Updates ohne Neuladen der Seite oder ein Audit-Log für Compliance-Zwecke, lohnt sich ein Managed Service. LaunchDarkly gilt als Marktführer mit ausgereiftem Dashboard und SDK für praktisch jede Sprache, ist dafür aber auch das teuerste Angebot der drei Optionen.
Unleash ist Open Source und kann komplett selbst gehostet werden, was besonders für Teams mit strengen Datenschutzanforderungen relevant ist. Flagsmith positioniert sich preislich zwischen beiden und bietet ebenfalls eine Self-Hosted-Variante an. Alle drei Services stellen ein React SDK bereit, das den eigenen Context-Ansatz aus dem vorherigen Abschnitt im Wesentlichen ersetzt, aber um Echtzeit-Updates über Server-Sent Events oder WebSockets erweitert.
// Example using Unleash's official React SDK
import { FlagProvider, useFlag, useVariant } from "@unleash/proxy-client-react";
const unleashConfig = {
url: "https://unleash.mironsoft.de/api/frontend",
clientKey: import.meta.env.VITE_UNLEASH_CLIENT_KEY,
appName: "shop-frontend",
};
function App() {
return (
<FlagProvider config={unleashConfig}>
<CheckoutPage />
</FlagProvider>
);
}
function CheckoutPage() {
const useNewCheckout = useFlag("new-checkout-flow");
const layoutVariant = useVariant("checkout-layout-experiment");
return useNewCheckout ? (
<NewCheckoutFlow layout={layoutVariant.name} />
) : (
<LegacyCheckoutFlow />
);
}
Der entscheidende Vorteil dieser SDKs für Feature Flags in React gegenüber dem eigenen Context: Änderungen im Dashboard werden ohne App-Neuladen live an alle verbundenen Clients gepusht, was Kill-Switch-Szenarien bei akuten Produktionsproblemen deutlich beschleunigt. Der Nachteil ist eine zusätzliche externe Abhängigkeit und, bei den gehosteten Varianten, laufende Kosten pro Nutzer oder Anfrage.
5. Progressive Rollouts: prozentuale Aktivierung und Zielgruppen
Ein zentraler Anwendungsfall für Feature Flags in React Apps ist der progressive Rollout: eine neue Funktion wird zunächst für 5 Prozent der Nutzer aktiviert, dann schrittweise auf 25, 50 und schließlich 100 Prozent erhöht, während Fehlerraten und Kennzahlen beobachtet werden. Dieses Muster reduziert den Blast Radius eines fehlerhaften Features drastisch, verglichen mit einem klassischen Alles-oder-nichts-Release.
Managed Services berechnen die Zugehörigkeit zur Prozent-Kohorte üblicherweise über einen konsistenten Hash der Nutzer-ID, sodass derselbe Nutzer bei jedem Aufruf denselben Flag-Wert erhält, statt bei jedem Seitenaufruf zufällig neu zugeordnet zu werden. Das ist wichtig, um inkonsistentes Nutzererlebnis innerhalb einer Session zu vermeiden.
// Simplified consistent-hash rollout logic (illustrates what SDKs do internally)
async function sha256Hex(input: string): Promise<string> {
const data = new TextEncoder().encode(input);
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
return Array.from(new Uint8Array(hashBuffer))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
}
async function isInRollout(userId: string, flagName: string, percentage: number): Promise<boolean> {
const hash = await sha256Hex(`${flagName}:${userId}`);
// Take the first 8 hex chars as a number between 0 and 0xFFFFFFFF
const bucket = parseInt(hash.slice(0, 8), 16) / 0xffffffff;
return bucket < percentage / 100;
}
// Same user always lands in the same bucket for this flag
const enabled = await isInRollout("user-4711", "new-checkout-flow", 25);
Für Feature Flags in React Apps mit einem selbst gebauten System lässt sich diese Logik serverseitig implementieren und das Ergebnis über die API der Frontend-Anwendung mitteilen, statt sie im Client zu berechnen. Managed Services übernehmen diese Berechnung bereits vollständig und bieten zusätzlich Zielgruppen-Filter nach Attributen wie Land, Gerätetyp oder Kundensegment im Dashboard an.
6. Feature Flags in Tests: deterministisches Verhalten erzwingen
Ohne besondere Vorkehrung führen Feature Flags in React Apps zu nicht deterministischen Tests, weil ein Test je nach externem Flag-Zustand mal den einen, mal den anderen Codepfad durchläuft. Die Lösung ist, den Feature-Flag-Context in Tests explizit mit festen Werten zu befüllen, statt echte Netzwerkaufrufe zuzulassen.
// CheckoutPage.test.tsx — deterministic feature flag values in tests
import { render, screen } from "@testing-library/react";
import { FeatureFlagContext } from "../feature-flags/FeatureFlagProvider";
import { CheckoutPage } from "./CheckoutPage";
function renderWithFlags(flags: Record<string, boolean>) {
return render(
<FeatureFlagContext.Provider value={{ flags, isLoading: false }}>
<CheckoutPage />
</FeatureFlagContext.Provider>
);
}
test("shows the new checkout flow when the flag is enabled", () => {
renderWithFlags({ "new-checkout-flow": true });
expect(screen.getByTestId("new-checkout-flow")).toBeInTheDocument();
});
test("falls back to the legacy checkout when the flag is disabled", () => {
renderWithFlags({ "new-checkout-flow": false });
expect(screen.getByTestId("legacy-checkout-flow")).toBeInTheDocument();
});
Dieses Muster stellt sicher, dass beide Codepfade eines Feature Flags in React-Setups tatsächlich getestet werden, unabhängig vom Zustand eines externen Services. Sobald ein Feature-Flag entfernt wird, sollten auch die zugehörigen Tests für den deaktivierten Zustand entfernt werden, was direkt zum nächsten Thema führt: dem geordneten Aufräumen alter Flags.
7. Feature-Flag-Debt vermeiden: Lifecycle und Aufräumen
Der größte praktische Nachteil von Feature Flags in React Apps zeigt sich nicht beim Einführen, sondern beim Vergessen. Jedes Flag, das nach vollständigem Rollout im Code verbleibt, addiert eine bedingte Verzweigung, die niemand mehr braucht, aber jeder Entwickler weiterhin lesen und verstehen muss. Dieses Phänomen wird treffend als Feature-Flag-Debt bezeichnet und wächst in großen Codebasen erfahrungsgemäß unbemerkt an.
Ein klarer Lifecycle hilft dagegen: jedes neue Flag bekommt bei der Erstellung ein geplantes Ablaufdatum und einen verantwortlichen Namen im Ticket-System eingetragen. Automatisierte Reports, die alle Flags auflisten, die seit mehr als 90 Tagen auf 100 Prozent stehen, erinnern Teams aktiv daran, den bedingten Code zu entfernen und das Flag selbst aus dem Managed Service zu löschen. Ohne diese Disziplin sammeln sich Feature Flags in React-Projekten typischerweise Dutzende toter Flags an, die die Codebasis unnötig komplex machen.
8. Server-seitige Flags in React Server Components
Mit React Server Components ergibt sich für Feature Flags in React Apps eine interessante Möglichkeit: die Flag-Auswertung kann komplett auf dem Server stattfinden, bevor überhaupt HTML an den Client gesendet wird. Das vermeidet einen sichtbaren Layout-Sprung, der bei client-seitiger Auswertung entsteht, wenn zuerst der Standardzustand gerendert und danach nach dem Laden der Flags neu gerendert wird.
Da Server Components bei jedem Request neu ausgeführt werden, lässt sich die Flag-Abfrage direkt in die Komponente integrieren, ohne einen zusätzlichen Loading-Zustand im Client zu benötigen. Das deaktivierte Feature erscheint so nie im initial gerenderten HTML, was zusätzlich verhindert, dass technisch versierte Nutzer im HTML-Quelltext Hinweise auf noch unveröffentlichte Funktionen finden.
9. Eigenbau gegen Managed Service im Vergleich
Die Entscheidung zwischen einem selbst gebauten Feature-Flag-System und einem Managed Service hängt von Team-Größe, Komplexität der Regeln und Budget ab. Die folgende Tabelle vergleicht die wichtigsten Eigenschaften.
| Kriterium | Eigener Context | Managed Service |
|---|---|---|
| Setup-Aufwand | Sehr gering | Konto und SDK-Integration nötig |
| Echtzeit-Updates | Nur mit eigener WebSocket-Logik | Eingebaut über SSE/WebSocket |
| Zielgruppen-Segmentierung | Muss selbst implementiert werden | Im Dashboard konfigurierbar |
| Kosten | Keine zusätzlichen Kosten | Pro Nutzer oder Anfrage, außer bei Unleash Self-Hosted |
| Audit-Log | Selbst zu bauen | Standardmäßig enthalten |
Für kleine Teams mit wenigen, einfachen Flags ist der eigene Context oft der pragmatischere Start in Feature Flags in React Apps, ohne eine externe Abhängigkeit einzugehen. Sobald Zielgruppen-Segmentierung, Echtzeit-Kill-Switches oder Compliance-Anforderungen wie ein Audit-Log wichtig werden, überwiegen die Vorteile eines Managed Service deutlich, insbesondere bei Unleash als selbst hostbare Open-Source-Option.
Mironsoft
Feature-Flag-Architektur und kontrollierte Rollouts für React Apps
Risikoarme Releases mit Feature Flags aufbauen?
Wir konzipieren ein passendes Feature-Flag-System für eure React App, ob leichtgewichtiger eigener Context oder Integration eines Managed Service mit progressiven Rollouts und Kill-Switch.
Architektur-Beratung
Eigenbau oder Managed Service anhand eurer Anforderungen entscheiden
SDK-Integration
LaunchDarkly, Unleash oder Flagsmith sauber in eure React App einbinden
Flag-Lifecycle
Prozesse gegen Feature-Flag-Debt etablieren und bestehende Altlasten aufräumen
10. Zusammenfassung
Feature Flags in React Apps trennen die technische Frage des Deployments von der Produktentscheidung, wann eine Funktion für Nutzer sichtbar wird. Ein selbst gebauter Context reicht für einfache Anwendungsfälle völlig aus und vermeidet externe Abhängigkeiten, während Managed Services wie LaunchDarkly, Unleash und Flagsmith bei komplexer Zielgruppen-Segmentierung und Echtzeit-Updates deutliche Vorteile bieten.
Progressive Rollouts über konsistentes Hashing reduzieren den Blast Radius fehlerhafter Features drastisch, während deterministisches Testen mit fest gesetzten Flag-Werten verhindert, dass die Test-Suite von externem Zustand abhängt. Der wichtigste organisatorische Aspekt bleibt der Lifecycle: ohne geplantes Aufräumen sammeln sich in jedem Projekt mit Feature Flags in React schnell tote Flags an, die die Codebasis unnötig aufblähen.
Feature Flags in React Apps — Das Wichtigste auf einen Blick
Deployment versus Release
Feature Flags entkoppeln, wann Code live geht, von der Entscheidung, wann eine Funktion sichtbar wird.
Eigener Context versus Managed Service
Für einfache Fälle reicht ein React Context, komplexe Zielgruppen-Logik lohnt LaunchDarkly, Unleash oder Flagsmith.
Progressive Rollouts
Konsistentes Hashing der Nutzer-ID sorgt für stabile Zuordnung zur Prozent-Kohorte über mehrere Sitzungen hinweg.
Flag-Lifecycle
Geplante Ablaufdaten und automatisierte Reports verhindern Feature-Flag-Debt in wachsenden Codebasen.