effect
SKILL.md
Effect-TS Best Practices
Opinionated patterns for Effect-TS codebases. Effect provides typed functional programming with composable errors, dependency injection, and observability.
Critical Rules
- NEVER use
anyor type casts (as Type) - UseSchema.make()for branded types,Schema.decodeUnknown()for parsing - Don't use
catchAllwhen error type isnever- No errors to catch - Never use global
Errorin Effect channels - UseSchema.TaggedErrorfor domain errors - Ban
{ disableValidation: true }- Lint against this - Don't wrap safe operations in Effect - Only use
Effect.try()for throwing operations - Use
mapErrornotcatchAllCause- Distinguish expected errors from bugs - Never silently swallow errors - Failures MUST be visible in the Effect's error channel E
Quick Reference
| Pattern | DON'T | DO |
|---|---|---|
| Service definition | Context.Tag |
Effect.Service with dependencies array |
| Error types | Generic Error |
Schema.TaggedError with context fields |
| Branded IDs | Raw string |
Schema.String.pipe(Schema.brand("@Ns/Entity")) |
| Running effects | runSync/runPromise in services |
Return Effect, run at edge |
| Logging | console.log |
Effect.log with structured data |
| Configuration | process.env |
Config with validation |
| Method tracing | Manual spans | Effect.fn("Service.method") |
| Nullable results | null/undefined |
Option types |
| State | Mutable variables | Ref |
| Time | Date.now(), new Date() |
Clock service |
Service Pattern
class UserService extends Effect.Service<UserService>()("UserService", {
dependencies: [DatabaseService.Default],
effect: Effect.gen(function* () {
const db = yield* DatabaseService
return {
findById: Effect.fn("UserService.findById")(
(id: UserId) => db.query(/* ... */)
),
}
}),
}) {}
// Usage - dependencies auto-provided
UserService.findById(userId)
Error Handling
// Define domain-specific errors
class UserNotFoundError extends Schema.TaggedError<UserNotFoundError>()(
"UserNotFoundError",
{ userId: UserId, message: Schema.String }
) {}
// Handle with catchTag (preserves type info)
effect.pipe(
Effect.catchTag("UserNotFoundError", (e) => /* handle */),
Effect.catchTag("AuthExpiredError", (e) => /* handle */)
)
Schema Pattern
// Branded ID
const UserId = Schema.String.pipe(Schema.brand("@App/UserId"))
// Domain entity with Schema.Class
class User extends Schema.Class<User>("User")({
id: UserId,
email: Schema.String,
createdAt: Schema.DateFromSelf,
}) {
get displayName() { return this.email.split("@")[0] }
}
Layer Composition
// Declare dependencies in service, not at usage
const MainLayer = Layer.mergeAll(
UserServiceLive,
AuthServiceLive,
DatabaseLive
)
// Run program
Effect.runPromise(program.pipe(Effect.provide(MainLayer)))
Detailed Guides
- Anti-Patterns - Forbidden patterns with fixes
- Error Patterns - Domain errors, rich context, HTTP mapping
- Schema Patterns - Branded types, transforms, validation
- Service Patterns - Effect.Service, dependency injection
- Layer Patterns - Composition, memoization, testing
- Observability Patterns - Logging, tracing, metrics
- SQL Patterns - Database integration, transactions
- Testing Patterns - effect-vitest, property testing, Testcontainers
- Atom Patterns - React state management with Effect
- RPC & Cluster Patterns - Distributed systems
Weekly Installs
1
Repository
smithery/aiFirst Seen
13 days ago
Installed on
amp1
opencode1
kimi-cli1
codex1
github-copilot1
gemini-cli1