skills/clementwalter/claudine/effect-solutions

effect-solutions

SKILL.md

Effect Solutions

Field manual for idiomatic Effect-TS code. All reference files are in references/.

Reference Index

Load the relevant file(s) based on the task at hand:

Task Reference File
Project setup (editor, language service, tsconfig) references/01-project-setup.md, references/02-tsconfig.md
Core patterns: Effect.gen, Effect.fn, pipe, retry, timeout references/03-basics.md
Services (Context.Tag) and Layers (DI, lifecycle, test layers) references/04-services-and-layers.md
Data modeling: Schema.Class, branded types, JSON encode/decode references/05-data-modeling.md
Error handling: Schema.TaggedError, catchTag, defects references/06-error-handling.md
Config loading: Config, Schema.Config, Redacted, test overrides references/07-config.md
Testing: @effect/vitest, it.effect, TestClock, test layers references/08-testing.md
HTTP clients: @effect/platform, typed REST clients, middleware references/11-http-clients.md
Observability: OpenTelemetry, spans, OTLP export references/12-observability.md
CLI building: @effect/cli, commands, args, options, subcommands references/13-cli.md
Wrapping Promise-based libraries (Prisma, AWS SDK, etc.) references/14-use-pattern.md

Quick-Start Patterns

Service + Layer (most common pattern)

class MyService extends Context.Tag("@app/MyService")<
  MyService,
  { readonly doThing: (input: string) => Effect.Effect<Result, MyError> }
>() {
  static readonly layer = Layer.effect(
    MyService,
    Effect.gen(function* () {
      const dep = yield* OtherService;
      const doThing = Effect.fn("MyService.doThing")(function* (input) {
        return yield* dep.use(input);
      });
      return MyService.of({ doThing });
    }),
  );

  static readonly testLayer = Layer.succeed(MyService, {
    doThing: (_) => Effect.succeed(mockResult),
  });
}

Tagged Error

class MyError extends Schema.TaggedError<MyError>()("MyError", {
  message: Schema.String,
}) {}

// Usage: yield* new MyError({ message: "..." })
// Recovery: effect.pipe(Effect.catchTag("MyError", (e) => ...))

Effect.fn (named, traced)

const processUser = Effect.fn("processUser")(function* (userId: string) {
  const user = yield* getUser(userId);
  return yield* transform(user);
});

Test with @effect/vitest

import { it } from "@effect/vitest";

it.effect("description", () =>
  Effect.gen(function* () {
    const result = yield* myEffect;
    expect(result).toBe(expected);
  }).pipe(Effect.provide(MyService.testLayer)),
);

Key Decisions

  • Service vs plain function: Use a service when you need DI, shared state, or testable boundaries; plain Effect.fn otherwise.
  • Layer.effect vs Layer.sync: Use Layer.effect when initialization requires effects (DB connect, config read); Layer.sync for pure in-memory setup.
  • Schema.TaggedError vs Effect.orDie: Typed errors for recoverable domain failures; orDie for invariant violations (bugs).
  • Per-test vs suite-shared layers: Default to per-test (clean state); use it.layer only for expensive resources.
Weekly Installs
1
GitHub Stars
1
First Seen
6 days ago
Installed on
zencoder1
amp1
cline1
openclaw1
opencode1
cursor1