Zum Inhalt
wannacode

Deine erste Architekturaufgabe

Kurz gesagt: Ein Projekt zur Softwarearchitektur: Anforderungen mit Zahlen, drei Optionen mit ihren Kosten, eine Entscheidungsmatrix, ein ADR, ein Migrationsplan und ein Risikoregister für einen wachsenden Shop.

Problem

Die Aufgabe

Der Inhaber des Shops schreibt:

Ab dem Frühling verkaufen wir auch in Griechenland und Kroatien. Außerdem lassen wir externe Händler zu: Sie laden ihre Produkte selbst hoch, wir nehmen eine Provision. Der Black Friday darf nicht wieder so abstürzen wie letztes Jahr. Was kostet das, und wann ist es fertig?

Das ist die echte erste Aufgabe des Architekten. Niemand sagt dir „baue einen modularen Monolithen“. Man nennt dir einen Wunsch, und du musst Folgendes zurückbringen:

  1. Anforderungen mit Zahlen, die sich messen lassen;
  2. mindestens zwei Optionen mit ihren Kosten;
  3. eine Entscheidung, festgehalten in einem ADR;
  4. einen Migrationsplan für das laufende System, der bei jedem Schritt umkehrbar ist;
  5. ein Risikoregister;
  6. eine Prüfung, die die neuen Grenzen bewacht.

Die drei Länder liegen in der Eurozone, das Geld bleibt also in Euro. Alles andere unterscheidet sich: Sprache, MwSt., Kuriere, Rückgaberecht.

Die Idee

Der Plan

Jeder Schritt nutzt eine Lektion des Moduls:

  1. Die Anforderungen mit Perzentilen und einem Verfügbarkeitsbudget, wie in „Nichtfunktionale Anforderungen, die du messen kannst“.
  2. Die Optionen mit den Trade-offs aus „Trade-offs“ und den Formen aus „Monolith, modularer Monolith, Services“.
  3. Ein ADR wie in „ADR: Architekturentscheidungen festhalten“.
  4. Die Migration mit Strangler Fig und Expand–Contract.
  5. Die Risiken wie in „Architektur-Review“.
  6. Eine Fitness Function für die Grenzen, ebenfalls aus dem Review.

Jeder Schritt hat ein Ergebnis, mit dem du deines vergleichst. Die Lösungen stehen nach jedem Schritt unter „Lösung“, und am Ende gibt es eine Musterlösung der ganzen Aufgabe. Versuch es zuerst selbst: Architektur lernt man durch Entscheiden, nicht durch Lesen von Entscheidungen.

Übung

Schritt 1: Anforderungen mit Zahlen

„Mach es schnell und lass es nicht abstürzen“ ist keine Anforderung. Mach aus jedem dieser Wünsche ein Qualitätsszenario: Quelle, Auslöser, Reaktion und ein Maß mit einer Zahl.

  • „Der Black Friday darf nicht abstürzen.“
  • „Die Zahlung muss immer funktionieren.“
  • „Händler sollen ihre Verkäufe sofort sehen.“
  • „Ein neues Land soll sich leicht starten lassen.“
Beispielszenarien
Qualität Quelle und Auslöser Reaktion Maß
Skalierung Black Friday, 5-facher üblicher Verkehr die Zahlungsseite antwortet p95 unter 800 ms, p99 unter 2 s
Verfügbarkeit ein Käufer in einem der drei Länder zahlt die Zahlung geht durch 99,7 % der Minuten eines Monats, höchstens 130 Min. Ausfall
Konsistenz ein Händler ändert den Bestand Käufer sehen es innerhalb von 2 Min.; alles, was über den Bestand hinaus verkauft wurde, wird innerhalb von 1 Stunde mit einer Entschuldigung storniert
Isolation ein Händler lädt auf einmal 10.000 Produkte hoch der Shop arbeitet wie zuvor p95 des Katalogs wächst um höchstens 20 %
Erweiterbarkeit ein viertes Land startet mit Einstellungen innerhalb von 2 Wochen, ohne Änderung am Bestellcode

