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:
- 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. - Parts 2–4 are the core language: constructors and combinators, typed
errors, the flat
Causemodel, services (Context.Service), layers, scopes, fibers, and structured concurrency. - Parts 5–6 cover time (
Schedule,DateTime) andStream. - Part 7 is
Schema— v4’s single source of truth for validation, serialization, and domain modeling. - Part 8 is the platform: HTTP clients, schema-first
HttpApiservers with generated docs + typed clients, SQL viaModel, request batching. - Part 9 is production: logging/tracing/metrics, testing with virtual time, embedding Effect inside non-Effect apps, and a capstone service.
- The Reference section holds a cheatsheet and a v3→v4 mapping.
Why a runtime, not just a library
Section titled “Why a runtime, not just a library”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 (likeTinPromise<T>)E— the typed failure channel (Promisehas 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.
What changed in v4 (the 60-second tour)
Section titled “What changed in v4 (the 60-second tour)”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.
How to read this course
Section titled “How to read this course”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.
pnpm add effect@latest # v4 lineFor Node-specific runtime pieces (NodeRuntime.runMain, the HTTP server
layer, file system):
pnpm add @effect/platform-nodeTypeScript requirements: enable strict, and ideally
exactOptionalPropertyTypes: true — several Schema optionality variants only
make their distinctions under it.