{"id":326,"date":"2026-09-05T18:21:37","date_gmt":"2026-09-05T18:21:37","guid":{"rendered":"https:\/\/www.fabricioruch.ch\/?p=326"},"modified":"2026-09-05T18:21:37","modified_gmt":"2026-09-05T18:21:37","slug":"erst-die-zustandsmaschine-dann-der-spinner","status":"publish","type":"post","link":"https:\/\/www.fabricioruch.ch\/?p=326","title":{"rendered":"Erst die Zustandsmaschine, dann der Spinner"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Bei Terminalanwendungen beginnt die Arbeit oft an der falschen Stelle.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Man hat eine Idee f\u00fcr ein Tool, baut die eigentliche Funktion und m\u00f6chte dann schnell etwas Feedback im Terminal anzeigen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also kommt zuerst:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Connecting...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Processing...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann ein Spinner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann Farben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann eine Progressbar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann ein Banner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann \u00dcberg\u00e4nge.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann ein finaler Erfolgs-Screen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Ergebnis sieht irgendwann gut aus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Und trotzdem f\u00fchlt sich der Code merkw\u00fcrdig fragil an.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Grund liegt h\u00e4ufig darin, dass die Darstellung gebaut wurde, bevor \u00fcberhaupt klar war, <strong>welche Zust\u00e4nde die Anwendung besitzt<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist die falsche Reihenfolge.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine gute TUI beginnt nicht mit dem Spinner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie beginnt mit der Zustandsmaschine.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Ein Spinner ist nur ein Symptom eines Zustands<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Spinner zeigt nicht einfach \u201eAnimation\u201c.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Er transportiert eine Information:<\/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 Anwendung arbeitet gerade, aber der Fortschritt ist nicht exakt messbar.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist bereits ein Zustand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn dort steht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u2839 Connecting...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">dann steckt dahinter semantisch:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>State = Connecting\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn dort steht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2591\u2591\u2591 72%\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">dann steckt dahinter eher:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>State = Processing\nProgress = 72%\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Und wenn sp\u00e4ter erscheint:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>VERIFICATION SUCCESSFUL\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">dann ist auch das kein dekorativer Screen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Es ist die Darstellung eines anderen Zustands.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die TUI ist deshalb nicht die Quelle der Wahrheit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie ist nur die Visualisierung eines bereits existierenden Zustandsmodells.<\/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 \u00fcberhaupt eine Zustandsmaschine?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine Zustandsmaschine beschreibt, in welchem Zustand sich ein System befindet und welche \u00dcberg\u00e4nge zwischen diesen Zust\u00e4nden erlaubt sind.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr ein einfaches Werkzeug k\u00f6nnte das so aussehen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>START\n  \u2193\nCONNECTING\n  \u2193\nPROCESSING\n  \u2193\nCOMPLETED\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das klingt zun\u00e4chst fast trivial.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sobald reale Fehlerf\u00e4lle dazukommen, wird es interessanter:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>START\n  \u2193\nCONNECTING\n  \u251c\u2500\u2500\u2192 FAILED\n  \u2193\nREADY\n  \u2193\nPROCESSING\n  \u251c\u2500\u2500\u2192 RETRYING\n  \u2502       \u2193\n  \u2502    PROCESSING\n  \u2502\n  \u251c\u2500\u2500\u2192 FAILED\n  \u2193\nVERIFYING\n  \u251c\u2500\u2500\u2192 FAILED\n  \u2193\nCOMPLETED\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Pl\u00f6tzlich wird sichtbar:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Programm besteht nicht nur aus einer Reihe von Funktionen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Es bewegt sich durch definierte Zust\u00e4nde.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Implizite Zust\u00e4nde entstehen sowieso<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Viele Skripte besitzen bereits Zust\u00e4nde, obwohl sie nie so genannt werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$connected = $false\n$processing = $false\n$completed = $false\n$failed = $false\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann sp\u00e4ter:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$connected = $true\n$processing = $true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">und irgendwann:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$processing = $false\n$completed = $true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Damit wurde bereits eine Zustandsmaschine gebaut.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nur eine schlechte.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Denn theoretisch w\u00e4ren jetzt auch Kombinationen m\u00f6glich wie:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>connected = false\nprocessing = true\ncompleted = true\nfailed = true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Was soll das bedeuten?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mit mehreren Booleans w\u00e4chst die Zahl m\u00f6glicher Kombinationen extrem schnell.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vier Boolean-Werte haben bereits:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>2\u2074 = 16\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">m\u00f6gliche Kombinationen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber vermutlich sind nur wenige davon tats\u00e4chlich g\u00fcltig.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein expliziter State ist deshalb besser:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$state = 'Processing'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Statt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$isConnected = $true\n$isProcessing = $true\n$isCompleted = $false\n$isFailed = $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\">Ein Zustand sollte eindeutig sein<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine Anwendung sollte zu einem Zeitpunkt m\u00f6glichst klar beantworten k\u00f6nnen:<\/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 mache ich gerade?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Initializing\nConnecting\nAuditing\nWaitingForConfirmation\nProcessing\nRetrying\nVerifying\nCompleted\nFailed\nCancelled\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Zust\u00e4nde schlie\u00dfen sich weitgehend gegenseitig aus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das reduziert Ambiguit\u00e4t.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die TUI kann darauf direkt reagieren.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>switch ($State.Phase) {\n    'Connecting' {\n        Show-ConnectingScreen\n    }\n\n    'Processing' {\n        Show-ProcessingScreen\n    }\n\n    'Verifying' {\n        Show-VerificationScreen\n    }\n\n    'Completed' {\n        Show-CompletedScreen\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Damit wird die Oberfl\u00e4che deterministisch.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Der Zustand ist mehr als ein Name<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Nur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$State = 'Processing'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">reicht meist nicht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Zustand braucht Kontext.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$state = &#91;pscustomobject]@{\n    Phase         = 'Processing'\n    CurrentFolder = 'Archive \/ 2025'\n    Processed     = 116\n    Total         = 184\n    Failed        = 0\n    StartedAt     = $startedAt\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die TUI kann daraus alles ableiten, was sie braucht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PROCESSING\n\nArchive \/ 2025\n\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 63%\n\n116 processed\n 68 remaining\n  0 failed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Renderer muss nichts \u00fcber die Gesch\u00e4ftslogik wissen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Er bekommt nur State.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">State und Event sind nicht dasselbe<\/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 Event beschreibt:<\/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\">Etwas ist gerade passiert.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Ein State beschreibt:<\/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\">So sieht die Situation jetzt aus.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Event:\nMessageProcessed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Danach kann der Zustand sein:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Processed = 117\nRemaining = 67\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Oder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Event:\nRetryScheduled\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Danach:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Phase = Retrying\nRetryCount = 2\nRetryAfter = 5\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Events sind punktuell.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">State ist persistent.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist besonders f\u00fcr TUIs wichtig, weil ein Screen jederzeit aus dem aktuellen Zustand neu aufgebaut werden k\u00f6nnen sollte.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Gute TUIs rendern State, nicht Event-Historie<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Schlecht w\u00e4re:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Connected\nFolder found\nMessage processed\nMessage processed\nMessage processed\nRetry\nMessage processed\n...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist Log-Ausgabe.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine TUI sollte eher den aktuellen Zustand verdichten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PROCESSING\n\nCurrent folder\nArchive \/ 2025\n\nProcessed   117\nRemaining    67\nRetries       1\nFailed        0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Logs beantworten:<\/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 alles passiert?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Eine TUI 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\">Wo stehen wir jetzt?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Trennung ist zentral.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Ein Screen ist eine Projektion des Zustands<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Screen sollte nicht selbst entscheiden, wie die Anwendung weiterl\u00e4uft.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Er stellt nur State dar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>State\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nPhase = Processing\nProcessed = 117\nTotal = 184\nFailed = 0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">wird projiziert auf:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PROCESSING\n\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591 64%\n\n117 \/ 184\n\n0 failed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Man k\u00f6nnte denselben State aber auch als JSON darstellen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"phase\": \"processing\",\n  \"processed\": 117,\n  \"total\": 184,\n  \"failed\": 0\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Oder als Log:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>2026-09-05T14:32:10Z processed=117 total=184 failed=0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das zeigt den Vorteil:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>State\n  \u251c\u2500\u2500 TUI\n  \u251c\u2500\u2500 JSON\n  \u2514\u2500\u2500 Log\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Engine bleibt dieselbe.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Zustands\u00fcberg\u00e4nge m\u00fcssen bewusst definiert sein<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn Zust\u00e4nde explizit werden, sollte auch klar sein, welche \u00dcberg\u00e4nge erlaubt sind.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Initializing\n    \u2193\nConnecting\n    \u2193\nAuditing\n    \u2193\nWaitingForConfirmation\n    \u2193\nProcessing\n    \u2193\nVerifying\n    \u2193\nCompleted\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ein \u00dcbergang:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Completed \u2192 Processing\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">w\u00e4re dagegen wahrscheinlich ung\u00fcltig.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Oder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Connecting \u2192 Verifying\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ergibt keinen Sinn.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Regeln m\u00fcssen nicht zwingend in einem gro\u00dfen State-Machine-Framework landen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Oft reicht schon eine klare Orchestrierung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber man sollte sie bewusst besitzen.<\/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 Zust\u00e4nde, nicht nur Exceptions<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine Exception ist ein technisches Ereignis.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Fehlerzustand ist eine fachliche Situation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>catch {\n    $state.Phase = 'Failed'\n    $state.Error = $_.Exception.Message\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Screen kann dann darstellen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PROCESSING FAILED\n\nCompleted\n117 \/ 184\n\nReason\nHTTP 503 Service Unavailable\n\nResume possible\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist wesentlich besser als:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Exception calling Invoke-MgGraphRequest...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Exception bleibt intern wichtig.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die TUI zeigt aber den Zustand, den sie verursacht hat.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Retry ist ein eigener Zustand<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Retry wird oft nur als Schleife implementiert:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>catch {\n    Start-Sleep -Seconds 5\n    retry\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr die Anwendung ist aber etwas Relevantes passiert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie wartet bewusst.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also kann das ein Zustand sein:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>RETRYING\n\nServer throttled the request.\n\nRetry 2 \/ 5\nContinuing in 4 seconds\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist f\u00fcr den Benutzer wesentlich verst\u00e4ndlicher als ein eingefrorener Spinner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Auch hier gilt:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Renderer braucht keine Kenntnis \u00fcber HTTP 429.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Er bekommt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$state = &#91;pscustomobject]@{\n    Phase       = 'Retrying'\n    RetryNumber = 2\n    RetryMax    = 5\n    RetryAfter  = 4\n    Reason      = 'Rate limited'\n}\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\">Warten ist ebenfalls ein Zustand<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein besonders wichtiger Zustand ist:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>WaitingForConfirmation\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Denn die Anwendung arbeitet hier nicht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie wartet auf den Benutzer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das sollte sich visuell unterscheiden von:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Processing\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Spinner w\u00e4re hier sogar falsch.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Denn:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u2839 Waiting for confirmation...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">suggeriert Aktivit\u00e4t.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tats\u00e4chlich ist die Anwendung blockiert und braucht eine Entscheidung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Besser:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CONFIRMATION REQUIRED\n\n184 items will be modified.\n\nType FILE to continue.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Auch kleine UI-Details werden klarer, sobald der Zustand sauber modelliert ist.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Ein Spinner geh\u00f6rt nur zu bestimmten Zust\u00e4nden<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn die Zust\u00e4nde definiert sind, l\u00e4sst sich auch Animation sinnvoll zuordnen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Zustand<\/th><th>Sinnvolle Darstellung<\/th><\/tr><\/thead><tbody><tr><td>Connecting<\/td><td>Spinner<\/td><\/tr><tr><td>Auditing<\/td><td>Spinner oder Counter<\/td><\/tr><tr><td>WaitingForConfirmation<\/td><td>keine Animation<\/td><\/tr><tr><td>Processing<\/td><td>Progressbar<\/td><\/tr><tr><td>Retrying<\/td><td>Countdown \/ Spinner<\/td><\/tr><tr><td>Verifying<\/td><td>Spinner<\/td><\/tr><tr><td>Completed<\/td><td>statischer Screen<\/td><\/tr><tr><td>Failed<\/td><td>statischer Screen<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Jetzt bekommt Animation eine Semantik.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie ist nicht mehr Dekoration.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Fortschritt braucht eine Definition<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Sobald ein State <code>Progress<\/code> enth\u00e4lt, muss klar sein, was er bedeutet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$state.Processed = 117\n$state.Total = 184\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>117 \/ 184 = 63.6%\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Aber was bedeutet <code>Processed<\/code>?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">M\u00f6gliche Interpretationen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Requests gesendet\nResponses erhalten\nerfolgreiche Operationen\nElemente verifiziert\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Dinge sind nicht gleich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Zustand sollte deshalb m\u00f6glichst ehrlich modelliert werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$state = &#91;pscustomobject]@{\n    Attempted = 120\n    Succeeded = 117\n    Failed    = 1\n    Retrying  = 2\n    Total     = 184\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann kann die TUI klar sagen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>117 successful\n1 failed\n2 retrying\n64 remaining\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist wesentlich informativer als eine vage Zahl.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Der Zustand sollte die Wahrheit des Systems abbilden<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist ein wichtiger Grundsatz:<\/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 TUI darf nicht mehr wissen als der State.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn der State nur sagt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>184 requests completed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">darf der Screen nicht behaupten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ALL ITEMS VERIFIED\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Daf\u00fcr braucht es einen separaten Verifikationszustand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Phase = Verifying\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">und danach:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Phase = Completed\nRemaining = 0\nVerified = true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann ist ein gr\u00fcnes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>VERIFIED\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">auch tats\u00e4chlich gerechtfertigt.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Zustandsmaschinen reduzieren UI-Bugs<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Viele klassische TUI-Probleme entstehen dadurch, dass Screens zu viel Eigenlogik besitzen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Wenn $processing und nicht $verifying und $failed -eq 0,\ndann gr\u00fcne Progressbar,\nau\u00dfer $retrying...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Mit expliziten Zust\u00e4nden wird daraus:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Processing\nRetrying\nVerifying\nFailed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das vereinfacht Rendering massiv.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Statt dutzender Bedingungen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if (...) {\n    if (...) {\n        if (...) {\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">gibt es:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>switch ($state.Phase) {\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist einfacher zu verstehen und zu testen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">State Explosion vermeiden<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Nat\u00fcrlich kann man es auch \u00fcbertreiben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Man k\u00f6nnte f\u00fcr jedes Detail einen Zustand erfinden:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ConnectingToApi\nConnectingToMailbox\nLoadingRootFolders\nLoadingHiddenFolders\nLoadingChildFolders\nBuildingQueue\n...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das wird schnell un\u00fcbersichtlich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine gute Faustregel:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Zustand sollte relevant sein, wenn er mindestens eine dieser Eigenschaften besitzt:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>andere Benutzerinformation<\/li>\n\n\n\n<li>anderes erlaubtes Verhalten<\/li>\n\n\n\n<li>anderer Fehlerpfad<\/li>\n\n\n\n<li>andere Interaktion<\/li>\n\n\n\n<li>andere Renderinglogik<\/li>\n\n\n\n<li>relevante Wiederaufnahmegrenze<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht jeder interne Funktionsaufruf braucht einen State.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Hauptzustand und Unterzustand<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Bei komplexeren Tools kann eine Hierarchie helfen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Processing\n\u251c\u2500\u2500 LoadingBatch\n\u251c\u2500\u2500 SendingBatch\n\u251c\u2500\u2500 WaitingForRetry\n\u2514\u2500\u2500 UpdatingCheckpoint\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die TUI muss vielleicht nur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Processing\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">darstellen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Intern kann trotzdem ein detaillierterer Substate existieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das verhindert, dass das UI-Modell mit Implementierungsdetails \u00fcberladen wird.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">State sollte m\u00f6glichst zentral verwaltet werden<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein h\u00e4ufiger Fehler ist, State \u00fcber das ganze Programm zu verteilen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$Script:Processed\n$Script:Failed\n$Script:CurrentFolder\n$Script:RetryCount\n$Script:IsVerifying\n$Script:StartedAt\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das funktioniert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber irgendwann ist schwer nachvollziehbar, welche Funktion welche Variable ver\u00e4ndern darf.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Besser ist ein zusammenh\u00e4ngendes Objekt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$state = &#91;pscustomobject]@{\n    Phase         = 'Processing'\n    CurrentFolder = $null\n    Processed     = 0\n    Failed        = 0\n    Retried       = 0\n    Total         = 0\n    StartedAt     = Get-Date\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann wird klar:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Application State\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ist eine eigene Einheit.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Noch besser: kontrollierte Zustands\u00e4nderungen<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Bei gr\u00f6\u00dferen Tools kann man \u00c4nderungen \u00fcber Funktionen laufen lassen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Set-FBState -Phase Processing\nUpdate-FBProgress -Processed 117 -Failed 0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">oder \u00fcber Events:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Publish-FBEvent -Type ItemProcessed -Data $item\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Vorteil:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zustands\u00e4nderungen sind zentral kontrollierbar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Man kann dabei:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>validieren<\/li>\n\n\n\n<li>loggen<\/li>\n\n\n\n<li>rendern<\/li>\n\n\n\n<li>testen<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist besonders wertvoll, wenn mehrere UI-Modi existieren.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">TUI und Orchestrator sind unterschiedliche Dinge<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Die Zustandsmaschine geh\u00f6rt konzeptionell eher zum Orchestrator als zum Renderer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Orchestrator entscheidet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Connecting\n\u2193\nAuditing\n\u2193\nConfirmation\n\u2193\nProcessing\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Renderer sagt nur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Wenn Connecting:\nzeige Connecting Screen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Also:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Orchestrator\n    \u2502\n    \u2502 changes State\n    \u25bc\nState\n    \u2502\n    \u25bc\nRenderer\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Renderer\n    \u2193\nentscheidet, was als N\u00e4chstes passiert\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist eine wichtige Grenze.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Der Renderer sollte m\u00f6glichst deterministisch sein<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Idealerweise gilt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gleicher State\n=\ngleicher Screen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das macht Rendering testbar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$state = @{\n    Phase = 'Completed'\n    Processed = 184\n    Failed = 0\n    Remaining = 0\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">dann sollte immer derselbe Completion Screen entstehen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Renderer braucht daf\u00fcr keine API.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keine Dateien.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keine Business Rules.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nur State.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Screenshots werden dadurch reproduzierbar<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Das hat einen angenehmen Nebeneffekt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Man kann UI-Zust\u00e4nde isoliert entwickeln.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$testState = &#91;pscustomobject]@{\n    Phase         = 'Processing'\n    CurrentFolder = 'Inbox \/ Archive \/ 2026'\n    Processed     = 125\n    Total         = 184\n    Failed        = 2\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Show-ProcessingScreen -State $testState\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Keine echte API-Verbindung n\u00f6tig.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keine 184 echten Nachrichten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keine Wartezeit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Damit wird TUI-Design pl\u00f6tzlich viel angenehmer.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Fehlerzust\u00e4nde lassen sich ebenfalls simulieren<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$testState = &#91;pscustomobject]@{\n    Phase     = 'Failed'\n    Processed = 125\n    Total     = 184\n    Error     = 'HTTP 503 Service Unavailable'\n    CanResume = $true\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Damit l\u00e4sst sich der Fehler-Screen jederzeit pr\u00fcfen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist ein gro\u00dfer Unterschied zu einer UI, die nur sichtbar wird, wenn zuf\u00e4llig ein echter Fehler auftritt.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Zustandsmaschinen verbessern Tests<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Auch funktionale Tests werden einfacher.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Man kann beispielsweise pr\u00fcfen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Initial State\n= Initializing\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nach erfolgreicher Authentifizierung:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Connecting\n\u2192 Auditing\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nach Best\u00e4tigung:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>WaitingForConfirmation\n\u2192 Processing\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nach erfolgreicher Verarbeitung:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Processing\n\u2192 Verifying\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nach Verifikation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Verifying\n\u2192 Completed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Bei Fehler:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Processing\n\u2192 Failed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist wesentlich pr\u00e4ziser als zu testen, ob irgendwo:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\"Success\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ausgegeben wurde.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Resume braucht Zustand<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Sp\u00e4testens bei Wiederaufnahme wird State unvermeidlich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Angenommen, eine Operation verarbeitet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>184 Elemente\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nach 120 Elementen bricht das Programm ab.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Checkpoint k\u00f6nnte enthalten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"phase\": \"processing\",\n  \"processed\": 120,\n  \"succeededIds\": &#91;\n    \"...\"\n  ],\n  \"startedAt\": \"2026-09-05T14:00:00Z\"\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Beim n\u00e4chsten Start entsteht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>RESUMING\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">als eigener sinnvoller Zustand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die TUI k\u00f6nnte anzeigen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>RESUMING PREVIOUS RUN\n\n120 items already completed\n64 items remaining\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ohne explizites Zustandsmodell wirkt Resume schnell wie ein angeklebtes Sonderfeature.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Persistierter Zustand ist nicht automatisch UI-State<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Dabei sollte man zwei Dinge unterscheiden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Persistenter Arbeitszustand:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>welche IDs sind bereits erfolgreich?\nwelcher Run war aktiv?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">UI-State:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>welcher Screen wird angezeigt?\nwelcher Ordner ist aktuell?\nwelche Animation l\u00e4uft?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht alles muss gespeichert werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nach einem Neustart ist beispielsweise egal, welches Spinner-Frame zuletzt sichtbar war.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Relevant ist nur der fachliche Zustand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist ein gutes Beispiel f\u00fcr die Trennung von:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Domain State\nUI State\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\">Domain State und UI State<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein sauberes Modell k\u00f6nnte so aussehen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Domain State\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nProcessed\nFailed\nRemaining\nCurrentItem\nVerificationResult\n\nUI State\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nCurrentScreen\nLastRenderTime\nSpinnerFrame\nTerminalWidth\nAnimationEnabled\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Renderer verwendet beide.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Gesch\u00e4ftslogik sollte aber nur Domain State kennen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Warum das gegen Flackern hilft<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Interessanterweise verbessert ein sauberes State-Modell auch das Rendering.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn die UI wei\u00df:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>State vorher = Processing\nState jetzt   = Processing\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">dann braucht sie keinen Full Clear.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nur ein Live Update.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn dagegen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>State vorher = Processing\nState jetzt   = Verifying\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">kann sie bewusst einen Screen Transition durchf\u00fchren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Damit ergibt sich automatisch:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>same state\n\u2192 in-place redraw\n\nstate transition\n\u2192 full redraw\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist wesentlich sauberer als \u00fcberall manuell zu entscheiden, wann <code>Clear-Host<\/code> ausgef\u00fchrt wird.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Rendering wird vom State Change getrieben<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Damit kann man ein sehr einfaches Modell bauen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if ($PreviousState.Phase -ne $CurrentState.Phase) {\n    Invoke-ScreenTransition\n}\n\nRender-State $CurrentState\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Zus\u00e4tzlich k\u00f6nnen Live-Updates gedrosselt werden:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if ((Get-Date) - $LastRender -gt 150ms) {\n    Render-State $CurrentState\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Zustandsmaschine liefert also nicht nur semantische Klarheit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie liefert auch eine Grundlage f\u00fcr effizientes Rendering.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Animation ist ein Detail des Renderers<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Jetzt kann der Spinner endlich an die richtige Stelle.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if ($State.Phase -eq 'Connecting') {\n    $frame = Get-SpinnerFrame\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Engine wei\u00df nicht, dass ein Spinner existiert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist genau richtig.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn Animation deaktiviert wird:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-NoAnimation\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u00e4ndert sich:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Connecting\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">nicht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nur seine Darstellung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vielleicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u2839 Connecting...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">wird zu:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Connecting...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Anwendungszustand bleibt identisch.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Dasselbe gilt f\u00fcr Farben<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Phase = Failed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">dann kann der Renderer entscheiden:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Rot\n&#91;ERROR]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Plain Renderer k\u00f6nnte dagegen nur schreiben:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ERROR: Processing failed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Und JSON:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"phase\": \"failed\"\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wieder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>State bleibt gleich\nDarstellung \u00e4ndert sich\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Genau so sollte es sein.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Machine Output profitiert davon besonders<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein guter State l\u00e4sst sich fast direkt serialisieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$state | ConvertTo-Json\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nat\u00fcrlich sollte ein \u00f6ffentliches JSON-Schema bewusst definiert werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber konzeptionell ist die N\u00e4he hilfreich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das verhindert, dass TUI und <code>-Quiet<\/code> unterschiedliche Vorstellungen vom Ergebnis entwickeln.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beide basieren auf derselben Wahrheit.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Die Zustandsmaschine sollte nicht zum Selbstzweck werden<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Nat\u00fcrlich braucht ein kleines Skript kein Framework mit:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>StateFactory\nStateTransitionManager\nStateRepository\nStateObserverProvider\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ein einfacher <code>switch<\/code> und ein State-Objekt k\u00f6nnen v\u00f6llig ausreichen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$state.Phase = 'Connecting'\n\nConnect-Service\n\n$state.Phase = 'Processing'\n\nInvoke-Processing\n\n$state.Phase = 'Verifying'\n\nInvoke-Verification\n\n$state.Phase = 'Completed'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist bereits viel besser, als State nur implizit \u00fcber Ausgaben zu definieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Architektur ist nicht die Zahl der Abstraktionen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Architektur ist Klarheit \u00fcber Verantwortlichkeiten.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Eine praktische Reihenfolge<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn ich heute eine neue TUI-Anwendung bauen w\u00fcrde, w\u00e4re die Reihenfolge:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Fachlichen Ablauf definieren<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>Connect\nDiscover\nConfirm\nProcess\nVerify\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">2. Zust\u00e4nde daraus ableiten<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>Connecting\nAuditing\nWaitingForConfirmation\nProcessing\nVerifying\nCompleted\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3. Fehler- und Sonderzust\u00e4nde erg\u00e4nzen<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>Retrying\nFailed\nCancelled\nResuming\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4. Daten pro Zustand definieren<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Processing:\nCurrentItem\nProcessed\nTotal\nFailed\n\nRetrying:\nRetryNumber\nRetryAfter\nReason\n\nCompleted:\nProcessed\nFailed\nRemaining\nVerified\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5. Erst danach Screens bauen<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>Show-ProcessingScreen\nShow-RetryScreen\nShow-CompletedScreen\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">6. Danach Rendering optimieren<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>Cursor Home\nClear Below\nThrottle\nResponsive Width\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">7. Ganz am Ende Animation<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>Spinner\nPulse\nTransitions\nCompletion Effect\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist fast die umgekehrte Reihenfolge, in der solche Tools oft spontan entstehen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Warum der Spinner zuletzt kommt<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Der Spinner ist visuell auff\u00e4llig.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Architektonisch ist er aber trivial.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Er 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\">Wie visualisiere ich einen laufenden, nicht exakt messbaren Zustand?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn nicht einmal klar ist, was:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>laufend\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">bedeutet, ist der Spinner zu fr\u00fch.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn State und Renderer dagegen sauber getrennt sind, ist seine Implementierung fast nebens\u00e4chlich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist die eigentliche Pointe.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Ein schlechtes Beispiel<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Angenommen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Write-Host \"Connecting\"\n\nConnect-Service\n\nClear-Host\nWrite-Host \"Processing\"\n\nforeach ($item in $items) {\n    Write-Host \"Processing $item\"\n}\n\nClear-Host\nWrite-Host \"Done\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Zust\u00e4nde existieren zwar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber nur implizit in der Reihenfolge der Ausgabe.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn jetzt Retry hinzukommt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>catch {\n    Write-Host \"Retrying\"\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">und sp\u00e4ter Verification:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Write-Host \"Verifying\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">beginnt die UI den Ablauf zu definieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das wird zunehmend schwer zu kontrollieren.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Dasselbe mit explizitem State<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Stattdessen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$state.Phase = 'Connecting'\nRender-State $state\n\nConnect-Service\n\n$state.Phase = 'Processing'\nRender-State $state\n\nforeach ($item in $items) {\n    $state.CurrentItem = $item\n    $state.Processed++\n    Render-State $state\n}\n\n$state.Phase = 'Verifying'\nRender-State $state\n\n$verification = Invoke-Verification\n\n$state.Phase = 'Completed'\n$state.Verified = $verification.Success\nRender-State $state\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Jetzt ist die Darstellung austauschbar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Und der Ablauf bleibt verst\u00e4ndlich.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Der entscheidende Test<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine einfache Frage zeigt schnell, ob die Architektur stimmt:<\/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\">Kann ich die Anwendung komplett ohne Terminaloberfl\u00e4che ausf\u00fchren?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn ja, ist die Zustandsmaschine wahrscheinlich von der TUI getrennt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Engine\n\u2192 State\n\u2192 JSON\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">statt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Engine\n\u2192 TUI\n\u2192 irgendwie Ergebnis\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ein zweiter Test:<\/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\">Kann ich jeden Screen mit k\u00fcnstlichem State anzeigen?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn ja, ist der Renderer wahrscheinlich sauber getrennt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beide Tests zusammen sind sehr stark.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Die TUI ist nicht das Programm<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist vielleicht die wichtigste Erkenntnis.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine gute TUI kann sich wie das Programm anf\u00fchlen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie ist es aber nicht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Programm besteht aus:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Business Logic\nState\nLifecycle\nError Handling\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die TUI ist nur eine m\u00f6gliche Oberfl\u00e4che darauf.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>                Application\n                      \u2502\n                    State\n                      \u2502\n          \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n          \u2502           \u2502           \u2502\n         TUI         JSON        Log\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn diese Trennung gelingt, wird fast alles einfacher:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Tests<\/li>\n\n\n\n<li>Quiet Mode<\/li>\n\n\n\n<li>Automation<\/li>\n\n\n\n<li>Fehlerbehandlung<\/li>\n\n\n\n<li>Resume<\/li>\n\n\n\n<li>Rendering<\/li>\n\n\n\n<li>Animation<\/li>\n<\/ul>\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\">Bei einer Terminalanwendung ist die Versuchung gro\u00df, mit dem sichtbaren Teil zu beginnen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Banner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Farben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Spinner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Progressbar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Transitions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber diese Dinge sind nur Darstellungen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Unter ihnen liegt etwas Wichtigeres:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Was macht die Anwendung gerade?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Frage sollte jederzeit eindeutig beantwortbar sein.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb ist die richtige Reihenfolge:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Operation\n   \u2193\nState\n   \u2193\nState Transition\n   \u2193\nRenderer\n   \u2193\nScreen\n   \u2193\nAnimation\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Spinner\n   \u2193\nnoch ein Spinner\n   \u2193\nif\n   \u2193\nClear-Host\n   \u2193\nmehr State irgendwie nachbauen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die wichtigste Regel lautet daher:<\/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>Erst die Zustandsmaschine, dann der Spinner.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn der Zustand sauber modelliert ist, wird die TUI fast automatisch einfacher.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn der Zustand nicht sauber modelliert ist, kann selbst ein Spinner zum Architekturproblem werden.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Bei Terminalanwendungen beginnt die Arbeit oft an der falschen Stelle. Man hat eine Idee f\u00fcr ein Tool, baut die eigentliche Funktion und m\u00f6chte dann schnell etwas Feedback im Terminal anzeigen. Also kommt zuerst: Dann: Dann ein Spinner. Dann Farben. Dann eine Progressbar. Dann ein Banner. Dann \u00dcberg\u00e4nge. Dann ein finaler Erfolgs-Screen. Das Ergebnis sieht irgendwann [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":336,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[60,27,8,25,59],"tags":[],"class_list":["post-326","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-automation","category-programming-principles","category-software-architecture","category-software-engineering","category-system-engineering"],"_links":{"self":[{"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/posts\/326","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=326"}],"version-history":[{"count":1,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/posts\/326\/revisions"}],"predecessor-version":[{"id":327,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/posts\/326\/revisions\/327"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/media\/336"}],"wp:attachment":[{"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=326"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=326"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=326"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}