Skip to content

Quick Start

This guide walks you through analyzing a real Effect program, viewing diagrams, and exploring different output formats.

Create a file called transfer.ts with a typical Effect workflow:

import { Effect, Context } from "effect"
class AccountService extends Context.Service<
AccountService,
{
readonly getBalance: (id: string) => Effect.Effect<number, AccountNotFound>
readonly debit: (id: string, amount: number) => Effect.Effect<void, InsufficientFunds>
readonly credit: (id: string, amount: number) => Effect.Effect<void, AccountNotFound>
}
>()("AccountService") {}
class AuditLog extends Context.Service<
AuditLog,
{ readonly record: (event: string) => Effect.Effect<void> }
>()("AuditLog") {}
class AccountNotFound {
readonly _tag = "AccountNotFound"
constructor(readonly id: string) {}
}
class InsufficientFunds {
readonly _tag = "InsufficientFunds"
constructor(readonly available: number, readonly requested: number) {}
}
export const transfer = (from: string, to: string, amount: number) =>
Effect.gen(function* () {
const accounts = yield* AccountService
const audit = yield* AuditLog
const balance = yield* accounts.getBalance(from)
if (balance < amount) {
yield* Effect.fail(new InsufficientFunds(balance, amount))
}
yield* accounts.debit(from, amount)
yield* accounts.credit(to, amount)
yield* audit.record(`Transferred ${amount} from ${from} to ${to}`)
return { from, to, amount }
})

Run the analyzer with no format flag to use auto mode, which selects the best diagrams for your program:

Terminal window
npx effect-analyze ./transfer.ts

That command prints the diagram and writes transfer.effect-analysis.md next to the source. Pass --no-colocate to print only.

Auto mode examines your program’s structure and picks the most relevant views. For this program, it will typically select a railway diagram (linear happy path with error branches) plus a service dependency map. Effect.runPromise in a test file is not treated as a second program.

Generate a focused railway-style view:

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

The railway diagram shows the happy path as a straight line with error branches forking off to the side. This is ideal for understanding the success flow and where failures can occur.

Export the raw intermediate representation:

Terminal window
npx effect-analyze ./transfer.ts --format json

The JSON output contains the full IR tree - every step, service dependency, error type, and source location. Use this for integration with your own tools.

Get a narrative description of what the program does:

Terminal window
npx effect-analyze ./transfer.ts --format explain

This produces a human-readable walkthrough of the program’s behavior, useful for documentation or code reviews.

View complexity metrics:

Terminal window
npx effect-analyze ./transfer.ts --format stats

This reports cyclomatic complexity, cognitive complexity, path count, nesting depth, and parallel breadth.

The default already writes transfer.effect-analysis.md. Use -o when you want a different path, and --no-colocate when you only want that file (or stdout):

Terminal window
npx effect-analyze ./transfer.ts --format mermaid-railway -o transfer-diagram.md --no-colocate

Re-analyze automatically when the file changes during development:

Terminal window
npx effect-analyze ./transfer.ts --watch

Import analysis and rendering functions from their task-specific entry points:

import { analyze, renderMermaid, calculateComplexity } from "effect-analyzer/diagram"
import { Effect } from "effect"
const ir = await Effect.runPromise(analyze("./transfer.ts").single)
// Generate a Mermaid diagram
const diagram = await Effect.runPromise(renderMermaid(ir))
// Calculate complexity
const metrics = calculateComplexity(ir)
console.log(metrics.cyclomaticComplexity) // e.g. 3

Use the root interface when you need a reusable analysis session or want to compare a static diagram with runtime spans:

import { analysis, computeDiagramFidelity } from "effect-analyzer"
import { Effect } from "effect"
const ir = await Effect.runPromise(analysis.file("./transfer.ts").single)
const fidelity = computeDiagramFidelity(ir)
console.log(fidelity.exact, fidelity.score)

The fidelity check fails when the analyzer finds an unknown or opaque node, a computed span name, or two nodes with the same span path:

Terminal window
npx effect-analyze ./transfer.ts --assert-diagram-fidelity

Use literal, distinct Effect.withSpan names for code that you plan to match against runtime traces. See Diagram Fidelity for the report fields and trace adapters.

Everything above works on one file. To see what a whole branch changed, run the review — the same report the GitHub Action posts on a PR:

Terminal window
npx effect-analyze review --base origin/main src/

It lists every Effect program the branch touched, flags removed error handlers and retries as regressions, shows lint findings the branch introduced, and grades the merge risk. Add the action and this arrives as a PR comment with the diagrams rendered.