Die Zahlen hier sind Beispiele: In einer echten Aufgabe kommen sie vom Inhaber („Was kostet eine Stunde Ausfall am Black Friday?“) und aus der Messung des heutigen Systems.

Aus „Händler sollen ihre Verkäufe sofort sehen“ wurde „innerhalb von 2 Minuten“. Das ist eine bewusste Wahl für Eventual Consistency: „sofort“ würde die Systeme der Händler in den Zahlungspfad setzen.

Übung

Schritt 1b: das Budget der Kette

Die Zahlung läuft über die Shop-API, die Datenbank und den Zahlungsanbieter. Die erste Idee für die Händler ist, bei jeder Zahlung das System des Händlers zu fragen, ob der Artikel auf Lager ist. Die Verfügbarkeiten und p95-Werte unten sind Beispiele (für Anbieter aus dem Vertrag, für Händler aus einem Test):

List<Hop> chain = List.of(
new Hop("Shop-API", 99.95, 120),
new Hop("Datenbank", 99.95, 40),
new Hop("Zahlung", 99.9, 450),
new Hop("Bestand bei den Händlern", 99.0, 400));
budget("Der Bestand bei den Händlern liegt im Zahlungspfad:", chain, 99.7, 800);
budget("Ohne ihn (eine Kopie, aus Ereignissen aktualisiert):", chain.subList(0, 3), 99.7, 800);

Schreibe budget, das die Verfügbarkeit entlang der Kette multipliziert, die p95-Werte addiert und mit dem Budget vergleicht: 99,7 % (Ausfall in Minuten für einen Monat mit 30 Tagen) und p95 unter 800 ms. Lass es mit den Händlern in der Kette laufen und ohne sie:

Ausgabe
Der Bestand bei den Händlern liegt im Zahlungspfad:
Verfügbarkeit 98.80% → 517 Min. Ausfall pro Monat (Budget 130) ✗
p95 1010 ms (Budget 800) ✗
Ohne ihn (eine Kopie, aus Ereignissen aktualisiert):
Verfügbarkeit 99.80% → 86 Min. Ausfall pro Monat (Budget 130) ✓
p95 610 ms (Budget 800) ✓
Lösung
record Hop(String name, double availability, int p95) {}
static final int MINUTES_PER_MONTH = 30 * 24 * 60;
static void budget(String title, List<Hop> chain, double target, int p95Budget) {
double availability = 1;
int p95 = 0;
for (Hop hop : chain) {
availability *= hop.availability() / 100;
p95 += hop.p95(); // die Summe der p95-Werte ist eine Obergrenze, nicht das genaue p95 der Kette
}
long down = Math.round((1 - availability) * MINUTES_PER_MONTH);
long allowed = Math.round((1 - target / 100) * MINUTES_PER_MONTH);
System.out.println(title);
System.out.printf(Locale.ROOT, " Verfügbarkeit %.2f%% → %d Min. Ausfall pro Monat (Budget %d) %s%n",
availability * 100, down, allowed, down <= allowed ? "✓" : "✗");
System.out.printf(" p95 %d ms (Budget %d) %s%n", p95, p95Budget, p95 <= p95Budget ? "✓" : "✗");
}
Locale.ROOT in printf: Sonst wird der Dezimalpunkt auf einem Rechner mit deutscher Spracheinstellung zu einem Komma.

Ein System mit 99,0 % zieht die Zahlung auf 98,80 % herunter: 517 Minuten Ausfall im Monat bei einem Budget von 130. Auch die Latenz addiert sich. Der Bestand bei den Händlern kann also kein synchroner Schritt der Zahlung sein. Er wird zu einer Kopie auf unserer Seite, die aus Ereignissen aktualisiert wird (die Lektion „Ereignisse und Warteschlangen“), und der Preis dafür ist, dass die Kopie hinterherhinken kann. Diese Entscheidung kommt aus den Zahlen, nicht aus Geschmack.

