8000 GitHub - PaulJPhilp/EffectPatterns: A community-driven knowledge base of practical patterns for Effect-TS.
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

PaulJPhilp/EffectPatterns

Repository files navigation

The Effect Patterns Hub

A community-driven knowledge base of practical, goal-oriented patterns for building robust applications with Effect-TS.

This repository is designed to be a living document that helps developers move from core concepts to advanced architectural strategies by focusing on the "why" behind the code.

Looking for machine-readable rules for AI IDEs and coding agents? See the AI Coding Rules section below.

Table of Contents


Core Concepts

The absolute fundamentals of Effect. Start here to understand the core philosophy.

Pattern Skill Level Summary
Create Pre-resolved Effects with succeed and fail 🟒 Beginner Use Effect.succeed(value) to create an Effect that immediately succeeds with a value, and Effect.fail(error) for an Effect that immediately fails.
Solve Promise Problems with Effect 🟒 Beginner Understand how Effect solves the fundamental problems of native Promises, such as untyped errors, lack of dependency injection, and no built-in cancellation.
Transform Effect Values with map and flatMap 🟒 Beginner Use Effect.map for synchronous transformations and Effect.flatMap to chain operations that return another Effect.
Understand that Effects are Lazy Blueprints 🟒 Beginner An Effect is a lazy, immutable blueprint describing a computation, which does nothing until it is explicitly executed by a runtime.
Understand the Three Effect Channels (A, E, R) 🟒 Beginner Learn about the three generic parameters of an Effect: the success value (A), the failure error (E), and the context requirements (R).
Use .pipe for Composition 🟒 Beginner Use the .pipe() method to chain multiple operations onto an Effect in a readable, top-to-bottom sequence.
Wrap Asynchronous Computations with tryPromise 🟒 Beginner Use Effect.tryPromise to safely convert a function that returns a Promise into an Effect, capturing rejections in the error channel.
Wrap Synchronous Computations with sync and try 🟒 Beginner Use Effect.sync for non-throwing synchronous code and Effect.try for synchronous code that might throw an exception.
Write Sequential Code with Effect.gen 🟒 Beginner Use Effect.gen with yield* to write sequential, asynchronous code in a style that looks and feels like familiar async/await.
Conditionally Branching Workflows 🟑 Intermediate Use predicate-based operators like Effect.filter and Effect.if to make decisions and control the flow of your application based on runtime values.
Control Flow with Conditional Combinators 🟑 Intermediate Use combinators like Effect.if, Effect.when, and Effect.cond to handle conditional logic in a declarative, composable way.
Control Repetition with Schedule 🟑 Intermediate Use Schedule to create composable, stateful policies that define precisely how an effect should be repeated or retried.
Manage Shared State Safely with Ref 🟑 Intermediate Use Ref to model shared, mutable state in a concurrent environment, ensuring all updates are atomic and free of race conditions.
Process Streaming Data with Stream 🟑 Intermediate Use Stream<A, E, R> to represent and process data that arrives over time, such as file reads, WebSocket messages, or paginated API results.
Understand Layers for Dependency Injection 🟑 Intermediate A Layer is a blueprint that describes how to build a service, detailing its own requirements and any potential errors during its construction.
Use Chunk for High-Performance Collections 🟑 Intermediate Use Chunk as a high-performance, immutable alternative to JavaScript's Array, especially for data processing pipelines.
Understand Fibers as Lightweight Threads 🟠 Advanced A Fiber is a lightweight, virtual thread managed by the Effect runtime, enabling massive concurrency on a single OS thread without the overhead of traditional threading.

Project Setup & Execution

Getting started and running code, from simple scripts to long-running applications.

Pattern Skill Level Summary
Execute Asynchronous Effects with Effect.runPromise 🟒 Beginner Use Effect.runPromise at the 'end of the world' to execute an asynchronous Effect and get its result as a JavaScript Promise.
Execute Synchronous Effects with Effect.runSync 🟒 Beginner Use Effect.runSync at the 'end of the world' to execute a purely synchronous Effect and get its value directly.
Set Up a New Effect Project 🟒 Beginner Initialize a new Node.js project with the necessary TypeScript configuration and Effect dependencies to start building.
Create a Managed Runtime for Scoped Resources 🟠 Advanced Use Layer.launch to safely manage the lifecycle of layers containing scoped resources, ensuring finalizers are always run.
Create a Reusable Runtime from Layers 🟠 Advanced Compile your application's layers into a reusable Runtime object to efficiently execute multiple effects that share the same context.
Execute Long-Running Apps with Effect.runFork 🟠 Advanced Use Effect.runFork at the application's entry point to launch a long-running process as a detached fiber, allowing for graceful shutdown.

