effect-incremental-migration

Migrate async Promise-based TypeScript modules to Effect services with backward-compatible facades.

1|Updated Aug 24, 2026
One-click install
npx skills add https://github.com/lambdasolver2/opencode-effect-harness --skill effect-incremental-migration-lambdasolver2
Or copy as Structured Prompt for Agent▼
Please help me install this Agent Skill.
Skill: effect-incremental-migration
Source: https://github.com/lambdasolver2/opencode-effect-harness/tree/main/packages/module-typescript/assets/skills/effect-incremental-migration
Command: npx skills add https://github.com/lambdasolver2/opencode-effect-harness --skill effect-incremental-migration-lambdasolver2

SYSTEM DOCUMENTATION & REQUIREMENTS

💡 This Skill requires effect.

What problem does it solve? Converting an existing Promise/async codebase to Effect all at once is risky and disruptive. This Skill provides a structured, incremental path that keeps non-Effect callers working while Effect callers move onto typed services early. ## Core Features & Use Cases - Step-by-step migration template: Eight ordered steps covering interface extraction, Context.Service declaration, Layer.effect construction, default layer wiring, ManagedRuntime bridging, async facades, caller updates, and facade pruning. - Backward compatibility bridge: A shared memoMap-based runtime bridge exposes runPromise/runSync so legacy async callers keep working during the transition. - Common transformations: Concrete before/after patterns such as replacing Promise.all fan-out with Effect.forEach and converting catchDefect handlers to typed catch/catchTag. - Use Case: You have an Items module with async get/list functions used across the app. Follow the template to create an Items.Service with a layer, keep thin async facades for old callers, migrate Effect callers to yield* Items.Service, then prune the dead facades in a separate commit. ## Quick Start Ask the AI to migrate a specific async module, such as 'convert my Items module to an Effect service while keeping the existing async functions working for legacy callers'.

Frequently Asked Questions about effect-incremental-migration

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I migrate async TypeScript code to Effect incrementally?▼

Extract an interface with Effect-returning methods, declare a Context.Service class, build a Layer.effect capturing dependencies, and bridge to legacy callers via a ManagedRuntime with runPromise. Migrate Effect callers to yield* the service directly, then prune dead facades in a separate commit.

How to keep backward compatibility when converting Promise APIs to Effect?▼

Keep thin async facade functions that delegate to a runtime bridge's runPromise method. These facades serve remaining non-Effect callers only; once all callers yield the service directly, delete the facades and bridge in a dedicated cleanup commit.

How do I replace Promise.all with Effect concurrency?▼

Replace Effect.promise wrapping Promise.all(items.map(...)) with Effect.forEach(items, fn, { concurrency: 'unbounded' }). This removes the Effect.promise wrapper entirely and gives explicit control over concurrency behavior.

Why use a shared memoMap with ManagedRuntime in Effect?▼

A shared memoMap from Layer.makeMemoMapUnsafe deduplicates layer allocations across all per-service runtimes. Forking the memo map isolates new allocations and defeats the deduplication the runtime bridge exists to provide.

What happens to error handling when migrating from Effect.promise to services?▼

Errors that previously flowed as defects through Effect.promise become typed channel errors when calling services directly. Update existing catchDefect handlers to catch or catchTag so the now-typed errors are handled in the error channel.