effect-ts
Installation
SKILL.md
Effect-TS Developer Guide
Guidelines, patterns, and best practices for Effect-TS in this project.
Reference Documents
Read the relevant reference before writing code. references/core-patterns.md is the master index.
| Reference | Topics |
|---|---|
references/foundations.md |
Setup, imports, TypeScript config |
references/construction-and-style.md |
Effect.gen, pipe, Effect.fn, Effect.fnUntraced |
references/schema-errors-config.md |
Schema modeling, errors, config, retry |
references/pattern-matching.md |
Match.type, Match.value, Match.tag, Match.exhaustive — mandatory for tagged unions |
references/control-flow-and-runtime.md |
Effect.if, Effect.when, loops, runSync, runPromise, ManagedRuntime |
references/data-types.md |
All data types: Option, Either, Data, Exit, Cause, Duration, DateTime, BigDecimal, Chunk, HashSet, Redacted |
references/data-and-testing.md |
Option/Either/Array quick ref, @effect/vitest setup |
references/concurrency-and-resources.md |
Concurrency, Scope, finalizers, resources |
references/streams-deep-dive.md |
Creating, operations, grouping, partitioning, broadcasting, buffering, throttling, error handling |
references/sink.md |
Sink constructors, collecting, folding, operations, concurrency, leftovers, Stream.transduce |
references/batching-and-caching.md |
Request batching (RequestResolver), cachedWithTTL |
references/schema-transforms-and-filters.md |
Schema.transform, Schema.filter, refinements |
references/api-platform-observability.md |
HttpApi, logging, tracing, spans |
references/class-patterns.md |
Context.Tag service pattern, layers, memoization, testing |
references/error-handling-patterns.md |
Data.TaggedError, Schema.TaggedError, error composition, recovery |
references/library-development-patterns.md |
Forbidden patterns, Effect.fn vs Effect.fnUntraced, resource management |
references/testing-patterns.md |
@effect/vitest with assert, TestClock, service mocking |
references/quality-tooling-and-resources.md |
Anti-patterns, validation checklist, packages |
Core Principles
- Effect is not just for async — Use
Effectfor any fallible operation - Immutability — Use Effect's immutable data structures (
Data,Chunk,HashSet) - Type Safety — Track errors in types. No
anyorunknownin error channels - Composition — Build programs by composing small Effects
- Schema-First — Define data models using
Schemawith branded types - Pattern Matching — Use
Matchfor all branching over tagged unions (neverswitch/if-elseon_tag) - Effect Data Types — Use
Option(not null),Either(not ad-hoc),Duration(not raw ms),DateTime(not Date),BigDecimal(not floats),Redacted(for secrets). Seereferences/data-types.md
Quick Reference
Import Convention
import * as Context from "effect/Context";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Schema from "effect/Schema";
import * as Match from "effect/Match";
import * as Option from "effect/Option";
import * as Either from "effect/Either";
import * as Data from "effect/Data";
import * as Duration from "effect/Duration";
import { pipe } from "effect/Function";
// Also: DateTime, BigDecimal, Chunk, HashSet, Exit, Cause, Redacted
Effect.gen vs pipe vs Effect.fn
// Effect.gen — complex logic with branching
Effect.gen(function* () {
const user = yield* fetchUser(id);
if (user.isAdmin) yield* logAdminAccess(user);
return user;
});
// pipe — linear transformations
pipe(fetchData(), Effect.map(transform), Effect.flatMap(save));
// Effect.fn — traced reusable functions (public API)
const processUser = Effect.fn("processUser")(function* (userId: string) {
const user = yield* getUser(userId);
return yield* processData(user);
});
Error Handling
Data.TaggedError for in-process discrimination, Schema.TaggedError for serializable errors. See references/error-handling-patterns.md.
export class NotFoundError extends Data.TaggedError("NotFoundError")<{
id: string;
}> {}
// Recovery
pipe(riskyOp, Effect.catchTag("NotFoundError", (e) => Effect.succeed(null)));
Pattern Matching (Mandatory for Tagged Unions)
Always use Match — Match.exhaustive catches missing cases at compile time. See references/pattern-matching.md.
// Match.type — reusable matcher function
const handle = Match.type<Status>().pipe(
Match.tag("Pending", (s) => `Pending since ${s.requestedAt}`),
Match.tag("Approved", (s) => `Approved by ${s.approvedBy}`),
Match.exhaustive // Compile error if any variant is missing
);
// Match.valueTags — shorthand for immediate matching
Match.valueTags(status, {
Pending: (s) => `Pending since ${s.requestedAt}`,
Approved: (s) => `Approved by ${s.approvedBy}`,
});
Service Pattern (Context.Tag)
See references/class-patterns.md for full pattern with factory methods and layers.
export class MyService extends Context.Tag("@myapp/MyService")<
MyService,
{ readonly find: (id: string) => Effect.Effect<Result, NotFoundError> }
>() {
static readonly layer = Layer.effect(MyService, Effect.gen(function* () {
const db = yield* Database;
return MyService.of({ find: MyService.createFind(db) });
}));
}
Testing
CRITICAL: Use assert from @effect/vitest for it.effect. Never expect with it.effect. See references/testing-patterns.md.
import { assert, describe, it } from "@effect/vitest";
it.effect("processes data", () =>
Effect.gen(function* () {
const result = yield* processData("input");
assert.strictEqual(result, "expected");
}).pipe(Effect.provide(MyService.testLayer))
);
Forbidden Patterns
// NEVER: try-catch in Effect.gen — use Effect.exit instead
Effect.gen(function* () {
try { yield* someEffect } catch (e) { } // WRONG — will never catch
});
// NEVER: Type assertions
const value = something as any; // FORBIDDEN
const value = something as never; // FORBIDDEN
// NEVER: Missing return on terminal yield
Effect.gen(function* () {
if (bad) { yield* Effect.fail("err") } // Missing return!
});
// NEVER: switch/if-else on _tag — use Match instead
switch (status._tag) { /* no exhaustiveness checking! */ }
// NEVER: Effect.runSync inside Effects
Effect.gen(function* () { Effect.runSync(sideEffect) }); // Loses error tracking
// NEVER: Native JS where Effect data types exist
const x: string | null = null; // Use Option<string>
const delay = 5000; // Use Duration.seconds(5)
const now = new Date(); // Use DateTime.now or DateTime.unsafeNow()
const price = 0.1 + 0.2; // Use BigDecimal for precision
const secret = "sk-1234"; // Use Redacted.make("sk-1234")
// NEVER: expect with it.effect
it.effect("test", () => Effect.gen(function* () {
expect(result).toBe(value) // WRONG — use assert.strictEqual
}));
// NEVER: Inline layers (breaks memoization)
Layer.provide(Postgres.layer({ url })) // Store in constant instead
Validation Checklist
- Imports use
import * as Module from "effect/Module" -
Effect.genfor complex logic,pipefor linear,Effect.fnfor public API -
Matchfor all_tagbranching withMatch.exhaustive - Branded types for domain primitives (IDs, Emails)
- Errors:
Data.TaggedError(discrimination) orSchema.TaggedError(serializable) - No
any/unknownin error channels, no type assertions - No try-catch in
Effect.gen— useEffect.exit -
return yield*for terminal effects (Effect.fail,Effect.interrupt) - Services:
Context.Tagwith static factory methods andEffect.fntracing - Layers:
Layer.merge/Layer.provide, parameterized layers in constants - Resources:
Effect.acquireReleaseorEffect.scoped -
Optionfor nullable values,Eitherfor sync success/failure -
Durationfor time values,DateTimefor dates (notDate) -
BigDecimalfor financial/precise math,Redactedfor secrets -
Data.struct/Data.Classfor structural equality,HashSetfor sets -
Clock.currentTimeMillisinstead ofDate.now() - Tests:
assertfrom@effect/vitest(notexpect) withit.effect - Run
pnpm run typecheckandpnpm run test
Reference Implementation
See /packages/looper/src/data/api-client/api-client.ts for Context.Tag service pattern.
Effect Solutions CLI
pnpm exec effect-solutions list # List all topics
pnpm exec effect-solutions show <slug...> # Read topics
pnpm exec effect-solutions search <term> # Search by keyword
Weekly Installs
40
Repository
pedronauck/skillsGitHub Stars
301
First Seen
Mar 18, 2026
Security Audits
Installed on
opencode40
gemini-cli40
github-copilot40
codex40
amp40
cline40