Feature Flags (Kill-Switch)
Kurz: Ein zur Laufzeit umschaltbarer Schalter, mit dem einzelne, nicht-kritische Features ohne Deployment ein-/ausgeschaltet werden können — z. B. um sie bei hoher Serverlast kurzfristig abzustellen.
Genauer: Wichtig ist die bewusste Trennung zwischen “nice-to-have”-Features, die über so einen Schalter abschaltbar sein dürfen, und kritischen Kernfunktionen (Checkout, Login), die niemals darüber deaktivierbar sein sollten — sonst wird aus einem “Last reduzieren”-Schalter versehentlich ein “Umsatz abschalten”-Schalter. Bei einem Ausfall des Speicher-Systems selbst (hier Redis) sollte der Fallback immer “alles an” sein, nie “alles aus”.
Kontext bei uns: app/utils/featureFlags.ts bei Emzett nutzt bewusst Upstash Redis statt der site_settings-Tabelle in Postgres — der Sinn des Kill-Switches ist ja gerade, bei Systemlast NICHT noch zusätzliche Postgres-Reads zu erzeugen. Aktuell nur für Soundeffekte und das Bewertungs-Popup im Einsatz, mit 30s In-Memory-Cache pro Lambda-Instanz.
Im Detail
Feature Flags werden in der Praxis für sehr unterschiedliche Zwecke eingesetzt, die sich in ihren Anforderungen deutlich unterscheiden: Kill-Switches (dieses Konzept — im Notfall schnell etwas abschalten, meist binär an/aus, betrifft alle Nutzer gleich), graduelle Rollouts (ein neues Feature erst für 5% der Nutzer aktivieren, dann langsam hochfahren, um das Risiko eines fehlerhaften Release zu begrenzen) und A/B-Tests (zwei Varianten parallel an unterschiedliche Nutzergruppen ausliefern, um zu messen, welche besser funktioniert). Kill-Switches haben die härtesten Antwortzeit-Anforderungen — im Ernstfall (z. B. eine fehlerhafte, last-erzeugende Funktion) zählt jede Sekunde, in der der Schalter noch nicht wirkt.
Der In-Memory-Cache pro Instanz ist ein bewusster Kompromiss zwischen Konsistenz und Geschwindigkeit: Ändert man einen Flag, dauert es bis zu 30 Sekunden, bis alle Serverinstanzen den neuen Wert übernehmen (statt sofort), aber dafür verursacht jede Anfrage nicht erneut einen Redis-Roundtrip. Für einen Kill-Switch, der im Zweifel „innerhalb einer Minute“ statt „sofort“ wirken muss, ist das ein akzeptabler Trade-off — für eine Zahlungsfreigabe wäre diese Verzögerung u. U. nicht akzeptabel.
Fail-Open vs. Fail-Closed
Ein zentraler Entwurfsentscheid bei jedem Feature-Flag-System ist das Verhalten, wenn die Flag-Quelle selbst (hier Redis) nicht erreichbar ist: Fail-Open bedeutet, im Zweifel wird das Feature als AN behandelt (der Normalzustand vor Einführung des Flags) — sinnvoll für nicht-kritische Features, wo ein kurzzeitig aktives Feature harmloser ist als ein fälschlich abgeschaltetes. Fail-Closed bedeutet das Gegenteil, im Zweifel AUS — sinnvoll für sicherheitsrelevante Flags (z. B. ein Flag, der eine noch nicht fertige, potenziell fehlerhafte Zahlungsfunktion steuert). Ein Kill-Switch-System sollte praktisch immer Fail-Open sein, da sein Zweck ja gerade ist, bestehende funktionierende Features abzuschalten — bricht die Flag-Infrastruktur selbst zusammen, soll das nicht automatisch ALLE Features mit ausschalten.
Abgrenzung zu Environment Variables
Eine simplere Alternative zu einem echten Feature-Flag-System sind reine Environment Variables — aber die erfordern zum Ändern immer ein neues Deployment, weil sie erst beim Start eines Prozesses gelesen werden. Ein Kill-Switch über Redis/eine Datenbank lässt sich dagegen zur Laufzeit ändern, ohne Deployment und ohne Neustart der laufenden Serverinstanzen — genau der Zeitgewinn, der im Ernstfall zählt.
Siehe auch: Upstash Redis, Rate Limiting, Dienst