Railway Oriented Programming ist Scott Wlaschins Metapher aus den Jahren 2013 und 2014 für das Komponieren fehlbarer Funktionen: eine Erfolgsspur und eine Fehlerspur, an jedem Schritt eine Weiche dazwischen. Fehler werden zu Werten in einem Result-artigen Rückgabetyp, Kombinatoren wie bind, map und mapError verbinden die Schritte, und der erste Fehler überspringt alle weiteren. Wlaschin selbst begrenzt den Stil auf erwartete Fehlschläge an den Rändern, nicht auf jeden Fehler.
Railway Oriented Programming
Details
- Vollständiger Name
-
Railway Oriented Programming, Scott Wlaschins Metapher für das Komponieren fehlbarer Funktionen — Artikel auf F# for Fun and Profit (2013) und NDC-Vortrag (2014), erweitert in Domain Modeling Made Functional (2018)
- Auch bekannt als
-
ROP, Zweispur-Programmierung, die Eisenbahn-Metapher, Result-basierte Fehlerbehandlung, Fehler als Werte
Kernkonzepte:
- Ein Kompositionsstil, keine Bibliothek
-
ROP benennt die Form des Codes: zwei Spuren, an jedem Schritt eine Weiche und ein Kompositionsoperator, der beide verbindet. Es ist kein Paket, das man installiert. ROP in einer Java- oder TypeScript-Codebasis zu nennen, verlangt diese Form — umgesetzt mit dem Either-artigen Typ, der schon da ist.
- Zwei Spuren und eine Weiche
-
Eine Spur trägt den Erfolg, eine den Fehler. Jeder Schritt ist eine Weiche: Er hält den Wert auf dem glücklichen Pfad oder lenkt ihn auf die Fehlerspur. Auf der Fehlerspur werden alle späteren Schritte übersprungen — Kurzschluss --, und die Pipeline liest sich von oben nach unten, ohne verschachtelte Bedingungen, die Fehlerfälle abzählen.
- Fehler sind Werte, nicht Kontrollfluss
-
Fehlschläge werden Teil des Rückgabetyps statt eines unsichtbaren zweiten Ausgangs aus der Funktion. Eine Signatur, die
Resultzurückgibt, sagt dem Aufrufer, dass Fehlschlag erwartet ist und behandelt werden muss; eine Signatur, die wirft, sagt ihm nichts. - Die Kombinatoren sind das Vokabular
-
bind(auchflatMap,andThen) hebt eine Weichenfunktion zur Zweispurfunktion.maphebt eine gewöhnliche Einspurfunktion auf die Erfolgsspur.mapErrortransformiert die Fehlerspur. Diese Namen zu nennen ist meist der schnellste Weg zu einer Pipeline statt zu einer Kaskade früher Returns. - Entsprechungen in anderen Sprachen
-
F#
Result<'Success, 'Failure>; RustResult<T, E>mit?für den Kurzschluss; ScalaEitherundTry, VavrsEitherundTryin Java,fp-tsundneverthrowin TypeScript,Arrowin Kotlin, Gos explizites Paar(value, err)als nicht-monadischer Verwandter. Die Metapher ist in F# geboren, die Übersetzung muss in anderen Sprachen also ausdrücklich benannt werden. - Kurzschluss oder Sammeln
-
Pipeline-Schritte schließen kurz — der erste Fehler gewinnt, was richtig ist, wenn Schritt zwei von Schritt eins abhängt. Validierung will meist das Gegenteil: alle Fehler sammeln und zusammen melden. Das ist ein Applicative, kein monadisches
bind; wer beides verwechselt, baut Formulare, die Feld für Feld meckern. - Domänenfehler werden modelliert, nicht in Strings gelegt
-
Die Fehlerspur trägt einen entworfenen Typ — typischerweise eine Union der Fehler, die diese Operation tatsächlich haben kann --, keinen String. Hier trifft ROP auf
domain-driven-design: Die Fehlerfälle gehören zum Domänenmodell. - An den Rändern, nicht überall
-
Der Stil gehört dorthin, wo Fehlschlag erwartet und Teil des Vertrags ist: Parsen, Validieren, Orchestrierung von Use Cases (
postels-lawam eingehenden Rand). In einer reinen Berechnung, die nicht fehlschlagen kann, fügt einResultnur Zeremonie hinzu.
Wann zu verwenden:
-
Bei der Orchestrierung von Use Cases, in denen mehrere fehlbare Schritte hintereinander komponiert werden: validieren, Eindeutigkeit prüfen, speichern, benachrichtigen
-
Beim Parsen und Validieren an einer Grenze, wo dem Aufrufer gesagt werden muss, was falsch war
-
Zum Ersetzen einer Kette von try/catch-Blöcken, deren einziger Zweck das Umwandeln einer Exception in eine andere ist
-
Um erwarteten Fehlschlag in Signaturen sichtbar zu machen, damit Reviewer oder Modell sehen, welche Aufrufe Behandlung brauchen
-
Um den Stil über Sprachgrenzen zu benennen, wenn der Typ im Team
Either,Tryoder ein selbstgebautesResultheißt und die Zweispur-Form gemeint ist, nicht die Idiome dieser Bibliothek -
Neben
design-by-contract: Contracts schieben den Fehlschlag in Vorbedingungen, ROP trägt ihn im Rückgabetyp — die Wahl zwischen beiden lohnt eine bewusste Entscheidung
Häufige Missverständnisse:
-
❌ „Railway Oriented Programming ist eine F#-Bibliothek“ — es ist ein Kompositionsstil;
Resultist ein Typ, und der Stil ist älter und langlebiger als jedes Paket -
❌ „Result für jeden Fehler verwenden“ — Wlaschin hat gegen genau das einen eigenen Beitrag geschrieben; Infrastrukturfehler und Programmierfehler sind keine Domänenfehler
-
❌ „ROP ersetzt Exceptions“ — es ersetzt sie nur für erwartete Fehlschläge; Panics und nicht behebbare Störungen bleiben außerhalb der zwei Spuren
-
❌ „Die Fehlerspur ist für Fehlermeldungen“ — sie trägt einen modellierten Typ; eine String-Fehlerspur wirft die Information weg, nach der der Aufrufer verzweigen muss
-
❌ „
bindund Applicative sind austauschbar“ —bindschließt kurz, Applicative sammelt; Validierung braucht meist das Zweite -
✓ „Zwei Spuren, je Schritt eine Weiche und ein modellierter Fehlertyp an den Rändern“ — das ist der Anker, und er überträgt sich über Sprachgrenzen
Kritik:
-
Wlaschin hat das Gegengewicht selbst geschrieben, und es ist die schärfste verfügbare Kritik. Fünf Jahre nach dem Vortrag, der die Metapher bekannt machte, veröffentlichte er „Against Railway-Oriented Programming“ (20. Dezember 2019). Sein einleitendes Zugeständnis: „I do think it is often used thoughtlessly, especially if it’s a shiny new technique that you’ve just added to your toolbox.“ Die Ersetzung, die die meisten Leser vornehmen, schließt er aus: „Don’t reinvent 'try-catch'!“ Die Grenze zieht er über den Zweck — „Result is only for expected control-flow, not for unexpected situations“ --, was genau die Fälle ausschließt, in denen man einen Stack Trace will. Und den Reflexgebrauch innerhalb eines Moduls lehnt er ab: „If you are doing some complex control flow but the logic is hidden from the outside world, don’t use Result just for the sake of it.“ Wlaschin, „Against Railway-Oriented Programming“.
-
Kein zweiter namentlicher Kritiker auf Englisch oder Deutsch gefunden. Gesucht wurde in beiden Sprachen nach dokumentierten Einwänden eines benannten Autors. Was auf Deutsch existiert, ist Werbung für den Stil, nicht Kritik: Stefan Macke, „Railway Oriented Programming in Java“, heise (20. Januar 2017), stellt den Stil für Java vor, und die kritischen Seiten des Artikels liegen hinter der heise+-Bezahlschranke — daraus wird hier keine Stelle zitiert. Die vielfach wiederholten Einwände — verschachtelte Container, wenn schon eine zweite Monade im Spiel ist, und Weitschweifigkeit in Sprachen ohne
do-Notation oder?— kursieren in Forenfäden ohne zitierbaren Autor und bleiben deshalb weg, statt niemandem zugeschrieben zu werden.
Aktueller Stand:
-
Wlaschins eigene Einschränkungen gehören inzwischen zum Kanon. Der Vortrag von 2014 und der Beitrag „Against ROP“ von 2019 stehen beide aktuell auf F# for Fun and Profit — die maßgebliche Quelle des Begriffs trägt also ihre eigenen Grenzen mit. Wer ROP ohne diese Grenzen zitiert, zitiert die Hälfte.
-
Die Gastsprachen haben Platz dafür gemacht, jede zu ihrer Zeit. Kotlin ist der dokumentierte Fall: „In versions before Kotlin 1.5
Result<T>cannot be used as a direct result type of Kotlin functions, properties ofResulttype are also restricted“, weil „future versions of Kotlin might wish to expand and/or change semantics of functions that returnResulttype“ — gefolgt von „UPDATE: These limitations are lifted since Kotlin 1.5.“ Kotlin KEEP,proposals/stdlib/result.md. Rusts?-Operator und die Either-artigen Bibliotheken in Java und TypeScript sind die andere Hälfte des Bildes: Der Stil ist heute überall verfügbar, mit unterschiedlicher Ergonomie. -
Den Begriff zu nennen erzeugt den Typ; eine Baseline griff ungefragt danach. Gemessen auf
claude-haiku-4-5im Clean Room am 3. Oktober 2026: Bei einem 900-zeiligen Datenjob, dessen einziges Fehlersignal eine Logzeile mitNonewar, griff eine von zwei Baselines von sich aus zuEither. Mit dem Begriff benannt, entstanden ein ausdrücklicherResult-Typ und die Erfolgs-/Fehlerspur. Wlaschin 2014 wird auf der schwachen Stufe mit hoher Zuversicht korrekt zugeschrieben.