Application Configuration

Managing configuration from different sources in a type-safe and testable way.

Pattern Skill Level Summary
Access Configuration from the Context 🟑 Intermediate Access your type-safe configuration within an Effect.gen block by yielding the Config object you defined.
Define a Type-Safe Configuration Schema 🟑 Intermediate Use Effect.Config primitives to define a schema for your application's configuration, ensuring type-safety and separation from code.
Provide Configuration to Your App via a Layer 🟑 Intermediate Use Config.layer(schema) to create a Layer that provides your configuration schema to the application's context.

Error Management

Strategies for building resilient applications by treating failures as first-class citizens.

Pattern Skill Level Summary
Accumulate Multiple Errors with Either 🟑 Intermediate Use Either<E, A> to represent computations that can fail, allowing you to accumulate multiple errors instead of short-circuiting on the first one.
Conditionally Branching Workflows 🟑 Intermediate Use predicate-based operators like Effect.filter and Effect.if to make decisions and control the flow of your application based on runtime values.
Control Repetition with Schedule 🟑 Intermediate Use Schedule to create composable, stateful policies that define precisely how an effect should be repeated or retried.
Define Type-Safe Errors with Data.TaggedError 🟑 Intermediate Create custom, type-safe error classes by extending Data.TaggedError to make error handling robust, predictable, and self-documenting.
Distinguish 'Not Found' from Errors 🟑 Intermediate Use Effect<Option> to clearly distinguish between a recoverable 'not found' case (None) and a true failure (Fail).
Handle Errors with catchTag, catchTags, and catchAll 🟑 Intermediate Use catchTag for type-safe recovery from specific tagged errors, and catchAll to recover from any possible failure.
Handle Flaky Operations with Retries and Timeouts 🟑 Intermediate Use Effect.retry and Effect.timeout to build resilience against slow or intermittently failing operations, such as network requests.
Leverage Effect's Built-in Structured Logging 🟑 Intermediate Use Effect's built-in logging functions (Effect.log, Effect.logInfo, etc.) for structured, configurable, and context-aware logging.
Mapping Errors to Fit Your Domain 🟑 Intermediate Use Effect.mapError to transform specific, low-level errors into more general domain errors, creating clean architectural boundaries.
Model Optional Values Safely with Option 🟑 Intermediate Use Option to explicitly represent a value that may or may not exist, eliminating null and undefined errors.
Retry Operations Based on Specific Errors 🟑 Intermediate Use Effect.retry and predicate functions to selectively retry an operation only when specific, recoverable errors occur.
Handle Unexpected Errors by Inspecting the Cause 🟠 Advanced Use Effect.catchAllCause or Effect.runFork to inspect the Cause of a failure, distinguishing between expected errors (Fail) and unexpected defects (Die).

Domain Modeling

Building a type-safe, expressive model of your business logic.

Pattern Skill Level Summary
Accumulate Multiple Errors with Either 🟑 Intermediate Use Either<E, A> to represent computations that can fail, allowing you to accumulate multiple errors instead of short-circuiting on the first one.
Avoid Long Chains of .andThen; Use Generators Instead 🟑 Intermediate Prefer Effect.gen over long chains of .andThen for sequential logic to improve readability and maintainability.
Define Contracts Upfront with Schema 🟑 Intermediate Use Schema to define the types for your data models and function 8000 signatures before writing the implementation, creating clear, type-safe contracts.
Define Type-Safe Errors with Data.TaggedError 🟑 Intermediate Create custom, type-safe error classes by extending Data.TaggedError to make error handling robust, predictable, and self-documenting.
Distinguish 'Not Found' from Errors 🟑 Intermediate Use Effect<Option> to clearly distinguish between a recoverable 'not found' case (None) and a true failure (Fail).
Model Optional Values Safely with Option 🟑 Intermediate Use Option to explicitly represent a value that may or may not exist, eliminating null and undefined errors.
Model Validated Domain Types with Brand 🟑 Intermediate Use Brand to turn primitive types like string or number into specific, validated domain types like Email or PositiveInt, making illegal states unrepresentable.
Parse and Validate Data with Schema.decode 🟑 Intermediate Use Schema.decode(schema) to create an Effect that parses and validates unknown data, which integrates seamlessly with Effect's error handling.
Transform Data During Validation with Schema 🟑 Intermediate Use Schema.transform to safely convert data from one type to another during the parsing phase, such as from a string to a Date.
Use Effect.gen for Business Logic 🟑 Intermediate Encapsulate sequential business logic, control flow, and dependency access within Effect.gen for improved readability and maintainability.