Die Summe der p95-Werte ist eine Obergrenze: Das p95 der Kette ist meist niedriger, weil selten alle Schritte gleichzeitig langsam sind. Für ein Budget ist die Obergrenze die sichere Seite.

Übung

Schritt 2: Optionen mit ihren Kosten

Beschreibe mindestens zwei Optionen, die wirklich funktionieren würden, und die Kosten jeder: Geld, Zeit, Leute und was du verlierst. Drei naheliegende Optionen:

A: drei Kopien des Shops B: ein modularer Monolith C: Services vom ersten Tag an
Idee ein Shop pro Land das Land ist eine Einstellung, Händler sind ein Modul Katalog, Bestellungen, Händler, Zahlung getrennt
Zeit bis zum Start am schnellsten mittel am langsamsten
Kosten pro Monat dreifache Infrastruktur und Pflege ein System viele Services, Monitoring, Netzwerk
was du verlierst die Kopien laufen auseinander und lassen sich nie wieder zusammenführen eine Änderung für ein Land geht an alle drei raus das Team lernt verteilte Systeme, während es ausliefert

Der Vergleich passiert in einer Entscheidungsmatrix: Kriterien mit Gewichten (wie wichtig jedes ist) und eine Punktzahl von 1 bis 5 für jede Option. Gewichte und Punkte gehören dem Team und dem Inhaber:

// Zeit bis zum Start 3, Verfügbarkeit 3, Kosten pro Monat 2, das Team kann es 2, Umkehrbarkeit 2
int[] weights = {3, 3, 2, 2, 2};
List<Option> options = List.of(
new Option("A: drei Kopien des Shops", new int[] {5, 3, 2, 4, 2}),
new Option("B: modularer Monolith, das Land ist eine Einstellung", new int[] {3, 4, 4, 4, 4}),
new Option("C: Services vom ersten Tag an", new int[] {1, 4, 2, 2, 2}));
for (Option o : options) System.out.println(o.name() + ": " + total(o, weights));
System.out.println("Wahl: " + best(options, weights).name());

Schreibe total (die Summe aus Gewicht × Punkten) und best:

Ausgabe
A: drei Kopien des Shops: 40
B: modularer Monolith, das Land ist eine Einstellung: 45
C: Services vom ersten Tag an: 27
Wahl: B: modularer Monolith, das Land ist eine Einstellung
Lösung
record Option(String name, int[] scores) {}
static int total(Option option, int[] weights) {
int sum = 0;
for (int i = 0; i < weights.length; i++) sum += weights[i] * option.scores()[i];
return sum;
}
static Option best(List<Option> options, int[] weights) {
Option best = options.get(0);
for (Option o : options)
if (total(o, weights) > total(best, weights)) best = o;
return best;
}

Die Matrix trifft die Entscheidung nicht für dich. Sie macht die Gewichte sichtbar: Sagt der Inhaber „Zeit ist alles“, ist das eine Zahl, über die man streiten kann.

Übung

Schritt 2b: wie brüchig ist die Entscheidung

Die Punkte sind Einschätzung. Prüfe die Sensitivität: Erhöhe das Gewicht der Zeit bis zum Start ab 3 und finde das erste Gewicht, bei dem eine andere Option gewinnt:

Ausgabe
Bei Gewicht 6 für die Zeit gewinnt A: drei Kopien des Shops (55 gegen 54)

Die Wahl hält, bis die Zeit doppelt so wichtig wird wie die Verfügbarkeit. Das sagst du dem Inhaber: „Wenn der Start im Frühling wichtiger ist als alles andere, gewinnen die Kopien, aber hier sind ihre Kosten.“

Übung

Schritt 3: der ADR

