Dependency injection is the reason to choose Effect

Matt Pocock7 puts dependency injection first:

Now, what I think people get wrong is that they think that error handling and the type-safe nature of Effect is the reason you go for Effect. I think the reason you go for Effect is dependency injection, actually. It allows you to create these beautiful small modules that you can just swap out as needed and create these really nice looking tests with Effect too.

Dillon Mulroy compares Effect with Spring and NestJS:

Effect has probably the most novel dependency injection system that I’ve worked with. I’ve worked in enterprise languages like Java, done the whole Spring system. I’ve worked with NestJS. Effect’s dependency injection is genuinely fantastic and really, really nice to work with.

Dillon Mulroy, recorded outdoors. Play his one-minute clip on Effect dependency injection.
Watch Dillon's one-minute clip.

Requirements stay visible

An Effect<A, E, R> describes its result, failure, and required services. The R type tracks those requirements. The compiler rejects execution while a required service remains in R.

A service gives callers a typed interface. A Layer builds its implementation and declares its construction requirements. Shared Layer identity reuses construction within the same memoization context.

A service captures its dependencies

FileInspector.make obtains FileSystem during construction. Its inspect method uses that captured service. The method’s type carries no filesystem requirement.

packages/core/src/file-inspector.ts · c39e106e · FileInspector captures FileSystem
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;

The static Layer builds the service through make.

packages/core/src/file-inspector.ts · c39e106e · The inspection method and static Layer
    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);
    });

    return { inspect } as const;
  }),
}) {
  static readonly layer = Layer.effect(this, this.make);
}

Tests supply another implementation

The inspection model test supplies a controlled FileInspector. Its fake waits for a result chosen by the test. One provideService replaces the implementation.

packages/core/test/inspect-machine.model.test.ts · c39e106e · A controlled FileInspector
  const inspector = FileInspector.of({
    inspect: (path) => {
      requested.push(path);

      return Deferred.await(result);
    },
  });

  const actor = yield* createEffectActor(inspectMachine, {
    input: { path: scenario.stats.path },
  }).pipe(Effect.provideService(FileInspector, inspector));

Effect.provide supplies a Layer or Context. Effect.provideService supplies one service directly. Both remove the supplied service from the requirements type.

Follow the dependency injection path

  1. Read context and requirements9.
  2. Read services that capture dependencies16.
  3. Read the Layer constructor pattern12.
  4. Read explicit Layer composition13.
  5. Read configuration as a dependency7.
  6. Read tests through supplied services22.

Sources

  1. Matt Pocock in conversation with Joel Hooks. Recorded 2026-10-05. Recorded conversation; quoted with permission.
  2. Dillon Mulroy, recorded for Joel Hooks's AI Engineer World's Fair 2026 workshop. Recorded 2026-06-29. Recorded workshop contribution; quoted with permission.
  3. Effect contributors. Effect 4.0.0 requirements and service provision.. Effect-TS. The R type tracks requirements; provide and provideService remove supplied requirements. Accessed 2026-10-07.
  4. Effect contributors. Effect 4.0.0 Layers.. Effect-TS. Layers construct services, compose requirements, and share construction through a MemoMap. Accessed 2026-10-07.
  5. rat-stack FileInspector.. rat-stack. The service captures FileSystem during construction and supplies a static layer. Accessed 2026-10-07.
  6. rat-stack inspection model test.. rat-stack. The test replaces FileInspector with a controlled implementation through provideService. Accessed 2026-10-07.

Last changed 2026-10-07 in 0c4cafb. 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.

    Read why dependency injection matters.¶

  2. A service captures its construction dependencies. Build a service with its dependencies, then expose methods that callers can use without rebuilding the graph.

    Read why dependency injection matters.¶

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

    Dependency injection is the reason to choose Effect — Effect tracks service requirements in types and supplies replaceable implementations through Layers.

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

    Dependency injection is the reason to choose Effect — edited: feat(learn): teach the next twenty cards with plain lines, diagrams and snippets

  5. Configuration is a supplied, validated dependency. Parse configuration when a Layer builds, reject invalid values, and supply controlled settings in tests.

    Read why dependency injection matters.¶

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

    Read why dependency injection matters.¶

  7. Effect Days 2024: The origin story. Michael Arnaldi traces Effect to a TypeScript team's needs for tests, dependencies, and resource safety.

    He says dependency injection was the missing way to replace dependencies in tests.

  8. Effect's design lineage. Michael Arnaldi describes Effect's path from financial software needs through ZIO influence and TypeScript iterations.

    Dependency injection supplies service implementations to the code that needs them.

  9. Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
  10. Layer composition makes dependencies explicit. Supply a service's dependencies, choose which services remain visible, and reuse Layer identity within a build.

    Read why dependency injection matters.¶

  11. Michael Arnaldi. Michael Arnaldi develops Effect and explains its TypeScript design in public talks.

    Dependency injection supplies service implementations to callers.

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

    Dependency injection is the reason to choose Effect — Effect tracks service requirements in types and supplies replaceable implementations through Layers.

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

    Dependency injection is the reason to choose Effect — edited: feat(learn): teach the next twenty cards with plain lines, diagrams and snippets

  14. Tests prove behavior through supplied services. Supply services through Layers, choose properties or models for domain rules, and reject tests that cannot detect broken behavior.

    Read why dependency injection matters.¶