Modeling Time

Representing and manipulating time in your applications.

Pattern Skill Level Summary
Accessing the Current Time with Clock 🟑 Intermediate Use the Clock service to access the current time in a testable, deterministic way, avoiding direct calls to Date.now().
Beyond the Date Type - Real World Dates, Times, and Timezones 🟑 Intermediate Use the Clock service for testable access to the current time and prefer immutable primitives for storing and passing timestamps.
Representing Time Spans with Duration 🟑 Intermediate Use the Duration data type to represent time intervals in a type-safe, human-readable, and composable way.

Modeling Data

Working with data structures and transformations in a type-safe way.

Pattern Skill Level Summary
Comparing Data by Value with Structural Equality 🟒 Beginner Use Data.struct and Equal.equals to safely compare objects by their value instead of their reference, avoiding common JavaScript pitfalls.

Making HTTP Requests

Acting as a client to call external APIs and services

Pattern Skill Level Summary
Add Custom Metrics to Your Application 🟑 Intermediate Use Effect's Metric module to instrument your code with counters, gauges, and histograms to track key business and performance indicators.
Create a Testable HTTP Client Service 🟑 Intermediate Define an HttpClient service with separate 'Live' and 'Test' layers to enable robust, testable interactions with external APIs.
Model Dependencies as Services 🟑 Intermediate Abstract external dependencies and capabilities into swappable, testable services using Effect's dependency injection system.
Add Caching by Wrapping a Layer 🟠 Advanced Implement caching by creating a new layer that wraps a live service, intercepting method calls to add caching logic without modifying the original service.
Build a Basic HTTP Server 🟠 Advanced Combine Layer, Runtime, and Effect to create a simple, robust HTTP server using Node.js's built-in http module.
Create a Managed Runtime for Scoped Resources 🟠 Advanced Use Layer.launch to safely manage the lifecycle of layers containing scoped resources, ensuring finalizers are always run.

Building APIs

Creating APIs with Effect, including routing, request handling, and response generation.

Pattern Skill Level Summary
Create a Basic HTTP Server 🟒 Beginner Launch a simple, effect-native HTTP server to respond to incoming requests.
Extract Path Parameters 🟒 Beginner Capture and use dynamic segments from a request URL, such as a resource ID.
Handle a GET Request 🟒 Beginner Define a route that responds to a specific HTTP GET request path.
Send a JSON Response 🟒 Beginner Create and send a structured JSON response with the correct headers and status code.
Handle API Errors 🟑 Intermediate Translate application-specific errors from the Effect failure channel into meaningful HTTP error responses.
Make an Outgoing HTTP Client Request 🟑 Intermediate Use the built-in Effect HTTP client to make safe and composable requests to external services from within your API.
Provide Dependencies to Routes 🟑 Intermediate Inject services like database connections into HTTP route handlers using Layer and Effect.Service.
Validate Request Body 🟑 Intermediate Safely parse and validate an incoming JSON request body against a predefined Schema.

Building Data Pipelines

Processing and transforming data in a lazy, composable, and resource-safe manner.