Halte die Entscheidung in einem ADR fest. Die Prüfung aus der ADR-Lektion schaut nur, ob die Abschnitte da sind. Hier machst du sie strenger, denn ein ADR ohne Kosten ist Werbung:

  • mindestens zwei Optionen unter „Optionen“;
  • mindestens eine Konsequenz, die mit „- schlecht:“ beginnt;
  • „Neu bewerten, wenn“ enthält eine Zahl, die sich messen lässt.

Hier ist der Entwurf, den dir ein Kollege schickt:

adr-014-entwurf.md
# ADR-014: Drei Länder und Händler im modularen Monolithen
## Status
vorgeschlagen
## Kontext
Der Shop startet in Griechenland und Kroatien und lässt externe Händler zu. Zahlung: 99.7% im Monat, p95 unter 800 ms.
## Optionen
- B: modularer Monolith, das Land ist eine Einstellung, Händler sind ein Modul
## Entscheidung
B. Der Bestand bei den Händlern kommt asynchron an und liegt nicht im Zahlungspfad.
## Konsequenzen
- gut: ein Code für die drei Länder

Schreibe checkAdr und lass es auf dem Entwurf und auf deinem endgültigen ADR laufen:

Ausgabe
Entwurf: 3
✗ Abschnitt „Neu bewerten, wenn“ fehlt
✗ Optionen: 1, es braucht mindestens 2
✗ Konsequenzen: kein einziges „schlecht:“
Endfassung: ✓ OK
Lösung
static final List<String> REQUIRED = List.of("Status", "Kontext", "Optionen", "Entscheidung", "Konsequenzen", "Neu bewerten, wenn");
static Map<String, List<String>> sections(String adr) {
Map<String, List<String>> result = new LinkedHashMap<>();
List<String> current = null;
for (String line : adr.strip().split("\n")) {
if (line.startsWith("## ")) {
current = new ArrayList<>();
result.put(line.substring(3), current);
} else if (current != null && !line.isBlank()) {
current.add(line);
}
}
return result;
}
static List<String> checkAdr(String adr) {
Map<String, List<String>> s = sections(adr);
List<String> problems = new ArrayList<>();
for (String name : REQUIRED)
if (!s.containsKey(name)) problems.add("✗ Abschnitt „" + name + "“ fehlt");
List<String> options = s.getOrDefault("Optionen", List.of());
long count = options.stream().filter(l -> l.startsWith("- ")).count();
if (count < 2) problems.add("✗ Optionen: " + count + ", es braucht mindestens 2");
List<String> cons = s.getOrDefault("Konsequenzen", List.of());
if (cons.stream().noneMatch(l -> l.startsWith("- schlecht:")))
problems.add("✗ Konsequenzen: kein einziges „schlecht:“");
List<String> revisit = s.get("Neu bewerten, wenn");
if (revisit != null && !String.join(" ", revisit).matches(".*\\d.*"))
problems.add("✗ Neu bewerten, wenn: keine Zahl, die sich messen lässt");
return problems;
}
for (String[] adr : new String[][] {{"Entwurf", DRAFT}, {"Endfassung", FINAL}}) {
List<String> problems = checkAdr(adr[1]);
System.out.println(adr[0] + ": " + (problems.isEmpty() ? "✓ OK" : problems.size()));
for (String p : problems) System.out.println(" " + p);
}

Der endgültige ADR steht unten in der Musterlösung.

Übung

Schritt 4: der Migrationsplan

Der Shop läuft gerade. Bestellungen müssen das Land kennenlernen, ohne Stillstand und ohne große Umschaltung. Bring die Schritte in eine Reihenfolge, in der sich jeder bis zum letzten rückgängig machen lässt:

  1. die Spalte country hinzufügen (leer, der alte Code liest sie nicht)
  2. 10 %, 50 %, 100 % nur bei grünen Metriken, bei roten zurück
  3. der neue Code schreibt country, der alte läuft weiter
  4. Backfill: alte Bestellungen bekommen BG, danach die Anzahl vergleichen
  5. 1 % des griechischen Verkehrs läuft über den neuen Weg (ein Flag)
  6. den alten Pfad abschalten und den alten Code entfernen

