Health Analyzers
Four project-wide analyzers surface structural problems that single-file lints can’t see: errors that drift across program boundaries, services that are required but never provided, performance patterns that look fine locally but cost you under load, and modules that have quietly turned into god objects everything imports.
All four feed into the agent report — you can run them standalone for a quick health check, or together for a complete project snapshot. Each supports --format json for scripting and --output <path> to write to a file.
Error channel
Section titled “Error channel”effect-analyze ./src --error-channelCross-file analysis of how errors flow through your typed channels. Detects:
| Type | What it catches |
|---|---|
generic-error | Program signature has unspecific E type instead of a concrete tagged error |
unhandled-error | A tagged error is produced somewhere in the project but never handled |
missing-catch-tag | A program calls another that fails with FooError without a matching catchTag('Foo', …) |
error-type-widening | An error gets re-thrown as a broader supertype, losing typing |
no-error-handlers | A program is reachable from an entry point but has no error handlers anywhere on the path |
Each issue carries program name, file path, error type, severity (error / warning / info), and a concrete remediation suggestion.
Example output
Section titled “Example output”$ effect-analyze ./src --error-channel
unhandled-error: PaymentFailedError produced in: src/payments/charge.ts:42 (chargeCard) reachable from entry point: src/server.ts (httpServer) never handled by Effect.catchTag or matched in Cause/Exit → add Effect.catchTag('PaymentFailed', handle) in the server boundary
generic-error: src/notify/sendEmail.ts (sendEmail) signature: Effect<…, E, R> → narrow E to a tagged error union (SmtpError | InvalidRecipientError)Service health
Section titled “Service health”effect-analyze ./src --service-healthProject-wide audit of Context.Tag services and their Layer providers. Detects:
| Type | What it catches |
|---|---|
unsatisfied | A service is required (yield* SomeService) but no Layer provides it anywhere reachable |
dead-service | A Layer provides a service that no program ever consumes |
layer-inefficiency | Multiple separate Layer.provide calls that could be merged into one |
duplicate-provide | Same service provided twice in a Layer.merge chain — the second silently wins |
Useful for finding “this service is defined but nothing uses it anymore” rot, and for catching missing wiring before the runtime tells you.
Performance
Section titled “Performance”effect-analyze ./src --performancePerformance anti-pattern detector. Catches things that fail load tests, not unit tests:
| Type | Impact | What it catches |
|---|---|---|
sequential-could-parallel | medium / high | Multiple independent yield* in a row where Effect.all([…], { concurrency }) would be faster |
unbounded-concurrency | high | Effect.all([…], { concurrency: "unbounded" }) on large collections — DoS your own DB |
n-plus-one | high | Looping over a collection and calling a service inside — classic ORM-style N+1 |
missing-batching | medium | Sequential service calls that could go through a RequestResolver batch |
large-gen-block | low | A single Effect.gen with > 50 yields — readability / refactor signal |
unbounded-retry | high | Effect.retry(p, Schedule.forever) with no upper bound |
forEach-sequential | medium | Effect.forEach with no concurrency option on a meaningful collection |
Each issue carries program name, file, line, severity, suggestion, and an estimatedImpact field (low / medium / high).
Coupling
Section titled “Coupling”effect-analyze ./src --couplingPer-file dependency-graph metrics. Computes fan-in (how many files import this one) and fan-out (how many internal files this one imports) across the project, then surfaces files that are quietly becoming god modules.
| Type | What it catches |
|---|---|
high-fanin | A file with fan-in ≥ 15 — changes to it ripple to many dependents |
critical-fanin | A file with fan-in ≥ 30 — a true hub; refactoring is expensive |
high-fanout | A file importing ≥ 20 internal modules — broad scope, hard to test in isolation |
Imports are parsed via the TypeScript AST, so import X, import type X, export { x } from, import('…') dynamic imports, and side-effect import './foo' are all counted; node_modules and node: imports are skipped.
Annotating intentional hubs
Section titled “Annotating intentional hubs”Some files are hubs on purpose — central type definitions, a public API entry point, a service registry. Annotate them in-source to suppress the warning while keeping them on the radar:
/** * Central IR type definitions imported by every analyzer. * * @known-hub central type registry */Or as a single-line marker if you don’t have a JSDoc:
// effect-analyzer-known-hub central type registryThe annotation is grep-able, lives next to the code, and carries a written reason. Annotated hubs stop generating high-fanin issues but still appear in the report as monitored hubs — so if one quietly doubles in fan-in, you’ll see it.
Example output
Section titled “Example output”$ effect-analyze ./src --coupling
# Module Coupling Analysis
## Summary- High fan-in files: 1- Critical fan-in files: 0- Known hubs (annotated): 2
## Issues🟡 [high-fanin] `notifications/index.ts` File "notifications/index.ts" has fan-in 18 (high) — changes affect 18 dependents 💡 Consider reducing the import surface. Add a `@known-hub <reason>` JSDoc tag if this is intentional.Use them together
Section titled “Use them together”For a single-command project health snapshot:
effect-analyze ./src \ --error-channel \ --service-health \ --performance \ --coupling \ --format json \ -o health.jsonOr feed the same flags into --agent-report to produce a prioritized backlog with concrete remediation steps:
effect-analyze ./src \ --agent-report \ --error-channel \ --service-health \ --performance \ --coupling \ -o backlog.mdRelated
Section titled “Related”- Improve Mode & Agent Report — consume health output into a prioritized plan
- Strict Diagnostics — IR-level structural diagnostics (complementary)
- CLI Reference — flags