Skip to content

Introduction

effect-analyzer is a static analysis toolkit for Effect programs. It parses your TypeScript source with ts-morph, extracts a typed intermediate representation (IR) of every Effect program it finds, and renders that IR into diagrams, metrics, reports, and structured data. Your code never runs.

A compiler front-end reads source and hands the compiler a structure to work on. This does the same for Effect workflows, then hands the structure to you and to your tooling.

Your CI can compare today’s IR against the one you approved last week and fail the build when a program’s shape moves, which gives a coding agent a failure it can read and act on. See Guardrails for Coding Agents. You can also look at the structure yourself: the live browser playground and the interactive HTML viewer put the services, error paths, and concurrency of a function on one page you can share.

The analyzer recognizes a broad set of Effect patterns and control-flow constructs:

  • Generators - Effect.gen functions with yield* bindings
  • Pipes - Effect.pipe and .pipe() chains
  • Services - Context.Service dependencies and Layer providers
  • Layers - Layer.effect, Layer.provide, Layer.merge compositions
  • Error handlers - Effect.catch, Effect.catchTag, Effect.catchTags
  • Parallel & race - Effect.all with concurrency, Effect.race
  • Retry & timeout - Effect.retry, Effect.timeout, schedule-based policies
  • Streams - Stream pipelines and operators
  • Fibers - Effect.fork, Fiber.join, Fiber.interrupt
  • Conditionals & loops - Effect.if, Effect.loop, Effect.iterate
  • Cause & Exit - Cause.match, Exit.match patterns
  • Resources - Effect.acquireRelease, Scope-managed lifecycles

Every detected program is converted into a StaticEffectIR - a tree of typed nodes that captures the structure of your Effect code. Each node records its type (effect, generator, parallel, error-handler, etc.), its children, its service dependencies, its error types, and its source location.

This IR is the foundation for everything the tool produces. Renderers transform it into Mermaid diagrams. Analyzers compute complexity metrics from it. Diff tools compare two IRs to find structural changes.

TypeScript Source → ts-morph AST → Effect IR → Output

Because the IR is a plain data structure, you can also consume it directly in your own tooling via the library API.

  • Developers exploring unfamiliar Effect codebases or reviewing pull requests
  • Coding agents that need a machine-readable failure to regenerate against, and a backlog to work down
  • Teams generating living documentation for complex workflows
  • CI/CD pipelines tracking complexity regressions and structural changes over time
  • Tooling authors building custom analysis, linting, or visualization on top of the structured IR

effect-analyzer ships with over 25 output formats:

The docs now use committed fixtures instead of screenshots, so the examples below are real analyzer output you can reproduce locally.

Example fixture: packages/effect-analyzer/src/__fixtures__/docs/transfer-workflow.ts

$ npx effect-analyze ./src/__fixtures__/docs/transfer-workflow.ts --format explain
transferWorkflow (generator):
1. Yields accounts <- AccountService
2. Yields audit <- AuditLog
3. Yields balance <- accounts.getBalance
4. If balance < amount:
Returns:
Calls fail - constructor
5. Calls accounts.debit
6. Calls accounts.credit
7. Calls audit.record
Services required: AccountService, AuditLog
Error paths: AccountNotFoundError, InsufficientFundsError
Concurrency: sequential (no parallelism)

Project-wide audit example: packages/effect-analyzer/src/__fixtures__/docs/audit

$ npx effect-analyze ./src/__fixtures__/docs/audit --coverage-audit
Discovered: 4
Analyzed: 2
Zero programs: 2
Suspicious zeros: 0
Failed: 0
Effect adoption: 50.0% (2/4 discovered files)
Analysis success: 100.0% (2/2 relevant files)
IR source resolution: 100.00%
Zero categories: barrel/index=1, config/build=0, test/dtslint=0, type-only=0, suspicious=0, other=1

Diff example: packages/effect-analyzer/src/__fixtures__/docs/send-money-before.ts vs packages/effect-analyzer/src/__fixtures__/docs/send-money-after.ts

$ npx effect-analyze ./src/__fixtures__/docs/send-money-before.ts ./src/__fixtures__/docs/send-money-after.ts --diff
# Effect Program Diff: sendMoney -> sendMoney
Added: 8
Removed: 1
Unchanged: 7
Structural changes: 1
- transfers.execute
+ FraudScreening
+ Notifications
+ fraud.screen
+ notifications.sendConfirmation
Structural Changes
- retry block added

Install the package and run your first analysis in the Quick Start guide.