From: aidotengineer

Building an AI-native customer support platform presents unique challenges, particularly concerning reliability and managing complexity in systems that interact directly with end-users and rely on Large Language Models (LLMs) in production [00:00:07]. Traditional TypeScript, while a strong foundation, can fall short when dealing with unreliable APIs, complex dependencies, non-deterministic model outputs, or long-running workflows [00:00:26].

Leveraging Effect for Robust Systems

To address these challenges, the TypeScript library ‘Effect’ is utilized for building robust, type-safe, and composable systems [00:00:16]. Effect provides essential tools to handle complex situations confidently as a platform evolves [00:00:36].

Key features of Effect include:

  • Strong type guarantees across the stack [00:00:44].
  • Powerful composition primitives [00:00:48].
  • Built-in concurrency, streaming, interruptions, and retry mechanisms [00:00:50].
  • Structured error modeling [00:00:54].
  • A clean dependency injection system that simplifies testing and modernization [00:00:57].
  • Easy observability via OpenTelemetry [00:01:02].

Effect enables the creation of more stable, testable, and maintainable code at scale [00:01:07]. Its gradual adoption capability makes it a natural extension of TypeScript for existing codebases [00:01:14].

Architectural Implementation with Effect

Effect is used across the entire stack of an AI-native customer support platform. Key components include:

  • React front end: Powers dashboards, agent IDE, knowledge management, insights, analytics, and SDKs [00:01:28].
  • Internal RPC server: Handles application logic, built on Effect RPC and a modified TanStack Query on the front end [00:01:37].
  • Public API server: Uses Effect HTTP with auto-generated OpenAPI documentation from annotated schemas [00:01:47].
  • Data processing engine: Syncs data from CRMs, documents, and databases, processing it for real-time analytics and reporting [00:01:53].
  • Agent workflows: Written in a custom Domain Specific Language (DSL) built on Effect, allowing for a mix of deterministic and non-deterministic behavior [00:02:00].
  • PostgreSQL database: Used for both data and vector storage, with Effect SQL handling queries [00:02:09].

All data is modeled using Effect schemas, which provide runtime validation, encoding, decoding, type-safe input/output handling, and auto-generated documentation [00:02:15].

Agentic Systems and Workflows

AI agents act as planners, taking user input, devising a plan, selecting the appropriate action, workflow, or sub-agent, executing it, and repeating until the task is complete [00:02:27].

  • Actions: Small, focused units of execution, akin to tool calls (e.g., fetching payment info, searching logs) [00:02:40].
  • Workflows: Deterministic multi-step processes (e.g., canceling a subscription involves collecting a reason, offering retention, checking eligibility, and performing cancellation) [00:02:51].
  • Sub-agents: Group related actions and workflows into larger, domain-specific modules (e.g., a billing agent or a log retrieval agent) [00:03:03].

To model this complexity, a DSL for workflows was built using Effect’s functional pipe-based system, allowing for clear and composable expression of branching, sequencing, retries, state transitions, and memory [00:03:12].

Ensuring Reliability and Testability

Reliability is paramount for mission-critical systems [00:03:32].

  • LLM Provider Fallbacks: If one LLM provider fails, the system falls back to another with similar performance characteristics (e.g., GPT-4 mini to GD Flash 2.0 for tool calling) [00:03:36]. This is modeled with retry policies that track state to avoid retrying failed providers [00:03:47].
  • Stream Duplication: When answers are streamed to the end-user, token streams are duplicated; one directly to the user and another for internal storage (e.g., for analytics), which Effect facilitates [00:03:55].
  • Testing: Heavy use of dependency injection enables mocking LLM providers and simulating failure scenarios [00:04:09]. This approach allows swapping service providers with mock versions without affecting internal system logic [00:04:14].

Enhancing Developer Experience

Effect significantly improves the developer experience for building agentic systems:

  • Schema-centric development: Input, output, and error types are defined upfront with powerful encoding/decoding, providing strong type safety and auto-generated documentation [00:04:31].
  • Dependency Injection (DI): Services are provided at system entry points, allowing for flexible composition and easy mocking for testing [00:04:48]. Dependencies are guaranteed at compile time, ensuring all required services are provided [00:04:59].
  • Modularity and Composability: Services are modular, enabling easy override of behavior or swapping implementations without altering system internals [00:05:06].
  • Strong Guard Rails: Effect helps prevent common mistakes, allowing engineers new to TypeScript to become productive quickly and avoid bad patterns after the initial learning curve [00:05:14].

Lessons Learned and Adoption Strategy

While powerful, using Effect effectively requires discipline [00:05:31].

  • Error Handling: It’s crucial to be careful not to accidentally catch errors silently upstream, as this can lead to losing important failure information [00:05:46].
  • Dependency Injection at Scale: Tracing where services are provided across multiple layers or subsystems can become challenging [00:05:57].
  • Learning Curve: Effect has a significant ecosystem and many concepts, which can be overwhelming initially. However, once the initial learning curve is overcome, the benefits compound [00:06:10].

Effect helps build predictable and resilient systems, but it is not a magic solution; developers still need to apply critical thinking [00:06:27].

A key advantage of Effect is its incremental adoption strategy [00:06:37]. Developers can start with a single service or endpoint and expand its use from there [00:06:41]. Effect is particularly useful for LLM and AI-based systems where reliability and coping with non-determinism are critical [00:06:47]. It provides tools for building predictable and observable systems [00:06:53].

Effect introduces the rigor of functional programming into real-world TypeScript in a practical way [00:06:58], allowing developers to gain value without being functional programming purists [00:07:05]. Starting small allows benefits to accrue over time [00:07:08].