Effect is a TypeScript library for building production-grade software with typed errors, structured concurrency, dependency injection, and built-in observability.
Before writing Effect code, detect which version the user is on:
# Check installed version
cat package.json | grep '"effect"'
Context.Service, Effect.catch, Effect.forkChild, Schema.TaggedErrorClassContext.Tag, Effect.catchAll, Effect.fork, Data.TaggedError> Note: v4 beta briefly used a ServiceMap module, renamed back to Context on 2026-04-07 (PR #1961). If you see ServiceMap. in any doc or older beta code, it is the current Context.. Both v3 and v4 import Context from "effect"; the exports inside differ (Context.Service in v4 vs Context.Tag in v3).
Prefer v4 for new projects - it's where Effect is going. In an existing codebase, match the installed version: don't rewrite v3 code in v4 syntax unless asked. If the version is genuinely unclear, default to v4 and say so. v4 is still in beta, so pin an exact version (4.0.0-beta.x) and expect occasional API churn.
v4 (primary):
v3 (for existing codebases):
Both versions:
LLM outputs frequently contain incorrect Effect APIs. Verify every API against the reference docs before using it.
Common hallucinations (both versions):
| Wrong (AI often generates) | Correct |
|---|---|
| ---------------------------------------------- | --------------------------------------------------------------- |
Effect.cachedWithTTL(...) | Cache.make({ capacity, timeToLive, lookup }) |
Effect.cachedInvalidateWithTTL(...) | cache.invalidate(key) / cache.invalidateAll() |
Effect.mapError(effect, fn) | Effect.mapError(fn) in pipe, or use Effect.catchTag |
import { Schema } from "@effect/schema" | import { Schema } from "effect" (v3.10+ and all v4) |
import { JSONSchema } from "@effect/schema" | import { JSONSchema } from "effect" (v3.10+) |
| JSON Schema Draft 2020-12 | Effect Schema generates Draft-07 |
| "thread-local storage" | "fiber-local storage" via FiberRef (v3) / Context.Reference (v4) |
| fibers are "cancelled" | fibers are "interrupted" |
| all queues have back-pressure | only bounded queues; sliding/dropping do not |
new MyError("message") | new MyError({ message: "..." }) (Schema errors take objects) |
v3-specific hallucinations:
| Wrong | Correct (v3) |
|---|---|
| ------------------------------------ | ----------------------------------------------------- |
Effect.Service (function call) | class Foo extends Effect.Service |
Effect.match(effect, { ... }) | Effect.match(effect, { onSuccess, onFailure }) |
Effect.provide(layer1, layer2) | Effect.provide(Layer.merge(layer1, layer2)) |
v4-specific hallucinations (AI may mix v3/v4):
| Wrong (v3 API used in v4 code) | Correct (v4) |
|---|---|
| ----------------------------------- | ------------------------------------------------------ |
Context.Tag("X") (v3 shape) | Context.Service or class syntax |
ServiceMap.Service / ServiceMap.Reference | Renamed back to Context.Service / Context.Reference on 2026-04-07 |
Effect.catchAll(fn) | Effect.catch(fn) |
Effect.fork(effect) | Effect.forkChild(effect) |
Effect.forkDaemon(effect) | Effect.forkDetach(effect) |
Data.TaggedError | Schema.TaggedErrorClass |
FiberRef.get(ref) | yield* References.X (a Context.Reference) |
yield* ref (Ref as Effect) | yield* Ref.get(ref) (Ref is no longer an Effect) |
yield* fiber (Fiber as Effect) | yield* Fiber.join(fiber) (Fiber is no longer Effect) |
Logger.Default / Logger.Live | Logger.layer (v4 naming convention) |
Schema.TaggedError | Schema.TaggedErrorClass |
Schema.makeUnsafe(input) | Schema.make(input) (throws SchemaError); also Schema.makeOption, Schema.makeEffect |
ParseResult (from "effect") | SchemaIssue module + SchemaError class; narrow with Schema.isSchemaError |
HttpApiEndpoint.get(n, p).pipe(HttpApiEndpoint.setPath(...), setPayload(...), setSuccess(...)) | HttpApiEndpoint.get(n, p, { params, query, payload, success, error }) (object-option form) |
Otlp.layer({ url, serviceName }) | OtlpTracer.layer({ url, resource: { serviceName } }) + OtlpSerialization.layerJson + FetchHttpClient.layer |
import { HttpApi } from "@effect/platform" (v4) | import { HttpApi } from "effect/unstable/httpapi" |
| HttpApi endpoint schema errors are typed errors by default | Since PR #2057 (2026-04-20) they default to defects unless transformed |
Read references/llm-corrections.md for the exhaustive corrections table.
Read only the reference files relevant to your task:
references/error-modeling.mdreferences/dependency-injection.mdreferences/retry-scheduling.mdreferences/concurrency.mdreferences/concurrency.mdExecutionPlan) → references/effect-ai.md / references/retry-scheduling.mdreferences/streams.mdreferences/resource-management.mdreferences/resource-management.mdreferences/schema.mdBrand) → references/schema.mdreferences/observability.mdreferences/http.mdreferences/http.md (covers both client and server)references/http.mdreferences/effect-ai.mdreferences/configuration.mdreferences/sql.mdreferences/cli.mdreferences/rpc.mdreferences/distributed.mdTx*) → references/stm.mdreferences/datetime.mdreferences/optics.mdMatch) → references/core-patterns.mdPool) → references/resource-management.mdreferences/concurrency.mdreferences/testing.mdreferences/testing.mdreferences/migration-async.mdreferences/migration-v4.mdreferences/core-patterns.mdreferences/llm-corrections.mdpackage.json before writing any codeEffect valuesEffect.gen for sequential logic, pipelines for simple transforms. In v4, prefer Effect.fn("name") for named functionsE channel; treat bugs as defectsScope when opening/closing things (files, connections, etc.)NodeRuntime.runMain or ManagedRuntime)Start with these ~20 functions (the official recommended set):
Creating effects: Effect.succeed, Effect.fail, Effect.sync, Effect.tryPromise
Composition: Effect.gen (+ Effect.fn in v4), Effect.andThen, Effect.map, Effect.tap, Effect.all
Running: Effect.runPromise, NodeRuntime.runMain (preferred for entry points)
Error handling: Effect.catchTag, Effect.catch (v4) / Effect.catchAll (v3), Effect.orDie
Resources: Effect.acquireRelease, Effect.acquireUseRelease, Effect.scoped
Dependencies: Effect.provide, Effect.provideService
Key modules: Effect, Schema, Layer, Option, Result (v4) / Either (v3), Array, Match
DI (v4): Context.Service, Context.Reference, Layer.effect, Effect.fn("name")
DI (v3): Context.Tag, Context.Reference
Always use barrel imports from "effect":
import { Context, Effect, Schema, Layer, Option, Stream } from "effect"
For companion packages, import from the package name. v3 and v4 differ here:
// v4 (recommended) - platform transports still separate, but HttpApi / observability
// moved under effect/unstable/*
import { NodeRuntime } from "@effect/platform-node"
import { FetchHttpClient } from "effect/unstable/http"
import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiBuilder, HttpApiScalar } from "effect/unstable/httpapi"
import { OtlpLogger, OtlpSerialization, OtlpTracer } from "effect/unstable/observability"
// v3 (stable) companion packages
import { NodeRuntime } from "@effect/platform-node"
import { HttpClient } from "@effect/platform"
import { NodeSdk } from "@effect/opentelemetry"
Avoid deep module imports (effect/Effect) unless your bundler requires it for tree-shaking.
Effect.gen (imperative) for multi-step logic; pipelines for transformsEffect.fn("name") instead of bare Effect.gen for named functions; use Effect.fnUntraced for internal helpers that don't need a span/stack-frameEffect.runPromise / Effect.runSync inside library code - only at program edgesNodeRuntime.runMain for CLI/server entry points (handles SIGINT gracefully)ManagedRuntime when integrating Effect into non-Effect frameworks (Hono, Express, etc.)return yield* when raising an error in a generator (ensures TS understands control flow)Effect.map((x) => fn(x)) not Effect.map(fn) (generics get erased)Effect shape when it helps design decisionsBefore outputting Effect code, verify:
"effect" (not @effect/schema, @effect/io, etc.)E; unexpected failures are defectsrun* is called only at program edges, not inside library codeacquireRelease are wrapped in Effect.scopedR requirements)yield (not yield without )return yield* pattern共 3 个版本