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:

ParameterMeaningFileInspector’s inspect method
ASuccess valueFileStats
EExpected failureFileStatsError
RRequired servicesnever

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 failure36 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 defects36 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 construction16, Layer composition13, 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 · 65e9465f
  Effect.provide(
    Layer.mergeAll(
      Layer.provideMerge(FileInspector.layer, NodeServices.layer),
      remoteJoinInterestLayer,
      Approval.denyAll
    )
  )
);

NodeRuntime.runMain(program);

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

apps/site/src/worker.ts · 246600bd
    return {
      fetch: observeRequestIncidents(
        events === undefined
          ? readerApp
          : withEventCapture({
              captureCity: true,
              identityMode,
              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 error. 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 boundary17: define and decode accepted data.
  3. Expected failures and defects36: name failures without hiding crashes.
  4. Service construction16: capture implementation dependencies.
  5. Layer composition13: supply the graph at an entrypoint.
  6. Configuration7: parse supplied settings.
  7. Testing22: prove behavior through supplied services.
  8. Scopes20: acquire resources inside their owner and release them on exit.
  9. Concurrency7: bound work and stop owned children.
  10. HTTP responses7: check status and decode accepted data.
  11. Tracing7: 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-10 in 978dccc. Source on GitHub. Change log.

Linked from

  1. 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.

  2. 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.

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

    Effect basics — edited: docs: point live docs, skills and Brain pages at apps/site

  4. 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.¶

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

    See Effect basics and infrastructure as effects.¶

  6. 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.¶

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

    Declared Effect actors record lifecycle transitions.

  8. Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
  9. 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.¶

  10. 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.¶

  11. 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.

  12. 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.

  13. 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.

  14. 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.¶

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

    Effect basics — edited: docs: point live docs, skills and Brain pages at apps/site

  16. 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.¶

  17. 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 (64)