Skip to content

Migration Assistant

The migration assistant scans your TypeScript files for patterns that have idiomatic Effect equivalents. It helps teams plan and execute a gradual migration to Effect by identifying exactly where imperative or Promise-based code could be replaced.

Terminal window
npx effect-analyze ./src/legacy-service.ts --format migration
Terminal window
npx effect-analyze ./src --format migration

This scans all TypeScript files in the directory and produces a consolidated report of migration opportunities.

The migration assistant detects the following patterns and suggests their Effect equivalents:

Pattern Suggestion
try/catch Effect.try or Effect.tryPromise with catch handler
Promise.all Effect.all([...], { concurrency: "unbounded" })
Promise.race Effect.race(first, second)
setTimeout Effect.sleep(Duration.millis(n))
setInterval Schedule.spaced(Duration.millis(n))
setImmediate Effect.sync + queueMicrotask or Effect.callback
fetch() effect/unstable/http HttpClient
http.request / https.request effect/unstable/http HttpClient
new XMLHttpRequest() effect/unstable/http HttpClient
dns.lookup / dns.resolve Effect.promise or dns.promises
fs.exists (callback) Effect.promise or fs.promises.access
new EventEmitter() PubSub.bounded<T>() or PubSub.unbounded<T>()
new Worker() effect/unstable/workers Worker
Class-based DI (*Service, *Repository, *Client) Context.Service<Self, Shape>()('Key') + Layer.effect or Layer.succeed

The migration report includes:

  • File path and line number for each opportunity
  • Pattern name - what was detected
  • Suggestion - the Effect equivalent
  • Code snippet - the first 80 characters of the matching code

Example output:

## Migration Opportunities
Found 7 opportunities across 3 files.
### src/legacy-service.ts
| Line | Pattern | Suggestion |
|------|---------|------------|
| 12 | try/catch | Effect.try or Effect.tryPromise with catch handler |
| 28 | Promise.all | Effect.all([...], { concurrency: "unbounded" }) |
| 45 | fetch() | effect/unstable/http HttpClient |
### src/utils/timer.ts
| Line | Pattern | Suggestion |
|------|---------|------------|
| 5 | setTimeout | Effect.sleep(Duration.millis(n)) |
| 18 | setInterval | Schedule.spaced(Duration.millis(n)) |
import { findMigrationOpportunities } from "effect-analyzer/migration"
const opportunities = findMigrationOpportunities("./src/legacy-service.ts")
for (const opp of opportunities) {
console.log(`${opp.filePath}:${opp.line} - ${opp.pattern}${opp.suggestion}`)
}
import { findMigrationOpportunitiesInProject, formatMigrationReport } from "effect-analyzer/migration"
const report = findMigrationOpportunitiesInProject("./src")
console.log(`Found ${report.opportunities.length} opportunities across ${report.fileCount} files`)
const markdown = formatMigrationReport(report)
console.log(markdown)

Pass a source string directly for testing or integration:

import { findMigrationOpportunities } from "effect-analyzer/migration"
const opportunities = findMigrationOpportunities("virtual.ts", `
async function fetchUser(id: string) {
try {
const response = await fetch(\`/api/users/\${id}\`)
return response.json()
} catch (error) {
console.error(error)
throw error
}
}
`)
// Detects: try/catch, fetch()