Agent skill

effect-advanced

Advanced Effect-TS patterns for typed errors, dependency injection, concurrency, resource management, schema validation, and streaming. Use when building Effect programs — not simple Effect.succeed/fail questions, but multi-concern tasks like designing service layers with Layer composition, handling typed error hierarchies with tagged errors, managing concurrent fibers with structured concurrency, scoped resource lifecycles, schema-driven API contracts, or integrating Effect with existing Express/Hono/database stacks. Do not use for basic TypeScript or general functional programming questions.

Stars 3
Forks 0

Install this agent skill to your Project

npx add-skill https://github.com/trancong12102/agentskills/tree/main/effect-advanced

SKILL.md

Effect Advanced: Patterns, Conventions & Pitfalls

This skill defines the rules, conventions, and architectural decisions for building production Effect-TS applications. It is intentionally opinionated to prevent common pitfalls and enforce patterns that scale.

For detailed API documentation, use other appropriate tools (documentation lookup, web search, etc.) — this skill focuses on how and why to use Effect idiomatically, not the full API surface.

Table of Contents

  1. Core Conventions
  2. Error Handling Philosophy
  3. Dependency Injection Architecture
  4. Resource & Scope Rules
  5. Concurrency Model
  6. Common Pitfalls
  7. Reference Files

Core Conventions

Use Effect.gen for business logic

Generators read like synchronous code and are strongly preferred over long .pipe / .flatMap chains for anything beyond trivial composition:

typescript
const program = Effect.gen(function* () {
  const config = yield* ConfigService;
  const user = yield* UserRepo.findById(config.userId);
  return user;
});

Reserve pipe for data transformation pipelines and short combinator chains.

Never throw — use Effect's error channel

Instead of... Use
throw new Error() Effect.fail(new MyError())
try/catch on promises Effect.tryPromise({ try, catch })
Callback APIs Effect.async((resume) => ...)
Unrecoverable crashes Effect.die(defect)

Functions over methods

Prefer Effect.map(e, f) over e.pipe(Effect.map(f)) for composability and tree-shaking. Flat imports (import { Effect } from "effect") are fine for applications; namespace imports (import * as Effect from "effect/Effect") are better for libraries.

@effect/schema is deprecated

Schema has been merged into core effect. Import from "effect" directly:

typescript
import { Schema } from "effect";
// NOT: import { Schema } from "@effect/schema"

Use NodeRuntime.runMain in production

Effect.runPromise does not handle SIGINT/SIGTERM gracefully:

typescript
import { NodeRuntime } from "@effect/platform-node";
NodeRuntime.runMain(program.pipe(Effect.provide(AppLayer)));

Error Handling Philosophy

Failures vs defects — the fundamental distinction

Aspect Failure (expected) Defect (unexpected)
API Effect.fail(new MyError()) Effect.die(new Error())
Type channel Tracked in E Never appears in E (never)
Recovery catchTag, catchAll, retry Only at system boundaries
Rule of thumb You intend to handle it at call site Bug or impossible state

Always use tagged errors

Plain Error or string failures miss the value of Effect's typed error channel:

typescript
class UserNotFound extends Data.TaggedError("UserNotFound")<{
  readonly id: string;
}> {}

// Tagged errors are yieldable — no Effect.fail wrapper needed
const program = Effect.gen(function* () {
  const user = yield* db.findUser(id);
  if (!user) yield* new UserNotFound({ id });
  return user;
});

catchAll does NOT catch defects

This is the #1 error handling mistake:

typescript
Effect.catchAll(program, handler); // catches E only — NOT defects
Effect.catchAllCause(program, handler); // catches everything (E + defects + interrupts)

Only use catchAllCause / catchAllDefect at system boundaries (top-level error handlers, HTTP response mappers).


Dependency Injection Architecture

Service → Layer → Provide (once)

text
1. Define services with Context.Tag  →  "what do I need?"
2. Implement via Layers              →  "how is it built?"
3. Provide once at entry point       →  "wire it all together"

Service methods must have R = never

Dependencies belong in Layer composition, not method signatures:

typescript
// WRONG: leaks dependency to callers
findById: (id: string) => Effect.Effect<User, UserNotFound, Database>;

// RIGHT: Database is wired in the Layer
findById: (id: string) => Effect.Effect<User, UserNotFound>;

Layer composition — know the operators

Operation When Behavior
Layer.merge(A, B) Independent services Both build concurrently
Layer.provide(downstream, upstream) A feeds B upstream builds first
Layer.fresh(layer) Force new instance Bypasses memoization

Critical: Layer.merge does NOT sequence construction. If B depends on A, use Layer.provide, not Layer.merge.

One Effect.provide at the entry point

Scattered provide calls create hidden dependencies and layer duplication:

typescript
// WRONG: provide scattered throughout codebase
const getUser = UserRepo.findById(id).pipe(Effect.provide(DbLayer));

// RIGHT: compose and provide once
const main = program.pipe(Effect.provide(AppLayer));
NodeRuntime.runMain(main);

Resource & Scope Rules

Effect.scoped is mandatory for acquireRelease

Forgetting Effect.scoped is the #1 resource management pitfall — resources accumulate until the program exits:

typescript
// WRONG: scope never closes, connection leaks
const result = yield * getDbConnection;

// RIGHT: scope closes when block completes
const result =
  yield *
  Effect.scoped(
    Effect.gen(function* () {
      const conn = yield* getDbConnection;
      return yield* conn.query("SELECT 1");
    }),
  );

Release finalizers always run

On success, failure, AND interruption — guaranteed. The finalizer receives the Exit value for conditional cleanup.

Multiple resources in one scope