Die Anteile (1 %, 10 %, 50 %, 100 %) werden nicht nach dem Kalender erhöht, sondern nach Metriken. Schreibe rollout: Für jeden Anteil liest es die nächsten aufgezeichneten Metriken. Bei Grün (höchstens 5 Fehler pro 1000 und p95 unter 800 ms) geht es weiter, bei Rot schiebt es den Verkehr auf den vorherigen Anteil zurück und versucht es nach der Korrektur erneut. Die Metriken sind ein aufgezeichnetes Szenario, die Ausgabe ist also immer dieselbe:

List<Metrics> observed = List.of(
new Metrics(2, 610),
new Metrics(9, 700),
new Metrics(3, 640),
new Metrics(4, 690),
new Metrics(3, 700));
rollout(new int[] {1, 10, 50, 100}, observed.iterator());
Ausgabe
1%: Fehler 2/1000, p95 610 ms ✓
10%: Fehler 9/1000, p95 700 ms ✗ zurück auf 1%
10%: Fehler 3/1000, p95 640 ms ✓
50%: Fehler 4/1000, p95 690 ms ✓
100%: Fehler 3/1000, p95 700 ms ✓
Der alte Pfad ist abgeschaltet: der Punkt ohne Rückweg.
Lösung
record Metrics(int errorsPer1000, int p95) {
boolean healthy() {
return errorsPer1000 <= 5 && p95 <= 800;
}
}
static void rollout(int[] shares, Iterator<Metrics> observed) {
int step = 0;
while (step < shares.length) {
Metrics m = observed.next();
String line = shares[step] + "%: Fehler " + m.errorsPer1000() + "/1000, p95 " + m.p95() + " ms";
if (m.healthy()) {
System.out.println(line + " ✓");
step++;
} else {
int back = step == 0 ? 0 : shares[step - 1];
System.out.println(line + " ✗ zurück auf " + back + "%");
}
}
System.out.println("Der alte Pfad ist abgeschaltet: der Punkt ohne Rückweg.");
}

Es gibt nur einen Punkt ohne Rückweg, und er kommt zuletzt: das Entfernen des alten Pfads. Alles davor macht man mit einem Flag rückgängig. Die Spalte wird leer hinzugefügt (Expand) und erst entfernt, wenn sie niemand mehr liest (Contract).

Probier es aus

Finde den Fehler

Ein Kollege schreibt die Prüfung der Metriken. Der Rollout läuft glatt, aber 10 % starten mit 9 Fehlern pro 1000:

Ausgabe
1%: Fehler 2/1000, p95 610 ms ✓
10%: Fehler 9/1000, p95 700 ms ✓
50%: Fehler 3/1000, p95 640 ms ✓
100%: Fehler 4/1000, p95 690 ms ✓
Der alte Pfad ist abgeschaltet: der Punkt ohne Rückweg.

Wähle die Zeile mit dem Fehler.

Übung

Schritt 5: das Risikoregister

Hier sind die Risiken, die das Team gesammelt hat. Schreibe register, das sie sortiert (nach Punktzahl, bei Gleichstand nach Auswirkung) und die Maßnahme zeigt. Ein hohes Risiko (12 oder mehr) ohne Maßnahme ist ein offenes Loch und wird mit „✗“ angezeigt, ein kleineres ohne Maßnahme wird bewusst akzeptiert.

