effect-patterns-observability

4
0
Source

Effect-TS patterns for Observability. Use when working with observability in Effect-TS applications.

Install

mkdir -p .claude/skills/effect-patterns-observability && curl -L -o skill.zip "https://mcp.directory/api/skills/download/2805" && unzip -o skill.zip -d .claude/skills/effect-patterns-observability && rm skill.zip

Installs to .claude/skills/effect-patterns-observability

About this skill

Effect-TS Patterns: Observability

This skill provides 13 curated Effect-TS patterns for observability. Use this skill when working on tasks related to:

  • observability
  • Best practices in Effect-TS applications
  • Real-world patterns and solutions

🟢 Beginner Patterns

Debug Effect Programs

Rule: Use Effect.tap and logging to inspect values without changing program flow.

Good Example:

import { Effect, pipe } from "effect"

// ============================================
// 1. Using tap to inspect values
// ============================================

const fetchUser = (id: string) =>
  Effect.succeed({ id, name: "Alice", email: "alice@example.com" })

const processUser = (id: string) =>
  fetchUser(id).pipe(
    // tap runs an effect for its side effect, then continues with original value
    Effect.tap((user) => Effect.log(`Fetched user: ${user.name}`)),
    Effect.map((user) => ({ ...user, processed: true })),
    Effect.tap((user) => Effect.log(`Processed: ${JSON.stringify(user)}`))
  )

// ============================================
// 2. Debug a pipeline
// ============================================

const numbers = [1, 2, 3, 4, 5]

const pipeline = Effect.gen(function* () {
  yield* Effect.log("Starting pipeline")

  const step1 = numbers.filter((n) => n % 2 === 0)
  yield* Effect.log(`After filter (even): ${JSON.stringify(step1)}`)

  const step2 = step1.map((n) => n * 10)
  yield* Effect.log(`After map (*10): ${JSON.stringify(step2)}`)

  const step3 = step2.reduce((a, b) => a + b, 0)
  yield* Effect.log(`After reduce (sum): ${step3}`)

  return step3
})

// ============================================
// 3. Debug errors
// ============================================

const riskyOperation = (shouldFail: boolean) =>
  Effect.gen(function* () {
    yield* Effect.log("Starting risky operation")

    if (shouldFail) {
      yield* Effect.log("About to fail...")
      return yield* Effect.fail(new Error("Something went wrong"))
    }

    yield* Effect.log("Success!")
    return "result"
  })

const debugErrors = riskyOperation(true).pipe(
  // Log when operation fails
  Effect.tapError((error) => Effect.log(`Operation failed: ${error.message}`)),

  // Provide a fallback
  Effect.catchAll((error) => {
    return Effect.succeed(`Recovered from: ${error.message}`)
  })
)

// ============================================
// 4. Trace execution flow
// ============================================

const step = (name: string, value: number) =>
  Effect.gen(function* () {
    yield* Effect.log(`[${name}] Input: ${value}`)
    const result = value * 2
    yield* Effect.log(`[${name}] Output: ${result}`)
    return result
  })

const tracedWorkflow = Effect.gen(function* () {
  const a = yield* step("Step 1", 5)
  const b = yield* step("Step 2", a)
  const c = yield* step("Step 3", b)
  yield* Effect.log(`Final result: ${c}`)
  return c
})

// ============================================
// 5. Quick debug with console
// ============================================

// Sometimes you just need console.log
const quickDebug = Effect.gen(function* () {
  const value = yield* Effect.succeed(42)
  
  // Effect.sync wraps side effects
  yield* Effect.sync(() => console.log("Quick debug:", value))
  
  return value
})

// ============================================
// 6. Run examples
// ============================================

const program = Effect.gen(function* () {
  yield* Effect.log("=== Tap Example ===")
  yield* processUser("123")

  yield* Effect.log("\n=== Pipeline Debug ===")
  yield* pipeline

  yield* Effect.log("\n=== Error Debug ===")
  yield* debugErrors

  yield* Effect.log("\n=== Traced Workflow ===")
  yield* tracedWorkflow
})

Effect.runPromise(program)

Rationale:

Use Effect.tap to inspect values and Effect.log to trace execution without changing program behavior.


Debugging Effect code differs from imperative code:

  1. No breakpoints - Effects are descriptions, not executions
  2. Lazy evaluation - Code runs later when you call runPromise
  3. Composition - Effects chain together

tap and logging let you see inside without breaking the chain.



Your First Logs

Rule: Use Effect.log and related functions for structured, contextual logging.

Good Example:

import { Effect, Logger, LogLevel } from "effect"

// ============================================
// 1. Basic logging
// ============================================

const basicLogging = Effect.gen(function* () {
  // Different log levels
  yield* Effect.logDebug("Debug message - for development")
  yield* Effect.logInfo("Info message - normal operation")
  yield* Effect.log("Default log - same as logInfo")
  yield* Effect.logWarning("Warning - something unusual")
  yield* Effect.logError("Error - something went wrong")
})

