App entwickeln lassen: Mit welchen Kosten rechnen? Was der Betrag nicht verrät
App entwickeln lassen: Mit welchem Budget rechnen? Der Betrag allein reicht nicht: Setzen Sie auf ein Ziel statt auf eine Liste, mit Nutzern ab dem ersten Monat.
13 Min. Lesezeit
Sébastien Nobour
Gründer von DEVEDANOS
Ein Feature Flag, auch Feature Toggle genannt, ist ein Schalter im Code. Je nach seinem Wert nimmt die Anwendung den einen oder den anderen Weg. DORA definiert es als bedingte Anweisung auf Grundlage von Konfigurationseinstellungen („Working in small batches“).
Das Werkzeug ist einfach. Die Fehler, die man damit macht, sind es weniger: Sie lassen in Produktion Kombinationen laufen, die niemand getestet hat. Pete Hodgson stellt das schon in der Einleitung seines Artikels zum Thema klar: Toggles bringen Komplexität mit sich, und man sollte versuchen, ihre Zahl gering zu halten („Feature Toggles (aka Feature Flags)“, 2017).
Hodgson teilt Toggles in vier Kategorien ein, nach zwei Kriterien: wie lange sie bestehen und wie oft sich ihre Entscheidung ändert.
Wer weiß, zu welcher Kategorie ein Toggle gehört, weiß auch, wie lange er bestehen darf. Hodgson fügt hinzu, dass umsichtige Teams ihre Toggles wie einen Bestand behandeln, der laufende Kosten verursacht, und diesen Bestand so klein wie möglich halten.
Martin Fowler macht daraus eine Regel: Release-Toggles sind das letzte Mittel, um eine Funktion in Produktion zu bringen. Die erste Wahl ist, sie so zu zerlegen, dass sich ihre Teile ohne Risiko einführen lassen („Feature Flag“, 2010).
Erste Technik: zuletzt anbinden. Man baut alles, was hinter der Oberfläche liegt, integriert es in den Trunk und deployt es, aber nichts führt dorthin. Der letzte Commit fügt den Einstiegspunkt hinzu, den Link oder den Button, und macht die Funktion sichtbar. Humble und Farley beschreiben eine neue Oberfläche, die unter einer Adresse entwickelt wurde, auf die kein Link verwies, und dann Stück für Stück an die bestehende Navigation angebunden wurde. Die Anwendung lief die ganze Zeit über (Continuous Delivery, 2010, S. 348). Fowler nennt dieses letzte Stück das Keystone Interface („Keystone Interface“, 2020).
Zweite Technik, um bestehendes Verhalten zu ändern: Branch by Abstraction. Man setzt eine Abstraktionsschicht vor den Teil, der sich ändern soll, baut die neue Implementierung neben der alten, entfernt dann die alte und manchmal auch die Abstraktion selbst (S. 349).
Bleibt der Fall, in dem jemand die Funktion in Produktion vor allen anderen sehen muss, um sie abzunehmen. Dann ist ein Toggle gerechtfertigt. Selbst dann rät Fowler, ihn nur auf die Benutzeroberfläche zu setzen, damit sich die Toggle Points nicht über den ganzen Code verteilen.
Warum diese Techniken es erlauben, jeden Tag unfertigen Code zu committen, erklären wir in unserem Artikel über Trunk-Based Development.
„In der Abnahmeumgebung an, in Produktion aus.“ Der Code sieht so aus:
// Anti-Pattern: Die Entscheidung hängt vom Namen der Umgebung ab.
if (process.env.APP_ENV === "staging") {
showInvoiceExport();
}
Das Problem passt in einen Satz: Die Produktion führt eine Kombination aus, die die Pipeline nie getestet hat. Die Akzeptanztests liefen mit eingeschaltetem Schalter; die Produktion läuft mit ausgeschaltetem.
MinimumCD beschreibt die Folge. Codepfade, die der Produktion vorbehalten sind, werden von der Testsuite nie ausgeführt, und wenn in Produktion eine andere Logik läuft, ist die Validierung in der Abnahmeumgebung von vornherein unvollständig („Hard-Coded Environment Assumptions“). Hodgson sieht es genauso: Jede umgebungsspezifische Übersteuerung widerspricht dem Ideal von Continuous Delivery, deploybare Einheiten und ihre Konfiguration so unabhängig von der Umgebung wie möglich zu halten. Humble und Farley hatten das Prinzip schon formuliert: überall dieselben Binaries deployen, damit das, was in Produktion geht, das ist, was getestet wurde (Continuous Delivery, S. 42).
Stattdessen: Der Zustand des Toggles ist Teil der Konfiguration der Anwendung, wird mit dem Artefakt ausgeliefert und ist in allen Umgebungen gleich. Was variiert, ist die Zielgruppe, die bei jedem Request ermittelt wird. Darauf kommen wir weiter unten zurück.
Um Zeit zu sparen, wird mit „alles an“ getestet. Das ist die Konfiguration von morgen. Es ist nicht die, die die Öffentlichkeit heute bekommt.
Hodgson ist da eindeutig. Am wichtigsten ist es, die Toggle-Konfiguration zu testen, die in Produktion aktiv sein wird. Es ist klug, auch die Fallback-Konfiguration zu testen. Und viele Teams ergänzen Tests mit allen Toggles eingeschaltet, um überraschende Regressionen beim nächsten Release zu vermeiden.
Stattdessen: mindestens zwei getestete Konfigurationen, die der Produktion und die, in der alles eingeschaltet ist.
Ein Release-Toggle dient dazu, laufende Arbeit zu verbergen. Laut Hodgson sollte er in der Regel nicht viel länger als ein bis zwei Wochen bestehen. Ein Toggle, der monatelang besteht, verbirgt keine laufende Arbeit mehr: Er hält zwei Versionen der Software am Leben, und jede muss getestet werden.
Stattdessen: ein Ablaufdatum ab der Erstellung und ein Test, der fehlschlägt, sobald es überschritten ist.
Jeder Toggle verdoppelt die Zahl der möglichen Kombinationen. Zwei Toggles ergeben vier, zehn ergeben mehr als tausend.
Hodgson beschreibt, was Teams tun, die diesen Bestand klein halten. Sie legen eine Aufgabe zum Entfernen ins Backlog, sobald ein Toggle angelegt wird. Sie geben jedem Toggle ein Ablaufdatum. Manche schreiben „Zeitbomben“, die einen Test fehlschlagen lassen oder sogar den Start der Anwendung verweigern, sobald das Datum überschritten ist. Andere legen eine Obergrenze für die Zahl der gleichzeitig erlaubten Toggles fest. Humble und Farley gaben schon denselben Rat: nicht zu viele Optionen vorhalten und sie sorgfältig zurückschneiden, sobald sie ihren Zweck erfüllt haben (Continuous Delivery, S. 348).
Eine neue Seite, ein neuer Abschnitt, ein neuer Bericht: Niemand sieht sie, solange kein Link dorthin führt. Ihnen einen Toggle hinzuzufügen heißt, für die Komplexität eines Schalters zu bezahlen, um etwas zu bekommen, das der fehlende Link schon geleistet hat.
Fowler formuliert es so: Release-Toggles nur einsetzen, wenn man weder in kleinen Teilen ausliefern noch ein Keystone Interface verwenden kann („Feature Flag“).
2012 verlor ein amerikanisches Brokerhaus in etwa 45 Minuten mehr als 460 Millionen Dollar. Die Verfügung der SEC, der US-Börsenaufsicht, schildert den Ablauf (Verfügung Nr. 34-70694, 2013, Ziff. 13 bis 17).
Der neue Code verwendete ein Flag wieder, das früher eine alte Funktion aktiviert hatte. Diese Funktion wurde seit Jahren nicht mehr genutzt, ihr Code war aber noch vorhanden und konnte noch aufgerufen werden. Beim Deployment erhielt einer der acht Server den neuen Code nicht. Auf diesem Server aktivierten die Orders mit dem wiederverwendeten Flag den alten Code, der nicht mehr richtig funktionierte.
Zwei Fehler kamen zusammen: ein Flag, das seine Bedeutung änderte, und toter Code, der nie entfernt wurde. Farley führt denselben Fall für einen dritten an, das manuelle Deployment, Server für Server (Continuous Delivery Pipelines, 2021, S. 112 und 113). Hodgson nennt ihn als Warnung davor, was passiert, wenn man seine Toggles schlecht verwaltet.
Stattdessen: Ein Toggle hat genau eine Bedeutung, für genau eine Funktion, und verschwindet mit ihr. Ein neuer Bedarf bekommt einen neuen Toggle unter einem neuen Namen. Der Code, den der Toggle ersetzt, wird zusammen mit ihm gelöscht.
Der Toggle wird im Browser ausgewertet. Der neue Abschnitt steht in der Seite, ist aber durch eine CSS-Regel oder das Attribut hidden verborgen. Der Inhalt wird trotzdem an jeden Besucher ausgeliefert.
Die HTML-Spezifikation sagt es deutlich. Das Attribut hidden wird in der Regel über CSS umgesetzt und kann daher durch CSS aufgehoben werden. Verborgene Elemente bleiben aktiv: Ihre Skripte laufen, ihre Formulare werden abgeschickt (WHATWG, HTML Living Standard, „The hidden attribute“). Jeder Besucher kann über die Entwicklertools seines Browsers anzeigen, was verborgen war.
Stattdessen: auf dem Server entscheiden. Ein Besucher, der nicht zur Zielgruppe gehört, bekommt den Inhalt schlicht nicht.
Hinter jedem Toggle stecken zwei Fragen. In welchem Zustand ist er? Wer sieht die Funktion? Beides zu vermischen ist Anti-Pattern 1.
Der Zustand ist Teil der Konfiguration der Anwendung. MinimumCD ordnet Feature Flags der anwendungsinternen Konfiguration zu, die nicht je nach Umgebung variiert. Diese Konfiguration wird mit dem unveränderlichen Artefakt deployt und ändert sich nicht mehr, sobald das Artefakt gebaut ist („Application Configuration“). Dasselbe Artefakt mit demselben Zustand durchläuft also die Abnahme und geht dann in Produktion.
Die Zielgruppe wird bei jedem Request ermittelt. Hodgson empfiehlt, den Toggle Point, die Stelle im Code, an der die Entscheidung angewendet wird, von der Logik zu trennen, die diese Entscheidung trifft. Er weist auch auf das Risiko von Übersteuerungen per Cookie, URL-Parameter oder HTTP-Header hin: Ein neugieriger oder böswilliger Nutzer kann dann den Zustand des Toggles selbst ändern. Die Zielgruppe wird deshalb aus einer serverseitig geprüften Identität ermittelt, nie aus einem Parameter, den der Browser fälschen kann.
Hier eine typisierte Toggle-Registry, für diesen Artikel geschrieben:
export type ToggleState = "in-progress" | "in-review";
export interface ReleaseToggle {
name: string;
state: ToggleState;
createdOn: string;
expiresOn: string;
}
export const MAX_RELEASE_TOGGLES = 3;
export const releaseToggles: readonly ReleaseToggle[] = [
{
name: "invoice-export",
state: "in-review",
createdOn: "2026-10-05",
expiresOn: "2026-10-19",
},
];
Und die Entscheidung, getrennt vom Toggle Point. Die Zielgruppe ergibt sich aus dem authentifizierten Nutzer, nie aus dem Namen der Umgebung:
export type Audience = "public" | "reviewers";
export interface Viewer {
roles: readonly string[];
}
export function audienceFor(viewer: Viewer | null): Audience {
return viewer?.roles.includes("reviewer") ? "reviewers" : "public";
}
export function isVisible(toggle: ReleaseToggle, audience: Audience): boolean {
return toggle.state === "in-review" && audience === "reviewers";
}
Ein Toggle im Zustand in-progress ist für niemanden sichtbar. Ein Toggle im Zustand in-review ist nur für Reviewer sichtbar. Einen Zustand „für alle sichtbar“ gibt es nicht: Die Funktion für alle sichtbar zu machen heißt, den Toggle zu entfernen.
Zwei Tests reichen aus, um Anti-Pattern 2 abzudecken. Der erste rendert die Seite so, wie die Öffentlichkeit sie erhält, mit der tatsächlich ausgelieferten Konfiguration. Der zweite rendert sie so, wie ein Reviewer sie erhält, mit eingeschaltetem Toggle. Sie sind hier in der gemeinsamen Syntax von Jest und Vitest geschrieben; renderInvoicesPage ist eine fiktive Funktion, die die Seite auf dem Server rendert.
test("öffentliche Besucher erhalten die Seite ohne den Rechnungsexport", async () => {
const page = await renderInvoicesPage({ viewer: null });
expect(page.text).not.toContain("Rechnungen exportieren");
});
test("ein Reviewer erhält die Seite mit dem Rechnungsexport", async () => {
const page = await renderInvoicesPage({ viewer: { roles: ["reviewer"] } });
expect(page.text).toContain("Rechnungen exportieren");
});
Der erste Test schützt das, was die Öffentlichkeit heute sieht. Der zweite prüft, ob die Funktion funktioniert, bevor sie für alle sichtbar wird.
Ein Release-Toggle durchläuft drei Phasen. Er entsteht mit der Funktion, für alle unsichtbar. Er geht in die Abnahme, sichtbar für die Reviewer. Dann entfernt ihn der Release-Commit, zusammen mit dem alten Pfad, falls es einen gab: Die Bedingung verschwindet, und der neue Code wird zum einzigen Pfad.
Damit dieses Ende auch kommt, bilden zwei Tests die „Zeitbombe“, die Hodgson beschreibt:
test("kein Release-Toggle hat sein Ablaufdatum überschritten", () => {
const today = new Date().toISOString().slice(0, 10);
const expired = releaseToggles
.filter((toggle) => toggle.expiresOn < today)
.map((toggle) => toggle.name);
expect(expired).toEqual([]);
});
test("die Zahl der Toggles überschreitet die Obergrenze nicht", () => {
expect(releaseToggles.length).toBeLessThanOrEqual(MAX_RELEASE_TOGGLES);
});
Der erste Test schlägt an dem Tag fehl, an dem ein Datum überschritten wird, und die Pipeline geht auf Rot. Das Team hat dann zwei Optionen: die Funktion in Produktion bringen und den Toggle entfernen, oder die Funktion entfernen. Der zweite Test verhindert, dass ein neuer Toggle angelegt wird, solange die Obergrenze erreicht ist.
Für unsere Kunden dient ein Toggle dazu, eine Funktion in Produktion abzunehmen, bevor sie für alle sichtbar ist. So beschreiben wir es in unserem Ansatz: „Sobald die Funktion freigegeben ist, machen wir die neue Version sichtbar. Der Code ist bereits über unsere automatisierte Deployment-Pipeline bereitgestellt und wartet nur noch auf die Aktivierung in Produktion. Wenn Sie das lieber nicht aus der Hand geben, prüfen Sie jede Funktion selbst und geben sie frei.“
Bei der Entwicklung individueller Software arbeiten wir zu einem festen Budget, das am Ende jedes Monats für die geleistete Arbeit abgerechnet wird. Ihre Anwendung ist innerhalb eines Monats nutzbar. Ihre Teams arbeiten ab dem ersten Monat damit, und diese Nutzung entscheidet, was wir im Folgemonat entwickeln.
Und damit das, was im letzten Monat funktioniert hat, auch in diesem Monat noch funktioniert, wenden wir die Null-Regressions-Garantie an: Jede Regression wird behoben und durch einen automatisierten Test abgesichert, damit sie nicht wiederkehrt, ohne Aufpreis.
In unserem Leitfaden finden Sie die Fragen, die Sie stellen sollten, bevor Sie ein Angebot für Softwareentwicklung unterschreiben.
Möchten Sie eine Software entwickeln lassen, und soll sie auf diese Weise gebaut werden? Sprechen wir darüber?
Zitate aus dem Englischen haben wir selbst übersetzt.
App entwickeln lassen: Mit welchem Budget rechnen? Der Betrag allein reicht nicht: Setzen Sie auf ein Ziel statt auf eine Liste, mit Nutzern ab dem ersten Monat.
Prädiktiver oder adaptiver Ansatz? Die eigentliche Trennlinie verläuft nicht zwischen V-Modell und agilem Vorgehen, sondern zwischen Vorhersagen und Anpassen. Was das für Software bedeutet.
Was ist ein MVP in der Softwareentwicklung? Nicht die Version 1 einer Software, sondern der kleinste Test, der zeigt, ob sich die Entwicklung einer Idee lohnt. Und was danach kommt.