List<Risk> risks = List.of(
new Risk("MwSt. und Rechnungen werden zwischen den Ländern durcheinandergebracht", 3, 5, "eine Testrechnung für jedes Land vor 1%"),
new Risk("der Kurier in Kroatien hat keine Tracking-API", 2, 4, ""),
new Risk("der Black-Friday-Verkehr in Griechenland ist 5-mal so hoch", 3, 4, ""),
new Risk("die Bestandskopie hinkt hinterher und wir verkaufen Ausverkauftes", 4, 3, "eine Reservierung bei der Zahlung und eine Stornierung mit Entschuldigung"),
new Risk("das Team kennt das Händlermodul nicht", 3, 2, "Pair Programming in den ersten zwei Monaten"));
register(risks);
Ausgabe
15 MwSt. und Rechnungen werden zwischen den Ländern durcheinandergebracht → eine Testrechnung für jedes Land vor 1%
12 der Black-Friday-Verkehr in Griechenland ist 5-mal so hoch → ✗ keine Maßnahme
12 die Bestandskopie hinkt hinterher und wir verkaufen Ausverkauftes → eine Reservierung bei der Zahlung und eine Stornierung mit Entschuldigung
8 der Kurier in Kroatien hat keine Tracking-API → (akzeptiert)
6 das Team kennt das Händlermodul nicht → Pair Programming in den ersten zwei Monaten
Lösung
record Risk(String name, int probability, int impact, String measure) {
int score() {
return probability * impact;
}
}
static void register(List<Risk> risks) {
List<Risk> sorted = new ArrayList<>(risks);
sorted.sort(Comparator.comparingInt(Risk::score).thenComparingInt(Risk::impact).reversed());
for (Risk r : sorted) {
String measure = !r.measure().isEmpty() ? r.measure()
: r.score() >= 12 ? "✗ keine Maßnahme" : "(akzeptiert)";
System.out.printf("%2d %s → %s%n", r.score(), r.name(), measure);
}
}

Für den Black Friday ist die Maßnahme ein Lasttest mit dem 5-fachen Verkehr vor dem Start in Griechenland und ein Plan, was zuerst abgeschaltet wird (Empfehlungen, Suche nach Händler), damit die Zahlung überlebt. Skalierung steht in „Skalierung“, und das Abschalten ist wie der Circuit Breaker aus „Wenn der Nachbar langsam ist: Timeout, Retry, Circuit Breaker“.

Übung

Schritt 6: eine Fitness Function für die neuen Grenzen

Das Händlermodul ist neu. Fängt es in seinem ersten Monat an, das Innere der Bestellungen zu lesen, lässt es sich ein Jahr später nicht mehr herauslösen. Die Regel: Jedes Modul hat eine api (den öffentlichen Teil) und ein internal; ein anderes Modul wird nur über seine api aufgerufen. Die Imports der Module:

imports.txt
sellers.internal -> orders.api
sellers.internal -> orders.internal
orders.internal -> payments.api
orders.internal -> sellers.internal
catalog.internal -> sellers.api
orders.internal -> orders.db
Ausgabe
✗ sellers.internal -> orders.internal: ruft das Innere auf von orders
✗ orders.internal -> sellers.internal: ruft das Innere auf von sellers
Verstöße: 2
if (otherModule && !to[1].equals(""))
Lösung
// ein fremdes Modul wird nur über seine api aufgerufen
static List<String> boundaryViolations(List<String> imports) {
List<String> found = new ArrayList<>();
for (String line : imports) {
String[] ends = line.split(" -> ");
String[] from = ends[0].split("\\.");
String[] to = ends[1].split("\\.");
boolean otherModule = !from[0].equals(to[0]);
if (otherModule && !to[1].equals("api"))
found.add(line + ": ruft das Innere auf von " + to[0]);
}
return found;
}
List<String> imports = List.of(
"sellers.internal -> orders.api",
"sellers.internal -> orders.internal",
"orders.internal -> payments.api",
"orders.internal -> sellers.internal",
"catalog.internal -> sellers.api",
"orders.internal -> orders.db");
List<String> found = boundaryViolations(imports);
for (String v : found) System.out.println("✗ " + v);
System.out.println("Verstöße: " + found.size());

Lass sie bei jedem Commit mit einer Baseline laufen, wie im Review. Die heutigen zwei Verstöße werden behoben, bevor das Händlermodul ausgeliefert wird.

Sieh es dir an

Musterlösung

