{"id":332,"date":"2026-09-05T18:25:41","date_gmt":"2026-09-05T18:25:41","guid":{"rendered":"https:\/\/www.fabricioruch.ch\/?p=332"},"modified":"2026-09-05T18:25:42","modified_gmt":"2026-09-05T18:25:42","slug":"fehlerbehandlung-beginnt-dort-wo-der-happy-path-endet","status":"publish","type":"post","link":"https:\/\/www.fabricioruch.ch\/?p=332","title":{"rendered":"Fehlerbehandlung beginnt dort, wo der Happy Path endet"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Software l\u00e4sst sich erstaunlich leicht demonstrieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Man startet das Programm.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Verbindung funktioniert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die API antwortet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Alle Daten sind vorhanden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Jede Operation ist erfolgreich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Am Ende erscheint:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SUCCESS\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Damit ist bewiesen, dass der Happy Path funktioniert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mehr aber auch nicht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Produktionsreife beginnt bei einer anderen Frage:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Was passiert, wenn die Operation nur teilweise funktioniert?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Genau dort wird Fehlerbehandlung interessant.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht beim <code>catch<\/code>-Block.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sondern bei der Frage, welchen Zustand das System nach einem Fehler tats\u00e4chlich besitzt.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Der Happy Path ist nur ein m\u00f6glicher Ablauf<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Nehmen wir ein einfaches Tool, das Daten \u00fcber eine API ver\u00e4ndert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der ideale Ablauf sieht so aus:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Connect\n   \u2193\nDiscover\n   \u2193\nProcess\n   \u2193\nVerify\n   \u2193\nCompleted\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das l\u00e4sst sich einfach implementieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Realit\u00e4t sieht eher so aus:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Connect\n   \u251c\u2500\u2500 Authentication failed\n   \u251c\u2500\u2500 Permission denied\n   \u2514\u2500\u2500 Success\n          \u2193\nDiscover\n   \u251c\u2500\u2500 Timeout\n   \u251c\u2500\u2500 Pagination failure\n   \u2514\u2500\u2500 Success\n          \u2193\nProcess\n   \u251c\u2500\u2500 Success\n   \u251c\u2500\u2500 Partial success\n   \u251c\u2500\u2500 Rate limited\n   \u251c\u2500\u2500 Server error\n   \u2514\u2500\u2500 Connection lost\n          \u2193\nVerify\n   \u251c\u2500\u2500 Expected state reached\n   \u2514\u2500\u2500 Residual state remains\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist nicht die Ausnahme.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist der normale Zustandsraum eines verteilten Systems.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><code>try\/catch<\/code> ist noch keine Fehlerstrategie<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine typische erste Fehlerbehandlung sieht so aus:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>try {\n    Invoke-Something\n}\ncatch {\n    Write-Error $_\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist technisch sinnvoll.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber es beantwortet nur:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Ist eine Exception aufgetreten?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Es beantwortet nicht:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Was wurde bereits erfolgreich ausgef\u00fchrt?<\/li>\n\n\n\n<li>Was wurde nicht ausgef\u00fchrt?<\/li>\n\n\n\n<li>Was darf wiederholt werden?<\/li>\n\n\n\n<li>Was darf nicht wiederholt werden?<\/li>\n\n\n\n<li>Kann die Operation fortgesetzt werden?<\/li>\n\n\n\n<li>Ist der aktuelle Zustand bekannt?<\/li>\n\n\n\n<li>Muss der Benutzer eingreifen?<\/li>\n\n\n\n<li>Ist das Ergebnis verifiziert?<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Das sind die eigentlich interessanten Fragen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Fehler sind nicht alle gleich<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein wichtiger Schritt ist die Klassifikation von Fehlern.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Angenommen, eine API antwortet mit:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>429 Too Many Requests\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das bedeutet im Wesentlichen:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Nicht jetzt.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Ein:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>500 Internal Server Error\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">bedeutet eher:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Der Server konnte die Anfrage momentan nicht korrekt verarbeiten.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Ein:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>403 Forbidden\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">bedeutet dagegen:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Diese Operation ist nicht erlaubt.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Und ein:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>400 Bad Request\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">h\u00e4ufig:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Mit dieser Anfrage stimmt grunds\u00e4tzlich etwas nicht.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Fehler sollten nicht identisch behandelt werden.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Retryable und Non-Retryable Errors<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine erste sinnvolle Klassifikation lautet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Retryable\nNon-Retryable\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Typische Kandidaten f\u00fcr Retry:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>429\n500\n502\n503\n504\nTimeout\ntempor\u00e4rer Netzwerkfehler\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Typische Kandidaten gegen blindes Retry:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>400\n401\n403\n404\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nat\u00fcrlich h\u00e4ngt die genaue Semantik von der jeweiligen API ab.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der wichtige Punkt ist:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Retry ist eine fachliche Entscheidung, nicht einfach eine Reaktion auf jede Exception.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Blindes Retry kann Probleme verschlimmern<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Dieser Code wirkt robust:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>try {\n    Invoke-Operation\n}\ncatch {\n    Start-Sleep -Seconds 2\n    Invoke-Operation\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Aber was passiert bei:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>403 Forbidden\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Man wartet zwei Sekunden und stellt dieselbe verbotene Anfrage erneut.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Oder bei:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>400 Bad Request\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Anfrage wird durch Warten nicht richtiger.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Retry ohne Klassifikation erzeugt nur zus\u00e4tzliche Last und verz\u00f6gert die eigentliche Fehlermeldung.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Retry braucht Grenzen<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Auch bei retrybaren Fehlern darf die Anwendung nicht endlos weitermachen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein typisches Modell:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Attempt 1\n   \u2193 failed\n\nwait 1s\n\nAttempt 2\n   \u2193 failed\n\nwait 2s\n\nAttempt 3\n   \u2193 failed\n\nwait 4s\n\nFAILED\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist <strong>Exponential Backoff<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vereinfacht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1\n2\n4\n8\n16\n...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dadurch h\u00e4mmert der Client bei einer St\u00f6rung nicht permanent auf den Server.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><code>Retry-After<\/code> ist besser als Raten<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Bei HTTP <code>429<\/code> liefern APIs h\u00e4ufig einen Hinweis:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Retry-After: 10\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann sagt der Server explizit:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Versuche es in zehn Sekunden erneut.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">In diesem Fall sollte der Client diese Information respektieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also nicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Start-Sleep -Seconds 2\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">sondern sinngem\u00e4\u00df:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Start-Sleep -Seconds $retryAfter\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist ein kleines Detail mit gro\u00dfer Wirkung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine robuste Anwendung arbeitet mit dem Server zusammen, statt gegen ihn.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Jitter verhindert synchronisierte Retries<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Bei vielen Clients entsteht noch ein anderes Problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Angenommen, ein Dienst f\u00e4llt kurz aus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1&#8217;000 Clients erhalten gleichzeitig einen Fehler.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Alle warten exakt f\u00fcnf Sekunden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann senden alle 1&#8217;000 Clients gleichzeitig erneut.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Dienst bekommt die n\u00e4chste Lastspitze.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb erg\u00e4nzt man Backoff h\u00e4ufig um zuf\u00e4llige Abweichung:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Backoff\n+\nJitter\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Aus:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>5 Sekunden\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">wird beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>4.7 Sekunden\n5.3 Sekunden\n5.8 Sekunden\n...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr ein einzelnes kleines PowerShell-Tool ist das vielleicht nicht entscheidend.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Konzept ist trotzdem wichtig, wenn man robuste verteilte Systeme verstehen will.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Ein erfolgreicher HTTP-Request bedeutet nicht automatisch Erfolg<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Nehmen wir an:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>HTTP 200 OK\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann wissen wir zun\u00e4chst nur:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Die Anfrage wurde auf Protokollebene erfolgreich beantwortet.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Bei Batch-APIs wird es noch deutlicher.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Batch-Request kann selbst erfolgreich sein:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>HTTP 200\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">w\u00e4hrend einzelne Operationen darin fehlschlagen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Batch\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nRequest 1   200\nRequest 2   200\nRequest 3   429\nRequest 4   403\nRequest 5   200\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Batch war transporttechnisch erfolgreich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die fachliche Operation war es nur teilweise.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Partial Failure ist der Normalfall, den viele vergessen<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Stellen wir uns 20 \u00c4nderungen vor.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Ergebnis:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>18 erfolgreich\n1 tempor\u00e4r fehlgeschlagen\n1 dauerhaft fehlgeschlagen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Eine zu einfache Funktion liefert vielleicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>return $false\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist zu wenig Information.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Denn:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>18 Operationen sind bereits erledigt.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ein komplettes Retry aller 20 Elemente k\u00f6nnte nun problematisch sein.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb sollte das Ergebnis pr\u00e4ziser sein.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;pscustomobject]@{\n    SucceededIds = @(...)\n    RetryIds     = @(...)\n    FailedIds    = @(...)\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Jetzt wei\u00df die n\u00e4chste Schicht genau, was passiert ist.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Counts reichen irgendwann nicht mehr<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr eine Anzeige ist das hier praktisch:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;pscustomobject]@{\n    Succeeded = 18\n    Retrying  = 1\n    Failed    = 1\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr Resume reicht es nicht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Denn nach einem Neustart muss die Anwendung wissen:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Welche 18 Elemente waren erfolgreich?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb braucht man bei zustandsbehafteter Verarbeitung Identit\u00e4ten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SucceededIds\nFailedIds\nPendingIds\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht nur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>18\n1\n1\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist ein gutes Beispiel daf\u00fcr, wie eine neue Anforderung das Datenmodell ver\u00e4ndert.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Fehlende Antworten sind ebenfalls ein Zustand<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein subtiler Fall entsteht bei Batch-Verarbeitung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Man sendet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>20 Requests\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">und erh\u00e4lt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>19 Responses\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Was ist mit Request Nummer 20?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Er ist nicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>successful\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Aber auch nicht nachweislich:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>failed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Sein Zustand ist:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>unknown\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist wichtig.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine robuste Implementierung sollte deshalb vergleichen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Sent Request IDs\n        -\nReceived Response IDs\n        =\nMissing Response IDs\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Diese fehlenden IDs m\u00fcssen bewusst behandelt werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel erneut versucht oder als unbestimmt protokolliert werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie d\u00fcrfen nicht einfach verschwinden.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">\u201eUnknown\u201c ist ein legitimer Zustand<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Entwickler modellieren gerne:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Success\nFailure\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Verteilte Systeme besitzen aber h\u00e4ufig einen dritten Zustand:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Unknown\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Beispiel:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Client sendet eine Anfrage.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann bricht die Netzwerkverbindung ab.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hat der Server die Operation ausgef\u00fchrt?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vielleicht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vielleicht nicht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Client wei\u00df es nicht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist fundamental.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Genau hier wird Idempotenz wichtig<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Angenommen, die Operation lautet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Set isRead = true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann ist eine Wiederholung normalerweise unproblematisch:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>false \u2192 true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">und erneut:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>true \u2192 true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Endzustand bleibt gleich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist eine idempotente Operation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vereinfacht:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Dieselbe Operation mehrfach auszuf\u00fchren erzeugt denselben Endzustand wie einmalige Ausf\u00fchrung.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Nicht jede Operation ist idempotent<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Betrachten wir dagegen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Create invoice\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn die Antwort verloren geht und wir die Operation erneut ausf\u00fchren, k\u00f6nnten zwei Rechnungen entstehen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Oder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Charge credit card\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ein blindes Retry w\u00e4re offensichtlich gef\u00e4hrlich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb muss Retry immer zusammen mit der Frage betrachtet werden:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Ist diese Operation sicher wiederholbar?<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Idempotency Keys<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Viele APIs l\u00f6sen dieses Problem mit einem Idempotency Key.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Idempotency-Key:\n550e8400-e29b-41d4-a716-446655440000\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wird dieselbe Operation mit demselben Key erneut gesendet, kann der Server erkennen:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Diese Operation habe ich bereits verarbeitet.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Das verhindert doppelte Seiteneffekte.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht jede API unterst\u00fctzt das.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber das zugrunde liegende Konzept ist f\u00fcr zuverl\u00e4ssige Integrationen zentral.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Retry und Idempotenz geh\u00f6ren zusammen<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Die Regel lautet deshalb nicht:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Bei Netzwerkfehlern einfach erneut versuchen.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Sondern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Fehler\n  \u2193\nRetryable?\n  \u2193\nOperation idempotent?\n  \u2193\nZustand bekannt?\n  \u2193\nRetry sicher?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Erst dann sollte die Wiederholung stattfinden.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Resume ist Retry \u00fcber Prozessgrenzen hinweg<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Retry passiert normalerweise innerhalb desselben Programmlaufs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Resume erweitert dieselbe Idee.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Retry\n\u2192 Wiederholung innerhalb des Runs\n\nResume\n\u2192 Wiederaufnahme nach Ende des Runs\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Daf\u00fcr muss Zustand persistiert werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel in einem Checkpoint.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Was ist ein Checkpoint?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Checkpoint speichert gen\u00fcgend Informationen, um nach einem Abbruch sinnvoll weiterarbeiten zu k\u00f6nnen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"runId\": \"12345\",\n  \"startedAt\": \"2026-09-05T14:00:00Z\",\n  \"succeededIds\": &#91;\n    \"A\",\n    \"B\",\n    \"C\"\n  ]\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Beim n\u00e4chsten Start kann die Anwendung erkennen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>A \u2192 bereits erledigt\nB \u2192 bereits erledigt\nC \u2192 bereits erledigt\nD \u2192 noch offen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Sie muss nicht wieder bei null beginnen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Ein Checkpoint ist keine Logdatei<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist eine wichtige Unterscheidung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Log beantwortet:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Was ist passiert?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Checkpoint beantwortet:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Was muss ich wissen, um korrekt weiterzumachen?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Das sind unterschiedliche Anforderungen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Log kann beispielsweise enthalten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>14:32:01 Processing A\n14:32:02 Processing B\n14:32:03 Retry B\n14:32:05 Processing C\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Checkpoint braucht vielleicht nur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"succeededIds\": &#91;\"A\", \"B\", \"C\"]\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Datenstrukturen sollten deshalb nicht verwechselt werden.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">\u201eProcessed\u201c ist ein gef\u00e4hrliches Wort<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Nehmen wir an, der Checkpoint speichert:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"processedIds\": &#91;\n    \"A\",\n    \"B\"\n  ]\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Was bedeutet <code>processed<\/code>?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr A:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Operation erfolgreich.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr B:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Operation versucht, aber fehlgeschlagen.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn beide als \u201eprocessed\u201c gespeichert werden, entsteht beim Resume ein schwerer Fehler.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">B wird \u00fcbersprungen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Obwohl die gew\u00fcnschte \u00c4nderung nie erfolgreich war.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Pr\u00e4zise Sprache verhindert Fehler<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Besser:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>AttemptedIds\nSucceededIds\nFailedIds\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Begriffe haben klare Semantik.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr Resume ist meistens entscheidend:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SucceededIds\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Denn nur diese Elemente d\u00fcrfen sicher als erledigt betrachtet werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist kein Naming-Detail.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist Teil der Korrektheit.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Checkpoints brauchen eine Commit-Semantik<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Man kann das gedanklich \u00e4hnlich wie eine kleine Transaktion betrachten.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Operation versuchen\n        \u2193\nErfolg best\u00e4tigt?\n        \u2193\nJa\n        \u2193\nCheckpoint aktualisieren\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Operation versuchen\n        \u2193\nCheckpoint aktualisieren\n        \u2193\nsp\u00e4ter herausfinden, ob es funktioniert hat\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Checkpoint sollte den <strong>best\u00e4tigten Zustand<\/strong> repr\u00e4sentieren.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Wann sollte ein Checkpoint geschrieben werden?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Hier entsteht ein Trade-off.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Angenommen, ein Folder enth\u00e4lt 10&#8217;000 Elemente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Variante A:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>10'000 verarbeiten\n\u2193\nCheckpoint schreiben\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn bei Element 9&#8217;999 der Prozess abst\u00fcrzt, fehlen viele erfolgreiche Operationen im Checkpoint.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Variante B:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jedes Element\n\u2193\nCheckpoint schreiben\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist sicherer, erzeugt aber sehr viele Schreiboperationen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine pragmatische L\u00f6sung:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Batch verarbeiten\n\u2193\nerfolgreiche IDs \u00fcbernehmen\n\u2193\nCheckpoint schreiben\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Damit entspricht die Persistenz der nat\u00fcrlichen Arbeitseinheit.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Checkpoint-Schreiben selbst kann scheitern<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Auch das wird gerne vergessen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Verarbeitung funktioniert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann kann der Checkpoint nicht gespeichert werden:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Disk full\nPermission denied\nFile locked\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Was nun?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die externe Operation ist bereits passiert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der lokale Zustand wurde aber nicht persistiert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Genau deshalb ist Idempotenz wieder wichtig.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beim Resume k\u00f6nnten bereits erfolgreiche Operationen erneut entdeckt werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn deren Wiederholung sicher ist, bleibt das System beherrschbar.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Checkpoints sollten atomar geschrieben werden<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein weiteres Problem:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Programm schreibt eine JSON-Datei.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mitten w\u00e4hrend des Schreibens st\u00fcrzt der Prozess ab.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zur\u00fcck bleibt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"succeededIds\": &#91;\n    \"A\",\n    \"B\",\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Checkpoint ist kaputt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine typische Strategie:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>checkpoint.tmp schreiben\n        \u2193\nvollst\u00e4ndig erfolgreich?\n        \u2193\nrename \/ replace\n        \u2193\ncheckpoint.json\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das reduziert das Risiko eines teilweise geschriebenen Zustands.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Ein Checkpoint braucht m\u00f6glicherweise eine Version<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Heute sieht der Checkpoint so aus:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"succeededIds\": &#91;]\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In sechs Monaten vielleicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"version\": 2,\n  \"runId\": \"...\",\n  \"scope\": \"...\",\n  \"succeededIds\": &#91;]\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ohne Versionierung kann ein neues Programm einen alten Checkpoint falsch interpretieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb kann selbst bei kleinen Tools sinnvoll sein:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"schemaVersion\": 1\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das kostet fast nichts und schafft eine klare Evolutionsgrenze.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Resume muss den Kontext validieren<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein gespeicherter Checkpoint darf nicht blind verwendet werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Angenommen, der Checkpoint stammt von:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Mailbox A\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">und der Benutzer startet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Mailbox B\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn nur Message IDs gespeichert sind, k\u00f6nnte das gef\u00e4hrlich werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb sollte ein Checkpoint seinen Kontext kennen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Target\nUser\nOperation\nRun\nSchema Version\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Beim Resume kann gepr\u00fcft werden:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Passt dieser Checkpoint \u00fcberhaupt zu diesem Lauf?<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Ein Checkpoint ist kein Ersatz f\u00fcr Idempotenz<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Es w\u00e4re verf\u00fchrerisch zu sagen:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Wir speichern alle erfolgreichen IDs, also brauchen wir keine idempotenten Operationen.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Das stimmt nicht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zwischen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Operation erfolgreich\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">und:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Checkpoint erfolgreich gespeichert\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">existiert immer ein kleines Zeitfenster.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Prozess kann genau dort abst\u00fcrzen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann wurde die Operation durchgef\u00fchrt, aber nicht persistiert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beim n\u00e4chsten Start wird sie m\u00f6glicherweise erneut ausgef\u00fchrt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb ist die Kombination stark:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Idempotente Operation\n+\nCheckpoint\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht entweder\/oder.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Verification ist eine eigene Phase<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Nach erfolgreicher Verarbeitung kommt eine weitere Frage:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Ist der gew\u00fcnschte Endzustand tats\u00e4chlich erreicht?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Das sollte nicht automatisch aus den Operationen abgeleitet werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Angenommen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>56 \u00c4nderungen angefordert\n56 erfolgreiche Responses\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist gut.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber der eigentliche Auftrag k\u00f6nnte lauten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Es sollen keine ungelesenen Nachrichten mehr vorhanden sein.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann ist die relevante Pr\u00fcfung:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Remaining unread = ?\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Operationserfolg und Zustandserfolg<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Unterscheidung ist fundamental.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Operation Success\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">bedeutet:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Die angeforderten Operationen wurden erfolgreich ausgef\u00fchrt.<\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-code\"><code>State Success\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">bedeutet:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Das System befindet sich jetzt im gew\u00fcnschten Zustand.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Beides kann auseinanderfallen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Beispiel<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Wir entdecken:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>56 ungelesene Nachrichten\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wir \u00e4ndern alle 56.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die API meldet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>56 erfolgreich\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">W\u00e4hrenddessen kommt eine neue Nachricht an.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Post-Verification:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1 ungelesene Nachricht\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">War die Verarbeitung erfolgreich?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ja.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ist der gew\u00fcnschte globale Zustand:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>0 unread\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">erreicht?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nein.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beide Aussagen k\u00f6nnen gleichzeitig wahr sein.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Verification braucht eine definierte Wahrheit<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Auch hier ist wichtig, <strong>was<\/strong> verifiziert wird.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Angenommen, eine API liefert Folder-Metadaten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>unreadItemCount = 10\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Eine konkrete Query:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>isRead = false\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">findet aber nur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>8\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn wir bereits wissen, dass Metadaten und tats\u00e4chliche Query voneinander abweichen k\u00f6nnen, sollten wir bei der Post-Verification nicht pl\u00f6tzlich die Metadaten zur endg\u00fcltigen Wahrheit erkl\u00e4ren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Verifikation sollte m\u00f6glichst dieselbe fachliche Definition verwenden wie die urspr\u00fcngliche Auswahl.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Pre-flight:\nisRead = false\n\nPost-flight:\nisRead = false\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das macht die Semantik konsistent.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Verification ist nicht dasselbe wie Retry<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn nach der Verarbeitung noch zwei Elemente \u00fcbrig sind, gibt es verschiedene M\u00f6glichkeiten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vielleicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>zwei neue Elemente sind entstanden\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Vielleicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>zwei urspr\u00fcngliche Operationen sind fehlgeschlagen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Vielleicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>eventual consistency\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Vielleicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>falsche Query\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb sollte Verification zun\u00e4chst feststellen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht automatisch reparieren.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Observe\n\u2193\nClassify\n\u2193\nDecide\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Observe\n\u2193\nblind retry everything\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Eventual Consistency<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Verteilte Systeme k\u00f6nnen \u00c4nderungen verz\u00f6gert sichtbar machen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine Mutation ist erfolgreich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Direkt danach liefert eine andere Query noch den alten Zustand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Kurz sp\u00e4ter stimmt alles.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das nennt man <strong>Eventual Consistency<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb kann eine Verification-Strategie beispielsweise lauten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Verify\n\u2193\nResidual state?\n\u2193\nkurz warten\n\u2193\nVerify again\n\u2193\nfinal result\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Auch hier sollten Retries begrenzt sein.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Erfolg braucht mehrere Ebenen<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr ein robustes Tool kann ein Ergebnis deshalb differenzierter aussehen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;pscustomobject]@{\n    Attempted  = 56\n    Succeeded  = 55\n    Failed     = 1\n    Verified   = $false\n    Remaining  = 1\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist viel aussagekr\u00e4ftiger als:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$success = $false\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><code>SUCCESS<\/code> ist eine Behauptung<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist besonders f\u00fcr TUIs relevant.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein gr\u00fcner Abschluss-Screen mit:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SUCCESS\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ist keine Dekoration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Er ist eine fachliche Behauptung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Anwendung sollte deshalb definieren, wann dieser Zustand erreicht ist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Completed\n+\nFailed = 0\n+\nVerification completed\n+\nRemaining = 0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Erst dann:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>VERIFIED SUCCESS\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Es gibt mehr als Success und Failure<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein ausgereiftes Tool kann Ergebnisse besitzen wie:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Completed\nCompletedWithFailures\nVerificationFailed\nCancelled\nInterrupted\nResumeAvailable\nFailed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht jede Operation passt sinnvoll in:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>true \/ false\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Gerade Batch-Systeme profitieren von pr\u00e4ziseren Endzust\u00e4nden.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Fehler sollten den n\u00e4chsten Schritt erkl\u00e4ren<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine Fehlermeldung wie:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>HTTP 503\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ist technisch korrekt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr den Benutzer ist hilfreicher:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PROCESSING INTERRUPTED\n\n117 \/ 184 items completed.\nThe service is temporarily unavailable.\n\nCheckpoint saved.\nThe operation can be resumed.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das beantwortet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Was ist passiert?\nWas wurde bereits erledigt?\nIst der Zustand sicher?\nWas kann ich jetzt tun?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist gute Fehlerbehandlung.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Fehlerbehandlung ist Teil der UX<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Das wird oft untersch\u00e4tzt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">UX wird gerne mit:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Farben<\/li>\n\n\n\n<li>Buttons<\/li>\n\n\n\n<li>Layout<\/li>\n\n\n\n<li>Animation<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">verbunden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bei technischen Werkzeugen entsteht gute UX aber besonders in Fehlerf\u00e4llen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein gutes Tool sagt nicht nur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Something went wrong.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Es sagt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>was\nwo\nwie weit\nwelcher Zustand\nob Retry sinnvoll ist\nob Resume m\u00f6glich ist\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das schafft Vertrauen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Logging und Benutzeroberfl\u00e4che haben unterschiedliche Aufgaben<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Log darf technisch sein:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>2026-09-05T14:32:01Z\nPATCH \/messages\/abc\nHTTP 503\nattempt=3\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die TUI sollte eher sagen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SERVICE TEMPORARILY UNAVAILABLE\n\nRetry 3 \/ 5\nContinuing in 8 seconds\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Beides beschreibt dasselbe Ereignis.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber f\u00fcr unterschiedliche Zielgruppen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Quiet Mode braucht ebenfalls saubere Fehler<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Maschinenlesbare Ausgabe sollte Fehler strukturiert liefern.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ERROR!!!\nSomething failed.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">sondern beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"status\": \"partial_failure\",\n  \"processed\": 117,\n  \"failed\": 1,\n  \"remaining\": 67,\n  \"resumeAvailable\": true\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Damit kann ein anderes Programm reagieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fehlerbehandlung wird dadurch Teil des \u00f6ffentlichen Schnittstellenvertrags.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Exit Codes geh\u00f6ren dazu<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr CLI-Tools sind Exit Codes ebenfalls wichtig.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>0\n\u2192 erfolgreich\n\n1\n\u2192 allgemeiner Fehler\n\n2\n\u2192 teilweise Verarbeitung\n\n3\n\u2192 Verifikation fehlgeschlagen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die konkrete Konvention ist projektspezifisch.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Entscheidend ist:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein automatisierter Aufrufer sollte nicht den Bildschirmtext analysieren m\u00fcssen, um herauszufinden, ob etwas funktioniert hat.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Fehlerbehandlung beginnt beim Design<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Die wichtigste Erkenntnis ist deshalb:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Robustheit kann nicht am Ende mit ein paar <code>catch<\/code>-Bl\u00f6cken hinzugef\u00fcgt werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bereits das Datenmodell muss Fehlerf\u00e4lle ausdr\u00fccken k\u00f6nnen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn eine Funktion nur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">oder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$false\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">zur\u00fcckgeben kann, kann sie keinen Partial Failure beschreiben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn ein Checkpoint nur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ProcessedIds\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">kennt, kann er Success und Failure nicht unterscheiden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn die TUI nur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Processing\nCompleted\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">kennt, kann sie Retry und Resume nicht sinnvoll darstellen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fehlerbehandlung beeinflusst deshalb:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Data Model\nState Model\nAPI Layer\nPersistence\nTUI\nMachine Output\nTests\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Sie ist Architektur.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Fehlerf\u00e4lle sollten zuerst modelliert werden<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Bei kritischer Logik lohnt sich eine kleine \u00dcbung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vor der Implementierung eine Tabelle erstellen:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Situation<\/th><th>Retry?<\/th><th>Zustand bekannt?<\/th><th>Resume?<\/th><\/tr><\/thead><tbody><tr><td>200<\/td><td>nein<\/td><td>ja<\/td><td>\u2013<\/td><\/tr><tr><td>429<\/td><td>ja<\/td><td>ja<\/td><td>ja<\/td><\/tr><tr><td>503<\/td><td>ja<\/td><td>meist<\/td><td>ja<\/td><\/tr><tr><td>403<\/td><td>nein<\/td><td>ja<\/td><td>nach Korrektur<\/td><\/tr><tr><td>Timeout nach Request<\/td><td>eventuell<\/td><td>nein<\/td><td>abh\u00e4ngig von Idempotenz<\/td><\/tr><tr><td>Partial Batch<\/td><td>teilweise<\/td><td>ja<\/td><td>ja<\/td><\/tr><tr><td>Prozessabbruch<\/td><td>nein<\/td><td>teilweise<\/td><td>ja<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Pl\u00f6tzlich werden Architekturfragen sichtbar, bevor sie Bugs werden.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Fehlerpfade brauchen Tests<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Der Happy Path sollte nat\u00fcrlich getestet werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber bei einem zuverl\u00e4ssigen Tool sind diese Tests mindestens genauso interessant:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>429\n\u2192 Retry\n\u2192 Success\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>500\n\u2192 Retry\n\u2192 Retry\n\u2192 Exhausted\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>Batch:\n19 Success\n1 Failure\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>20 Requests\n19 Responses\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>Operation erfolgreich\nCheckpoint-Schreiben schl\u00e4gt fehl\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>Resume\n\u2192 Failed ID wird erneut verarbeitet\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>Verification\n\u2192 Remaining &gt; 0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das sind die F\u00e4lle, die Architekturfehler sichtbar machen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Chaos im Kleinen<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Man braucht daf\u00fcr kein gro\u00dfes Chaos-Engineering-System.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Schon gezielte simulierte Fehler sind wertvoll.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Was passiert, wenn Request Nummer 7 fehlschl\u00e4gt?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Oder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Was passiert, wenn nach Batch 3 der Prozess beendet wird?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Oder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Was passiert, wenn die Verification andere Werte findet?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Fragen testen nicht nur Code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie testen das Modell des Systems.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Der Happy Path versteckt schlechte Architektur<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist vielleicht die wichtigste Beobachtung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Viele strukturelle Fehler sind im Happy Path unsichtbar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn alles funktioniert:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Request\n\u2193\nSuccess\n\u2193\nRequest\n\u2193\nSuccess\n\u2193\nDone\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">braucht man weder:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Retry-State<\/li>\n\n\n\n<li>Failed IDs<\/li>\n\n\n\n<li>Checkpoints<\/li>\n\n\n\n<li>Resume<\/li>\n\n\n\n<li>Verification<\/li>\n\n\n\n<li>Unknown State<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Erst Fehlerf\u00e4lle zwingen das System, seine tats\u00e4chliche Semantik offenzulegen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb sind sie so wertvoll.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Reliability ist kein einzelnes Feature<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Man kann nicht einfach sagen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Feature:\nRobust Error Handling \u2713\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Reliability entsteht aus mehreren Eigenschaften:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>klare Fehlerklassifikation\n+\nbegrenztes Retry\n+\nBackoff\n+\nIdempotenz\n+\nPartial-Failure-Modell\n+\nCheckpoints\n+\nResume\n+\nVerification\n+\npr\u00e4zise Resultate\n+\nTests\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Keine davon allein macht ein System zuverl\u00e4ssig.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zusammen ergeben sie ein belastbares Verhalten.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Ein robustes Verarbeitungsmodell<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr viele Batch-Tools l\u00e4sst sich der Ablauf ungef\u00e4hr so zusammenfassen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>DISCOVER\n   \u2193\nBUILD WORK QUEUE\n   \u2193\nPROCESS BATCH\n   \u2193\nCLASSIFY RESULTS\n   \u251c\u2500\u2500 Succeeded\n   \u251c\u2500\u2500 Retryable\n   \u251c\u2500\u2500 Failed\n   \u2514\u2500\u2500 Unknown\n   \u2193\nRETRY SAFE OPERATIONS\n   \u2193\nCOMMIT CHECKPOINT\n   \u2193\nNEXT BATCH\n   \u2193\nVERIFY FINAL STATE\n   \u2193\nREPORT RESULT\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist deutlich mehr als:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>foreach item\n    try\n        process\n    catch\n        retry\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Aber genau diese zus\u00e4tzlichen Schritte machen den Unterschied.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Das Interessante daran: Es gilt weit \u00fcber PowerShell hinaus<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Konzepte sind nicht PowerShell-spezifisch.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie gelten f\u00fcr:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>REST-Clients<\/li>\n\n\n\n<li>Background Jobs<\/li>\n\n\n\n<li>Message Processing<\/li>\n\n\n\n<li>Datenmigrationen<\/li>\n\n\n\n<li>Synchronisationsdienste<\/li>\n\n\n\n<li>Importer<\/li>\n\n\n\n<li>Deployment Tools<\/li>\n\n\n\n<li>Batch-Verarbeitung<\/li>\n\n\n\n<li>Cloud-Automation<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Die konkrete Syntax \u00e4ndert sich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Fragen bleiben dieselben:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Was wurde versucht?\nWas war erfolgreich?\nWas ist fehlgeschlagen?\nWas ist unbekannt?\nWas darf wiederholt werden?\nWas wurde persistiert?\nWie kann weitergemacht werden?\nWie wissen wir, dass wir fertig sind?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wer diese Fragen beantworten kann, hat einen gro\u00dfen Teil des Reliability-Problems bereits verstanden.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Was ich bei einem neuen Tool heute zuerst definieren w\u00fcrde<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Noch bevor die aufwendige Fehlerbehandlung implementiert wird:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>1. Was ist die atomare Arbeitseinheit?<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>eine Nachricht\neine Datei\nein Datensatz\nein API-Request\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>2. Was bedeutet Erfolg?<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Request angenommen?\nMutation best\u00e4tigt?\nEndzustand verifiziert?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>3. Ist die Operation idempotent?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>4. Welche Fehler sind retrybar?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>5. Welche Zust\u00e4nde k\u00f6nnen nach einem Abbruch existieren?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>6. Welche Informationen braucht Resume?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>7. Was muss ein Checkpoint speichern?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>8. Wie wird der Endzustand verifiziert?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn diese Fragen klar sind, wird die Implementierung deutlich einfacher.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Fazit<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Der Happy Path beweist, dass ein System unter idealen Bedingungen funktioniert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Produktionsreife zeigt sich unter nicht idealen Bedingungen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht die Frage:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Funktioniert die API?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">ist dann entscheidend.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sondern:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Wissen wir nach einem Fehler noch, was tats\u00e4chlich passiert ist?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Ein robustes System unterscheidet deshalb zwischen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Attempted\nSucceeded\nFailed\nRetryable\nUnknown\nVerified\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Es wiederholt Operationen nicht blind.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Es versteht Idempotenz.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Es persistiert best\u00e4tigten Fortschritt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Es kann nach einem Abbruch weiterarbeiten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Und es pr\u00fcft am Ende nicht nur, ob Requests erfolgreich waren, sondern ob der gew\u00fcnschte Zustand tats\u00e4chlich erreicht wurde.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die wichtigste Erkenntnis dieser Serie lautet deshalb:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Fehlerbehandlung bedeutet nicht, Exceptions abzufangen. Sie bedeutet, auch nach einem teilweise gescheiterten Ablauf noch eindeutig zu wissen, in welchem Zustand sich das System befindet und wie es sicher weitergehen kann.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Genau dort endet der Happy Path.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Und dort beginnt Reliability.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Software l\u00e4sst sich erstaunlich leicht demonstrieren. Man startet das Programm. Die Verbindung funktioniert. Die API antwortet. Alle Daten sind vorhanden. Jede Operation ist erfolgreich. Am Ende erscheint: Damit ist bewiesen, dass der Happy Path funktioniert. Mehr aber auch nicht. Produktionsreife beginnt bei einer anderen Frage: Was passiert, wenn die Operation nur teilweise funktioniert? Genau dort [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":338,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[60,27,25,59],"tags":[],"class_list":["post-332","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-automation","category-programming-principles","category-software-engineering","category-system-engineering"],"_links":{"self":[{"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/posts\/332","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=332"}],"version-history":[{"count":1,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/posts\/332\/revisions"}],"predecessor-version":[{"id":333,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/posts\/332\/revisions\/333"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/media\/338"}],"wp:attachment":[{"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=332"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=332"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=332"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}