Pattern Skill Level Summary
Collect All Results into a List 🟒 Beginner Run a pipeline and gather all of its results into an in-memory array.
Create a Stream from a List 🟒 Beginner Turn a simple in-memory array or list into a foundational data pipeline using Stream.
Run a Pipeline for its Side Effects 🟒 Beginner Execute a pipeline for its effects without collecting the results, saving memory.
Automatically Retry Failed Operations 🟑 Intermediate Build a self-healing pipeline that can automatically retry failed processing steps using a configurable backoff strategy.
Process a Large File with Constant Memory 🟑 Intermediate Create a data pipeline from a file on disk, processing it line-by-line without loading the entire file into memory.
Process collections of data asynchronously 🟑 Intermediate Process collections of data asynchronously in a lazy, composable, and resource-safe manner using Effect's Stream.
Process Items Concurrently 🟑 Intermediate Perform an asynchronous action for each item in a stream with controlled parallelism to dramatically improve performance.
Process Items in Batches 🟑 Intermediate Group items into chunks for efficient bulk operations, like database inserts or batch API calls.
Turn a Paginated API into a Single Stream 🟑 Intermediate Convert a paginated API into a continuous, easy-to-use stream, abstracting away the complexity of fetching page by page.
Manage Resources Safely in a Pipeline 🟠 Advanced Ensure resources like file handles or connections are safely acquired at the start of a pipeline and always released at the end, even on failure.

Concurrency

Building efficient, non-blocking applications that can handle multiple tasks simultaneously.

Pattern Skill Level Summary
Control Repetition with Schedule 🟑 Intermediate Use Schedule to create composable, stateful policies that define precisely how an effect should be repeated or retried.
Manage Shared State Safely with Ref 🟑 Intermediate Use Ref to model shared, mutable state in a concurrent environment, ensuring all updates are atomic and free of race conditions.
Process a Collection in Parallel with Effect.forEach 🟑 Intermediate Use Effect.forEach with the concurrency option to process a collection of items in parallel with a fixed limit, preventing resource exhaustion.
Race Concurrent Effects for the Fastest Result 🟑 Intermediate Use Effect.race to run multiple effects concurrently and proceed with the result of the one that succeeds first, automatically interrupting the others.
Run Independent Effects in Parallel with Effect.all 🟑 Intermediate Use Effect.all to run multiple independent effects concurrently and collect all their results into a single tuple.
Add Caching by Wrapping a Layer 🟠 Advanced Implement caching by creating a new layer that wraps a live service, intercepting method calls to add caching logic without modifying the original service.
Decouple Fibers with Queues and PubSub 🟠 Advanced Use Queue for point-to-point work distribution and PubSub for broadcast messaging to enable safe, decoupled communication between concurrent fibers.
Execute Long-Running Apps with Effect.runFork 🟠 Advanced Use Effect.runFork at the application's entry point to launch a long-running process as a detached fiber, allowing for graceful shutdown.
Implement Graceful Shutdown for Your Application 🟠 Advanced Use Effect.runFork and listen for OS signals (SIGINT, SIGTERM) to trigger a Fiber.interrupt, ensuring all resources are safely released.
Manage Resource Lifecycles with Scope 🟠 Advanced Use Scope for fine-grained, manual control over resource lifecycles, ensuring cleanup logic (finalizers) is always executed.
Poll for Status Until a Task Completes 🟠 Advanced Use Effect.race to run a repeating polling effect alongside a main task, automatically stopping the polling when the main task finishes.
Run Background Tasks with Effect.fork 🟠 Advanced Use Effect.fork to start a computation in a background fiber, allowing the parent fiber to continue its work without waiting.
Understand Fibers as Lightweight Threads 🟠 Advanced A Fiber is a lightweight, virtual thread managed by the Effect runtime, enabling massive concurrency on a single OS thread without the overhead of traditional threading.

Testing

How to test Effect code effectively, reliably, and deterministically.

Pattern Skill Level Summary
Accessing the Current Time with Clock 🟑 Intermediate Use the Clock service to access the current time in a testable, deterministic way, avoiding direct calls to Date.now().
Create a Testable HTTP Client Service 🟑 Intermediate Define an HttpClient service with separate 'Live' and 'Test' layers to enable robust, testable interactions with external APIs.
Mocking Dependencies in Tests 🟑 Intermediate Use a test-specific Layer to provide mock implementations of services your code depends on, enabling isolated and deterministic unit tests.
Model Dependencies as Services 🟑 Intermediate Abstract external dependencies and capabilities into swappable, testable services using Effect's dependency injection system.
Use the Auto-Generated .Default Layer in Tests 🟑 Intermediate When testing, always use the MyService.Default layer that is automatically generated by the Effect.Service class for dependency injection.
Write Tests That Adapt to Application Code 🟑 Intermediate A cardinal rule of testing: Tests must adapt to the application's interface, not the other way around. Never modify application code solely to make a test pass.
Organize Layers into Composable Modules 🟠 Advanced Structure a large application by grouping related services into 'module' layers, which are then composed together with a shared base layer.

