Skip to content

Effect-TS v4 — Overview

A deep, expert-track course on Effect v4 — from the generator machinery underneath to HttpApi, SQL, streams, and cluster. Start here for the map.

Effect is a runtime for TypeScript programs — a standard library plus an execution model that makes errors, dependencies, resources, concurrency, and observability values in your type system instead of conventions in your codebase. If you’ve ever wished that Promise carried its error type, that DI was checked at compile time, or that cancellation had actual semantics, Effect is the answer — and v4 is a near-total rewrite of the runtime with a dramatically consolidated API.

This course targets expert TypeScript developers. We assume you’re fluent in the type system (variance annotations, conditional types, declaration merging), you know what Symbol.iterator does even if you’ve never shipped a generator, and you don’t need anyone to explain async/await. What we build:

  1. Part 1 starts below the library: the iterator/generator protocol that Effect’s entire ergonomic layer rests on, then reconstructs Effect<A, E, R> from first principles.
  2. Parts 2–4 are the core language: constructors and combinators, typed errors, the flat Cause model, services (Context.Service), layers, scopes, fibers, and structured concurrency.
  3. Parts 5–6 cover time (Schedule, DateTime) and Stream.
  4. Part 7 is Schema — v4’s single source of truth for validation, serialization, and domain modeling.
  5. Part 8 is the platform: HTTP clients, schema-first HttpApi servers with generated docs + typed clients, SQL via Model, request batching.
  6. Part 9 is production: logging/tracing/metrics, testing with virtual time, embedding Effect inside non-Effect apps, and a capstone service.
  7. The Reference section holds a cheatsheet and a v3→v4 mapping.

A Promise<T> is eager, starts executing when created, can reject with anything, has no notion of “what it needs”, cannot be cancelled (only forgotten), and composes poorly with resource lifetimes. Effect replaces the unit of work with a description:

interface Effect<out A, out E = never, out R = never>
  • A — the success type (like T in Promise<T>)
  • E — the typed failure channel (Promise has none; this is the headline feature)
  • R — the requirements: which services must be in context to run this

Because a description is inert data, the runtime can do things promises can’t: interrupt fibers mid-flight, retry with backoff schedules, acquire/release resources under interruption, memoize dependency graphs, propagate spans and log context automatically, and execute everything on a scheduler that plays nicely with the JS event loop.

If you learned Effect v3 from blog posts, most of what you know still applies conceptually but almost nothing is spelled the same way:

Theme v3 v4
Packages effect + @effect/platform, @effect/rpc, @effect/cluster, … Consolidated into effect; platform drivers stay separate
Unstable APIs mixed into packages Explicit effect/unstable/* import paths (http, httpapi, sql, ai, …)
Versioning independent per package One version for the whole ecosystem
Services Context.Tag, GenericTag, Effect.Tag, Effect.Service One primitive: Context.Service (+ Context.Reference for defaults)
Fiber-local state FiberRef Removed — Context.Reference in the References module
Runtime Runtime<R> bundle + RuntimeFlags bit flags Deleted — Context<R> is the runtime; behavior via References
Errors catchAll, catchSome catch, catchFilter, new Filter module, reason-errors
Cause recursive tree (Sequential/Parallel) Flat { reasons: Reason[] }
Yieldable types Ref, Fiber, Deferred, Queue were Effects Only Effect, Option, Result, Config, Context.Key yield
Equality reference by default, Data for structure Structural by default for plain objects/arrays/Maps/Sets
Either Either<L, R> Renamed Result<E, A>, error-first
STM STM, TRef, TQueue, … TxRef, TxQueue, TxHashMap, …
Keep-alive needed runMain to stay alive Reference-counted keep-alive timer in core runtime

The runtime itself was rewritten for memory/speed — a minimal program tree-shakes to ~6 KB gzipped (~15 KB with Schema), which is why the old lightweight Micro runtime was deleted entirely.

The parts are ordered by dependency: each part presupposes the ones before it. Code samples are complete and idiomatic-v4 — they follow the house style of the upstream repo: Effect.gen / Effect.fn("name") as the primary style, combinators attached around them, Schema for all boundaries.

Terminal window
pnpm add effect@latest # v4 line

For Node-specific runtime pieces (NodeRuntime.runMain, the HTTP server layer, file system):

Terminal window
pnpm add @effect/platform-node

TypeScript requirements: enable strict, and ideally exactOptionalPropertyTypes: true — several Schema optionality variants only make their distinctions under it.