// ============================================
// 2. Logging with context
// ============================================

const withContext = Effect.gen(function* () {
  // Add structured data to logs
  yield* Effect.log("User logged in").pipe(
    Effect.annotateLogs({
      userId: "user-123",
      action: "login",
      ipAddress: "192.168.1.1",
    })
  )

  // Add a single annotation
  yield* Effect.log("Processing request").pipe(
    Effect.annotateLogs("requestId", "req-456")
  )
})

// ============================================
// 3. Log spans for timing
// ============================================

const withTiming = Effect.gen(function* () {
  yield* Effect.log("Starting operation")

  // withLogSpan adds timing information
  yield* Effect.sleep("100 millis").pipe(
    Effect.withLogSpan("database-query")
  )

  yield* Effect.log("Operation complete")
})

// ============================================
// 4. Practical example
// ============================================

interface User {
  id: string
  email: string
}

const processOrder = (orderId: string, userId: string) =>
  Effect.gen(function* () {
    yield* Effect.logInfo("Processing order").pipe(
      Effect.annotateLogs({ orderId, userId })
    )

    // Simulate work
    yield* Effect.sleep("50 millis")

    yield* Effect.logInfo("Order processed successfully").pipe(
      Effect.annotateLogs({ orderId, status: "completed" })
    )

    return { orderId, status: "completed" }
  }).pipe(
    Effect.withLogSpan("processOrder")
  )

// ============================================
// 5. Configure log level
// ============================================

const debugProgram = basicLogging.pipe(
  // Show all logs including debug
  Logger.withMinimumLogLevel(LogLevel.Debug)
)

const productionProgram = basicLogging.pipe(
  // Only show warnings and errors
  Logger.withMinimumLogLevel(LogLevel.Warning)
)

// ============================================
// 6. Run
// ============================================

const program = Effect.gen(function* () {
  yield* Effect.log("=== Basic Logging ===")
  yield* basicLogging

  yield* Effect.log("\n=== With Context ===")
  yield* withContext

  yield* Effect.log("\n=== With Timing ===")
  yield* withTiming

  yield* Effect.log("\n=== Process Order ===")
  yield* processOrder("order-789", "user-123")
})

Effect.runPromise(program)

Rationale:

Use Effect's built-in logging functions for structured, contextual logging that works with any logging backend.


Effect's logging is superior to console.log:

  1. Structured - Logs are data, not just strings
  2. Contextual - Automatically includes fiber info, timestamps
  3. Configurable - Change log levels, formats, destinations
  4. Type-safe - Part of the Effect type system


🟡 Intermediate Patterns

Instrument and Observe Function Calls with Effect.fn

Rule: Use Effect.fn to wrap functions with effectful instrumentation, such as logging, metrics, or tracing, in a composable and type-safe way.

Good Example:

import { Effect } from "effect";

// A simple function to instrument
function add(a: number, b: number): number {
  return a + b;
}

// Use Effect.fn to instrument the function with observability
const addWithLogging = Effect.fn("add")(add).pipe(
  Effect.withSpan("add", { attributes: { "fn.name": "add" } })
);

// Use the instrumented function in an Effect workflow
const program = Effect.gen(function* () {
  yield* Effect.logInfo("Calling add function");
  const sum = yield* addWithLogging(2, 3);
  yield* Effect.logInfo(`Sum is ${sum}`);
  return sum;
});

// Run the program
Effect.runPromise(program);

Explanation:

  • Effect.fn("name")(fn) wraps a function with instrumentation capabilities, enabling observability.
  • You can add tracing spans, logging, metrics, and other observability logic to function boundaries.
  • Keeps instrumentation separate from business logic and fully composable.
  • The wrapped function integrates seamlessly with Effect's observability and tracing infrastructure.

Anti-Pattern:

Scattering logging, metrics, or tracing logic directly inside business functions, making code harder to test, maintain, and compose.

Rationale:

Use Effect.fn to wrap and instrument function calls with effectful logic, such as logging, metrics, or tracing.
This enables you to observe, monitor, and debug function boundaries in a composable, type-safe way.

Instrumenting function calls is essential for observability, especially in complex or critical code paths.
Effect.fn lets you add effectful logic (logging, metrics, tracing, etc.) before, after, or around any function call, without changing the function’s core logic.


Leverage Effect's Built-in Structured Logging

Rule: Use Effect.log, Effect.logInfo, and Effect.logError to add structured, context-aware logging to your Effect code.

Good Example:

import { Effect } from "effect";

// Log a simple message
const program = Effect.gen(function* () {
  yield* Effect.log("Starting the ap

---

*Content truncated.*

You might also like

flutter-development

aj-geddes

Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.

643969

drawio-diagrams-enhanced

jgtolentino

Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.

591705

ui-ux-pro-max

nextlevelbuilder

"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."

318398

godot

bfollington

This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.

339397

nano-banana-pro

garg-aayush

Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.

451339

fastapi-templates

wshobson

Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.

304231

Stay ahead of the MCP ecosystem

Get weekly updates on new skills and servers.