Vergleiche deine Lösung mit dieser. Sie ist nicht die einzig richtige: Richtig ist jede Lösung, deren Zahlen, Optionen und Risiken ehrlich und festgehalten sind.

Die Anforderungen. Die fünf Szenarien aus Schritt 1. Die wichtigsten: Zahlung mit 99,7 % und p95 unter 800 ms, und der Bestand bei den Händlern eventuell konsistent, innerhalb von 2 Minuten.

Die Optionen. A, B und C mit ihren Kosten; gewählt wird B mit 45 Punkten gegen 40 und 27. Die Entscheidung hält bis zu einem Gewicht von 6 für die Zeit.

Die Architektur von B.

  • Das Land ist eine Einstellung: Sprache, MwSt., Kurier und Rückgaberegeln kommen aus einer Tabelle pro Land, nicht aus einem if im Code.
  • Händler sind ein Modul im Monolithen, mit eigenen Daten und einer öffentlichen api („Monolith, modularer Monolith, Services“).
  • Der Bestand bei den Händlern kommt als Ereignisse in eine Kopie auf unserer Seite (ein Outbox auf ihrer Seite oder eine Warteschlange auf unserer).
  • Alles, was über den Bestand hinaus verkauft wurde, wird mit einer Saga storniert: Reservierung, Zahlung und bei einer Absage des Händlers eine Rückerstattung.
  • Das Lager 2009 und die Buchhaltung bleiben wie bisher hinter einem Anti-Corruption-Layer.

Der ADR.

adr-014.md
# ADR-014: Drei Länder und Händler im modularen Monolithen
## Status
akzeptiert
## Kontext
Der Shop startet in Griechenland und Kroatien und lässt externe Händler zu. Zahlung: 99.7% im Monat, p95 unter 800 ms.
## Optionen
- A: drei Kopien des Shops, eine pro Land
- B: modularer Monolith, das Land ist eine Einstellung, Händler sind ein Modul
- C: Services vom ersten Tag an
## Entscheidung
B. Der Bestand bei den Händlern kommt asynchron an und liegt nicht im Zahlungspfad.
## Konsequenzen
- gut: ein Code für die drei Länder
- schlecht: eine Änderung für ein Land geht an alle drei raus
- schlecht: die Bestandskopie kann Minuten hinterherhinken
## Neu bewerten, wenn
Wenn die Händler auf über 500 wachsen oder die Teams auf mehr als 3.

Der Plan. Die Spalte country (Expand), der neue Code schreibt in sie, ein Backfill der alten Bestellungen als Bulgarien, dann 1 %, 10 %, 50 % und 100 % des griechischen Verkehrs bei grünen Metriken und zuletzt das Entfernen des alten Pfads (Contract). Kroatien folgt Griechenland mit demselben Plan.

Die Risiken. Das Register aus Schritt 5, mit einer Maßnahme für den Black Friday. Der Verantwortliche jedes Risikos steht dafür gerade, und das Register wird bis zum Start alle zwei Wochen durchgesehen.

Die Prüfungen. Die Grenzen aus Schritt 6 und das Budget aus Schritt 1 als Fitness Function: ein Lasttest vor jedem Start, der fehlschlägt, wenn das p95 der Zahlung über 800 ms liegt. Wie sie in der Pipeline laufen, ist ein Thema für ZeroToOps, den DevOps-Kurs.

Was wir nicht entschieden haben. Einen eigenen Service für Händler. Der ADR sagt, wann man darauf zurückkommt: bei mehr als 500 Händlern oder mehr als 3 Teams.

Test

Teste dich

1. Warum verlässt der Bestand bei den Händlern den Zahlungspfad?
2. Die Matrix wählt B. Was prüft die Sensitivitätsanalyse?
3. Was macht einen ADR auch nach zwei Jahren nützlich?
4. Der Rollout mit 10 % zeigt 9 Fehler pro 1000, erlaubt sind 5. Was tut der Plan?
5. Wann kommt im Plan der Punkt ohne Rückweg?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·