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.