Skip to content

Railway Diagrams

The railway diagram is the signature visualization of effect-analyzer. Inspired by the railway-oriented programming pattern, it renders your Effect program as a straight-line happy path with error branches forking off at failure points.

flowchart LR
A[accounts #lt;- AccountService] -->|ok| B[audit #lt;- AuditLog]
B -->|ok| C[balance #lt;- accounts.getBalance]
C -->|ok| D[decision]
D -->|ok| Done((Success))
C -->|err| CE[AccountNotFoundError]
D -->|err| DE[InsufficientFundsError]

In a railway diagram, the main track represents the success path - the sequence of operations that execute when everything goes right. At each point where a failure can occur, an error branch splits off to show what happens on failure.

This makes it immediately clear:

  • What the program does when it succeeds
  • Where failures can happen
  • What error types are produced at each point
Terminal window
npx effect-analyze ./src/transfer.ts --format mermaid-railway

For the docs transfer fixture, this produces the diagram above. Solid |ok| arrows trace the happy path; solid |err| arrows show error branches.

Auto mode picks the railway diagram as the baseline when your program has:

  • Low cyclomatic complexity - few branching points
  • Linear structure - mostly sequential yield* steps in a generator
  • No parallel or race patterns - those push auto mode toward the concurrency view

Programs with Effect.gen and a series of yields are the ideal fit for railway diagrams.

Railway diagrams default to left-to-right (LR) flow, which reads naturally as a timeline. Override this with the --direction flag:

Terminal window
npx effect-analyze ./src/transfer.ts --format mermaid-railway --direction TB
Direction Best For
LR Default - reads like a timeline
TB Tall, narrow programs
RL Right-to-left reading order
BT Bottom-up flow

Generate railway diagrams through the library API:

import { analyze } from "effect-analyzer/analysis"
import { renderRailwayMermaid } from "effect-analyzer/diagram"
import { Effect } from "effect"
const ir = await Effect.runPromise(analyze("./src/transfer.ts").single)
const diagram = renderRailwayMermaid(ir, { direction: "LR" })
console.log(diagram)

Enable --style-guide for cleaner diagrams that apply readability heuristics - collapsing trivial nodes, shortening labels, and reducing visual noise:

Terminal window
npx effect-analyze ./src/transfer.ts --format mermaid-railway --style-guide

Railway diagrams work best for linear, sequential programs. If your program has:

  • Heavy branching - use mermaid-decisions or the standard mermaid flowchart
  • Parallel operations - use mermaid-concurrency
  • Complex error handling - use Error Flows
  • Many services - use Service Maps