Effect basics

A service is a named interface for work. A context holds supplied service implementations.

An Effect describes work. Build the description, compose it with other Effects, supply its services, then let a runtime execute it.

Read the three type parameters

Effect.Effect<A, E, R> describes three things:

This method’s full return type is Effect.Effect<FileStats, FileStatsError, never>. The source omits the final parameter because it defaults to never.

packages/core/src/file-inspector.ts · 65e9465f
export class FileInspector extends Context.Service<
  FileInspector,
  {
    readonly inspect: (
      path: string
    ) => Effect.Effect<FileStats, FileStatsError>;
  }
>()("@rat-stack/core/FileInspector", {
  make: Effect.gen(function* makeFileInspector() {
    const fileSystem = yield* FileSystem.FileSystem;

Constructing the inspector needs FileSystem. Its returned method has captured that dependency and does not require it again.

R = never means no services remain to supply. It does not mean the computation cannot fail. E = never means no declared expected failures; it does not rule out defects or interruption.

A description is not a completed operation

Calling inspector.inspect(path) produces an Effect description. It does not read the file immediately. The method composes the read with an error translation before a runtime executes either operation.

packages/core/src/file-inspector.ts · 65e9465f
    const inspect = Effect.fn("FileInspector.inspect")(function* inspect(
      path: string
    ) {
      const content = yield* fileSystem
        .readFile(path)
        .pipe(
          Effect.mapError(
            (error) => new FileStatsError({ path, reason: error.message })
          )
        );

      return summarizeBytes(path, content);
    });

Ordinary JavaScript still evaluates while you build a description. Starting a Promise first, then wrapping it, does not undo that eager work. Keep external work inside the Effect constructor’s callback.

Write a sequence with gen; transform it with pipe

Effect.gen composes Effects in generator syntax. yield* obtains a success value or propagates the failure. The generator describes work for the Effect runtime.

Use pipe for short transforms, such as mapping a value, translating a failure, or providing a service. Use a generator when the next step needs values from earlier steps.

Generators and combinators both compose descriptions without running a nested runtime.

Expected failures and defects are different

An expected failure38 belongs in E, where callers can handle it by its declared type or tag. A defect is an unexpected execution failure. Logging a failure does not make the operation successful. Handle expected failures without hiding defects38 shows the file error and its CLI handler.

Requirements name services to supply

A requirement is a service the computation needs. A Layer describes how to build a service and supply its dependencies.

Layers build implementations and satisfy their construction dependencies. Getting FileInspector from context requires that service; calling its captured inspect method needs no extra service. Read service construction30, Layer composition21, and context and requirements9.

Run at the application boundary

The CLI builds and transforms a program with pipe, then provides its services and their dependencies. NodeRuntime.runMain executes the final Effect:

apps/cli/src/cli.ts · b4d6b8f4
  Effect.provide(
    Layer.mergeAll(
      Layer.provideMerge(FileInspector.layer, NodeServices.layer),
      remoteJoinInterestLayer,
      localPromptLibraryLayer.pipe(Layer.provide(NodeServices.layer)),
      localCafeDirectoryLayer.pipe(Layer.provide(NodeServices.layer)),
      localLearnLayer,
      Flags.defaults(eventFlags),
      Approval.denyAll
    )
  )
);

NodeRuntime.runMain(program);

The Worker60 returns a fetch Effect to its Alchemy Worker adapter:

apps/site/src/worker.ts · b4d6b8f4
    return {
      fetch: observeRequestLog(
        observeRequestIncidents(
          events === undefined
            ? readerApp
            : Effect.gen(function* captureWithLiveIdentity() {
                return yield* withEventCapture({
                  captureCity: true,
                  identityMode: currentIdentity,
                  runInBackground: inBackground,
                })(readerApp).pipe(Effect.provideContext(events));
              })
        )
      ),
    };

The Worker adapter owns request execution. The domain does not scatter runPromise calls through handlers.

In tests, it.effect owns execution of the returned Effect. The test still supplies the services it needs. See tests through supplied services22. Run Effect at the integration boundary8 covers foreign code, runtime reuse, and disposal.

The common wrong first version

Scattering await Effect.runPromise(...) through library methods creates separate execution boundaries where composition should remain intact. It also turns the expected failure into a rejected Promise rather than preserving the Effect error channel for the caller. Return the composed Effect and let the application boundary run it.

Effect.promise assumes Promise rejection is an unexpected defect. For an external API with expected failures, use Effect.tryPromise with a translation into a named domain error10. Wrapping an already-started Promise also loses control over when its work begins.

  1. Effect basics: build a description before running it.
  2. Schemas define the boundary30: define and decode accepted data.
  3. Expected failures and defects38: name failures without hiding crashes.
  4. Service construction30: capture implementation dependencies.
  5. Layer composition21: supply the graph at an entrypoint.
  6. Configuration7: parse supplied settings.
  7. Testing22: prove behavior through supplied services.
  8. Scopes35: acquire resources inside their owner and release them on exit.
  9. Concurrency23: bound work and stop owned children.
  10. HTTP responses11: check status and decode accepted data.
  11. Tracing8: name meaningful operations and inspect export evidence.
  12. Runtime boundaries8: execute through the framework’s Effect integration and connect disposal.

Sources

  1. Arnaldi, Michael. n.d. TypeScript & Effects with Michael Arnaldi. Happy Path 81.. Michael Arnaldi. Historical explanation at 49:15 of success, errors, and requirements; current API evidence comes from the pinned source. Accessed 2026-10-06.
  2. Arnaldi, Michael. n.d. Why JavaScript Is Broken: How Effect and TypeScript Fixes It.. Michael Arnaldi. Historical explanation at 12:32 of lazy computations that can be transformed before execution. Accessed 2026-10-06.
  3. Effect contributors. 2026. Effect. Effect 4.0.0.. Effect-TS. Effect<A, E, R>, lazy execution, gen, mapError, fn, and typed failure handling. Accessed 2026-10-06.
  4. Effect contributors. 2026. Context. Effect 4.0.0.. Effect-TS. Service keys express requirements; supplied context satisfies them. Accessed 2026-10-06.
  5. Effect contributors. 2026. Effect Vitest integration.. Effect-TS. it.effect owns execution of an Effect returned by a test. Accessed 2026-10-06.
  6. rat-stack contributors. 2026. rat-stack source snapshot.. rat-stack. FileInspector's method type, CLI composition and execution, Worker fetch Effect, and Effect-aware tests. Accessed 2026-10-06.

Last changed 2026-10-24 in c525d23. Source on GitHub. Change log.

Linked from

  1. A Layer as constructor. Sam Goodwin compares an Effect Layer that builds a service to a constructor.

    Its constructor asks for SourceRepository, so the Layer carries that requirement.¶

  2. Accumulate independent errors before writing output. Check every independent job before any job writes an asset.

    Effect 4’s Effect.validate evaluates every collection element.

  3. Agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.

    Effect basics — An Effect describes work, expected failures, and required services before a runtime executes it.

  4. Alchemy Domain models a business as commands, aggregates, and events. Alchemy Domain is a domain-driven design framework for Effect whose one model runs in memory for tests and on Cloudflare Durable Objects in production.

    The Domain docs call it a domain-driven design framework for Effect.

  5. Bindings. Sam Goodwin describes bindings as the place where runtime permissions and configuration meet infrastructure.

    Cloudflare bindings In Alchemy, the binding is declared in the same Effect program as the resource.¶

  6. CAFE all stars. Public profiles of people and channels who build with Cloudflare, Alchemy, Foldkit and Effect, served by five read-only capabilities.

    CAFE all stars lists people and channels whose public work builds with Cloudflare, Alchemy, Foldkit and Effect.

  7. Change log. What changed in the files served here, newest first.

    Effect basics — new page: Initial commit from joelhooks/rat-stack template

  8. Cleanup can be an idempotent postcondition. Already-absent state can be success when absence is the operation’s intended result.

    Put the domain meaning in the service contract; Effect supplies execution.

  9. Concurrent work needs an owner and a bound. Limit active work, retain result order, and stop child work when its owning lifetime ends.

    Return to the Effect reading order.¶

  10. Context and requirements. Effect requirements make a computation's needed services visible in its type.

    Goal: read an Effect type and say which services the work still needs.¶

  11. durability. Keep a rat-stack app standing when a provider, pool, or dependency slows down or a worker dies. Use at any I/O boundary, connection pool, provider call, shared waiter, fan-out, flag, gate, or work that can outlive a request, or when asked "what happens when this is slow", "add a timeout", "make this durable", or "what survives a crash".

    Effect runs each fiber as a concurrent computation in one JavaScript runtime, not as an isolated process.

  12. Durability. Seven durable Effect patterns, each with its seam, its fence, and the incident that taught it.

    rat-stack uses Effect’s own primitives for each one, in real seams.

  13. Durability bibliography. Books, checked short quotations, source limits, and Effect mapping provenance.

    Book principles and our Effect mappings are separate.

  14. Effect is not BEAM. Fibers share a runtime; scope ownership does not supply isolated heaps or OTP restart strategies.

    Test a worker defect and a hard runtime exit separately.

  15. Effect keeps the surfaces on one contract. Schema owns values, projections build interfaces, and Layers supply implementations.

    Start with Effect basics, then construct a service and compose its Layers.¶

  16. Failures keep their meaning. Expected refusals, infrastructure faults, and defects need different policies.

    Classification and defect policy remain domain decisions.

  17. Feature flags. Typed feature flags with owners, removal dates, and stable subject rollouts.

    Declared Effect actors record lifecycle transitions.

  18. Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
  19. Handle expected failures without hiding defects. Declare expected failures, preserve their context, and recover by tag without turning crashes into successful outcomes.

    Such a field does not itself turn an expected error into a runtime defect.¶

  20. HTTP success needs a status check and a decoder. Supply the client, check the response status, decode accepted data, and name failures at the adapter boundary.

    Return to the Effect reading order.¶

  21. learn-rat-stack. Trace a capability through Effect, XState, five projections, the CLI, and Alchemy. Try hosted search and read.

    Read Effect basics before this trace if the computation model is new.

  22. onboard-a-list. Analyze or migrate an email list. Measure sanitation rules, preserve stops, choose a cohort, and verify an authorized import or sending cutover.

    Decode policy data with Effect Schema.

  23. One capability, every surface. A schema-typed contract and one handler feed every interface rat-stack keeps.

    Effect Schema provides types and runtime checks for these values.

  24. Pure cores reduce timeline coupling. Move deterministic rules out of time-dependent actions and shared mutable state.

    Wrapping a callback in Effect does not make it pure.¶

  25. Queues, Pipelines, Workflows and K2 do different jobs. Pick a Cloudflare product for work outside a request by the unit you track: a message, a row, a step, or a record in a log.

    In Alchemy, a Workflow body is an Effect.

  26. Rat Stack lore | rat-stack. Short, source-grounded notes on the ideas and decisions behind rat-stack.

    Effect basics — An Effect describes work, expected failures, and required services before a runtime executes it.

  27. Recovery needs model tests. Test sequences of failures and recoveries against explicit invariants.

    Check the model by injecting a defect.¶

  28. Replay needs stable inputs. Historical processing must not silently depend on today’s clock or external decisions.

    Activity can record an effect result under an engine.

  29. Retry needs idempotent effects. Repeating an operation must preserve its intended observable result.

    Processing guarantees inside one store do not cover every external side effect.¶

  30. Run Effect at the integration boundary. Build the runtime where a calling framework enters, reuse its services, and dispose it when that integration ends.

    The basics page shows CLI, Worker and test execution boundaries.

  31. Scopes release resources when work ends. Acquire resources lazily, keep their use inside the owner, and release them after success, failure, or interruption.

    Return to the Effect reading order.¶

  32. Source change history (log.md). The generated change log as Markdown.

    Effect basics — new page: Initial commit from joelhooks/rat-stack template

  33. Structure an Effect codebase by domain. Start with folders for application work, services and Layers in the same file, and startup code that supplies implementations.

    Start with Effect basics.¶

  34. The war stories. Seven public repo incidents: a deploy undoes itself, failure says 200, sessions forget their guests, and the rat tests the demolition instructions.

    Two typecheckers disagree about which imported files receive Effect diagnostics.

  35. Trace operations where they mean something. Name useful operations, add safe context, and distinguish creating a span from delivering it to a backend.

    Return to the Effect reading order.¶

Unlinked mentions (72)