typescript
Effect.scoped(
  Effect.gen(function* () {
    const conn = yield* Effect.acquireRelease(openConn(), closeConn);
    const file = yield* Effect.acquireRelease(openFile(), closeFile);
    // both released when scope closes, in REVERSE acquisition order
  }),
);

Concurrency Model

Prefer high-level APIs over raw fork

API Use case
Effect.all([], { concurrency: N }) Bounded parallel execution
Effect.forEach(items, fn, { concurrency: N }) Worker pool pattern
Effect.race(a, b) First to complete wins, others interrupted
Effect.timeout(e, dur) Deadline on any effect

Only reach for Effect.fork / Fiber when high-level APIs are insufficient.

Fork variants — know the lifecycle

Function Scope Cleanup
Effect.fork Parent's scope Auto-interrupted with parent
Effect.forkDaemon Global scope Nothing cleans it up — you must
Effect.forkScoped Nearest Scope Tied to resource lifecycle

Gotcha: forkDaemon leaks fibers if you forget to interrupt them.


Common Pitfalls

  1. Floating effects — creating an Effect without yielding or running it is a silent bug. Effect.log("msg") inside a generator does nothing unless yield*-ed.

  2. catchAll won't catch defects — use catchAllCause at system boundaries for full failure visibility.

  3. Missing Effect.scopedacquireRelease without a scope boundary leaks resources until program exit.

  4. Scattered Effect.provide — compose all layers and provide once at the entry point.

  5. Point-free on overloaded functionsEffect.map(myOverloadedFn) silently erases generics. Use explicit lambdas: Effect.map((x) => myOverloadedFn(x)).

  6. Effect.async resume called multiple times — resume must be called exactly once. Multiple calls cause undefined behavior.

  7. orDie silences errors — converts typed failures to untyped defects. Handle errors properly instead.

  8. Layer.merge for dependent services — merge doesn't sequence construction. Use Layer.provide when one layer needs another's output.

  9. Fiber.join vs Fiber.awaitjoin can cause premature finalizer execution in edge cases. Prefer await when resource safety matters.

  10. runCollect on infinite streams — never call without a prior take. It will never terminate and consume unbounded memory.

  11. Using it.effect for scoped tests — effects requiring Scope must use it.scoped, not it.effect, or you get a type error.


Reference Files

Read the relevant reference file when working with a specific concern:

File When to read
references/error-handling.md Tagged errors, Cause, defect recovery, error mapping patterns
references/dependency-injection.md Services, Layers, composition, memoization, provide patterns
references/concurrency.md Fibers, fork variants, Deferred, Semaphore, structured concurrency
references/resource-management.md Scope, acquireRelease, Layer resources, fork + scope interaction
references/schema.md Schema definition, transforms, branded types, recursive schemas
references/stream.md Stream operators, chunking, backpressure, resourceful streams
references/testing.md @effect/vitest, TestClock, Layer mocking, Config mocking
references/platform.md HTTP client, FileSystem, Command, runtime, framework integration

Expand your agent's capabilities with these related and highly-rated skills.

trancong12102/agentskills

deps-dev

Look up the latest stable version of any open-source package across npm, PyPI, Go, Cargo, Maven, and NuGet. Use when the user asks 'what's the latest version of X', 'what version should I use', 'is X deprecated', 'how outdated is my package.json/requirements.txt/Cargo.toml', or needs version numbers for adding or updating dependencies. Also covers pinning versions, checking if packages are maintained, or comparing installed vs latest versions. Do NOT use for private/internal packages or for looking up documentation (use context7).

3 0
Explore
trancong12102/agentskills

github-codebase-search

Semantic search for public GitHub repos without cloning. Use when the user wants to understand how an external library or framework works internally, investigate upstream bugs, trace code paths in a repo they haven't cloned, or search GitHub source code by intent. Do NOT use for local codebase questions (use codebase-search), documentation lookup (use context7), or private repos.

3 0
Explore
trancong12102/agentskills

council-review

Multi-model AI code review — runs Codex, Claude, and Simplify reviews in parallel, then synthesizes a unified report. Use when the user asks to review code changes, audit a diff, check code quality, review a PR, review commits, or review uncommitted changes. Also covers 'code review', 'review my changes', 'check this before I merge', or wanting multiple perspectives on code. Do NOT use for documentation/markdown review or trivial single-line changes.

3 0
Explore
trancong12102/agentskills

react-native-advanced

React Native and Expo patterns for navigation, data fetching lifecycle, infinite scroll lists, form handling, state persistence, authentication routing, gesture-driven animations, bottom sheets, push notifications, and OTA updates. Use when building Expo/React Native apps that need screen-level data prefetching, auth guards with protected routes, infinite scroll feeds, native form input handling, offline-capable state persistence, platform-specific setup (focus/online managers), fluid animations and gesture interactions, modal bottom sheets, push notification flows, or over-the-air update strategies. Do not use for React web apps.

3 0
Explore
trancong12102/agentskills

react-web-advanced

Web-specific React patterns for type-safe file-based routing, route-level data loading, server-side rendering, search param validation, code splitting, and list virtualization. Use when building React web apps with route loaders, SSR streaming, validated search params, lazy route splitting, or virtualizing large DOM lists. Do not use for React Native apps — use react-native-advanced instead.

3 0
Explore
trancong12102/agentskills

context7

Fetch up-to-date documentation for any open-source library or framework. Use when the user asks to look up docs, check an API, find code examples, or verify how a feature works — especially with a specific library name, version migration, or phrases like 'what's the current way to...' or 'the API might have changed'. Also covers setup and configuration docs. Do NOT use for general programming concepts, internal project code, or version lookups (use deps-dev).

3 0
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results