Observability

Pattern Skill Level Summary
Add Custom Metrics to Your Application 🟑 Intermediate Use Effect's Metric module to instrument your code with counters, gauges, and histograms to track key business and performance indicators.
Trace Operations Across Services with Spans 🟑 Intermediate Use Effect.withSpan to create custom tracing spans, providing detailed visibility into the performance and flow of your application's operations.

Resource Management

Pattern Skill Level Summary
Create a Managed Runtime for Scoped Resources 🟠 Advanced Use Layer.launch to safely manage the lifecycle of layers containing scoped resources, ensuring finalizers are always run.
Implement Graceful Shutdown for Your Application 🟠 Advanced Use Effect.runFork and listen for OS signals (SIGINT, SIGTERM) to trigger a Fiber.interrupt, ensuring all resources are safely released.
Manage Resource Lifecycles with Scope 🟠 Advanced Use Scope for fine-grained, manual control over resource lifecycles, ensuring cleanup logic (finalizers) is always executed.

Tooling and Debugging

Pattern Skill Level Summary
Supercharge Your Editor with the Effect LSP 🟑 Intermediate Install the Effect Language Server (LSP) extension for your editor to get rich, inline type information and enhanced error checking for your Effect code.
Teach your AI Agents Effect with the MCP Server 🟠 Advanced Use the Effect MCP server to provide live, contextual information about your application's structure directly to AI coding agents.

AI Coding Rules

This project provides a machine-readable set of coding rules for AI-powered IDEs and coding agents.

You can find the latest rules files in the rules directory. These files are designed for integration with tools like Cursor, GitHub Copilot, and other AI coding assistants.

  • rules.md: Human-readable rules summary
  • rules.json: Structured rules for programmatic consumption

Contributing

This is a community-driven project, and we welcome contributions! Whether it's a new pattern, a correction, or an improvement to an existing one, your help is valued.

Please read our CONTRIBUTING.md file for guidelines on how to get started.

This README.md is automatically generated. To update it, run the generation script.

Roadmap & Future Vision

This repository is the foundational layer for a much larger vision. The goal is to evolve this knowledge base into a comprehensive platform for learning and building with Effect.

Our roadmap includes:

1. The Effect Patterns Hub Website

A dedicated blog and documentation site built with Effect. This will provide a beautiful, searchable, and interactive interface for browsing the patterns.

2. The "Recipes" Section

A collection of end-to-end, practical blueprints for building complete applications. This will go beyond individual patterns to show how they are composed to solve real-world problems, including:

  • Building Enterprise Apps
  • Building SaaS Apps
  • Building Real-time Apps
  • Building Runtimes for AI Agents
  • Building Blogs
  • Building Full APIs
  • Building AI-powered Applications

3. The "Learn Effect" Chat App

A specialized, AI-powered chat application. This app will be trained on this knowledge base, allowing developers to ask questions in natural language ("How do I handle retries?") and get synthesized answers, code examples, and links to the relevant patterns.

4. AI Agent Integration

Enhancing the scripts that generate machine-readable rulebooks ('rules.md', 'rules.json') from our pattern library. The goal is to create self-contained artifacts that can be directly consumed by AI coding agents like Cursor or custom bots, providing them with the full context of our best practices to assist in code generation and refactoring.

5. Internal Tooling & Automation

  • README Generation: The immediate next step is to create a script that automatically generates the tables in this README by parsing the frontmatter from all '.mdx' files.
  • Rule Generation: Continue to enhance and maintain the scripts that generate the machine-readable rulebooks.

Contributing

This is a community-driven project, and we welcome contributions! Whether it's a new pattern, a correction, or help with one of the roadmap items, your help is valued.

Please read our CONTRIBUTING.md file for guidelines on how to get started.

About

A community-driven knowledge base of practical patterns for Effect-TS.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  
0