elixir-otp-patterns

OTP architecture patterns for Elixir: GenServer, Supervisor, DynamicSupervisor, Registry, Application. Use when designing fault-tolerant process architectures, supervision trees, or stateful services.

Best use case

elixir-otp-patterns is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

OTP architecture patterns for Elixir: GenServer, Supervisor, DynamicSupervisor, Registry, Application. Use when designing fault-tolerant process architectures, supervision trees, or stateful services.

Teams using elixir-otp-patterns should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/elixir-otp-patterns/SKILL.md --create-dirs "https://raw.githubusercontent.com/ratnesh-maurya/cursor-claude-personas/main/senior-elixir-developer/.claude/skills/elixir-otp-patterns/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/elixir-otp-patterns/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How elixir-otp-patterns Compares

Feature / Agentelixir-otp-patternsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

OTP architecture patterns for Elixir: GenServer, Supervisor, DynamicSupervisor, Registry, Application. Use when designing fault-tolerant process architectures, supervision trees, or stateful services.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Elixir OTP Patterns

OTP architecture patterns for building fault-tolerant, supervised process trees in Elixir.

## When to Use

- Designing supervision trees and process hierarchies
- Implementing stateful services with GenServer
- Managing dynamic process pools with DynamicSupervisor
- Building named process registries
- Architecting fault-tolerant BEAM applications

## Do Not Use When

- Simple stateless data transformations (use modules/functions)
- You need a different language runtime
- Basic scripting without concurrency needs

## GenServer Patterns

### Client API Module

Centralize all GenServer interactions — never scatter `GenServer.call/cast` across the codebase:

```elixir
defmodule MyApp.Counter do
  use GenServer

  # ── Client API ──
  def start_link(opts) do
    name = Keyword.get(opts, :name, __MODULE__)
    GenServer.start_link(__MODULE__, opts, name: name)
  end

  def increment(server \\ __MODULE__), do: GenServer.call(server, :increment)
  def get_count(server \\ __MODULE__), do: GenServer.call(server, :get_count)
  def reset(server \\ __MODULE__), do: GenServer.cast(server, :reset)

  # ── Server Callbacks ──
  @impl true
  def init(_opts), do: {:ok, 0}

  @impl true
  def handle_call(:increment, _from, count), do: {:reply, count + 1, count + 1}
  def handle_call(:get_count, _from, count), do: {:reply, count, count}

  @impl true
  def handle_cast(:reset, _count), do: {:noreply, 0}
end
```

### Callback Guidelines

| Callback | Use For | Return |
|----------|---------|--------|
| `handle_call` | Synchronous request/reply | `{:reply, response, new_state}` |
| `handle_cast` | Async fire-and-forget | `{:noreply, new_state}` |
| `handle_info` | System/external messages, timers | `{:noreply, new_state}` |
| `handle_continue` | Post-init work without blocking | `{:noreply, new_state}` |
| `terminate` | Cleanup (only with `trap_exit`) | Any |

### Deferred Replies

```elixir
def handle_call(:expensive_op, from, state) do
  Task.start(fn ->
    result = do_expensive_work()
    GenServer.reply(from, result)
  end)
  {:noreply, state}
end
```

## Supervisor Strategies

| Strategy | Behavior | Use When |
|----------|----------|----------|
| `:one_for_one` | Restart only crashed child | Children are independent |
| `:one_for_all` | Restart all children | Children are tightly coupled |
| `:rest_for_one` | Restart crashed + those started after | Ordered dependencies |

```elixir
defmodule MyApp.Application do
  use Application

  def start(_type, _args) do
    children = [
      {Registry, keys: :unique, name: MyApp.Registry},  # Start first
      MyApp.Repo,                                         # Database
      {Phoenix.PubSub, name: MyApp.PubSub},              # PubSub
      MyApp.Worker,                                       # Application worker
      MyAppWeb.Endpoint                                   # HTTP (last)
    ]

    Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
  end
end
```

## DynamicSupervisor

For runtime-spawned children (one process per user/room/session):

```elixir
defmodule MyApp.SessionSupervisor do
  use DynamicSupervisor

  def start_link(init_arg), do: DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  def init(_init_arg), do: DynamicSupervisor.init(strategy: :one_for_one)

  def start_session(session_id) do
    spec = {MyApp.SessionWorker, session_id: session_id}
    DynamicSupervisor.start_child(__MODULE__, spec)
  end
end
```

**Bottleneck fix**: Use `PartitionSupervisor` (Elixir 1.14+) to distribute across multiple instances:

```elixir
{PartitionSupervisor, child_spec: DynamicSupervisor, name: MyApp.PartitionedSessions}
```

## Registry + DynamicSupervisor (Singleton Pattern)

```elixir
def get_or_start_session(session_id) do
  case Registry.lookup(MyApp.Registry, {:session, session_id}) do
    [{pid, _}] -> {:ok, pid}
    [] -> SessionSupervisor.start_session(session_id)
  end
end
```

## child_spec Options

| Restart | Behavior |
|---------|----------|
| `:permanent` | Always restart (default) |
| `:temporary` | Never restart |
| `:transient` | Restart only on abnormal exit |

## Anti-Patterns

- **Processes for code organization**: Use modules for namespacing, processes for runtime concerns (state, concurrency, fault isolation)
- **Single GenServer bottleneck**: One process = one sequential mailbox. Partition state across multiple processes for high throughput
- **Processes outside supervision**: Never use raw `spawn/1` for long-running work — always supervise
- **Oversized state**: Keep GenServer state lean; offload large data to ETS or persistent storage
- **Spreading GenServer.call/cast**: Always wrap in a client API module

## Key Libraries

- `Registry`, `DynamicSupervisor`, `PartitionSupervisor` (stdlib)
- `Horde` — distributed Registry and DynamicSupervisor for clustered environments

Related Skills

wcag-audit-patterns

5
from ratnesh-maurya/cursor-claude-personas

Conduct WCAG 2.2 accessibility audits with automated testing, manual verification, and remediation guidance. Use when auditing websites for accessibility, fixing WCAG violations, or implementing ac...

similarity-search-patterns

5
from ratnesh-maurya/cursor-claude-personas

Implement efficient similarity search with vector databases. Use when building semantic search, implementing nearest neighbor queries, or optimizing retrieval performance.

projection-patterns

5
from ratnesh-maurya/cursor-claude-personas

Build read models and projections from event streams. Use when implementing CQRS read sides, building materialized views, or optimizing query performance in event-sourced systems.

ddd-tactical-patterns

5
from ratnesh-maurya/cursor-claude-personas

Apply DDD tactical patterns in code using entities, value objects, aggregates, repositories, and domain events with explicit invariants.

rust-async-patterns

5
from ratnesh-maurya/cursor-claude-personas

Master Rust async programming with Tokio, async traits, error handling, and concurrent patterns. Use when building async Rust applications, implementing concurrent systems, or debugging async code.

go-concurrency-patterns

5
from ratnesh-maurya/cursor-claude-personas

Master Go concurrency with goroutines, channels, sync primitives, and context. Use when building concurrent Go applications, implementing worker pools, or debugging race conditions.

cc-skill-backend-patterns

5
from ratnesh-maurya/cursor-claude-personas

Backend architecture patterns, API design, database optimization, and server-side best practices for Node.js, Express, and Next.js API routes.

react-ui-patterns

5
from ratnesh-maurya/cursor-claude-personas

Modern React UI patterns for loading states, error handling, and data fetching. Use when building UI components, handling async data, or managing UI states.

cc-skill-frontend-patterns

5
from ratnesh-maurya/cursor-claude-personas

Frontend development patterns for React, Next.js, state management, performance optimization, and UI best practices.

angular-ui-patterns

5
from ratnesh-maurya/cursor-claude-personas

Modern Angular UI patterns for loading states, error handling, and data display. Use when building UI components, handling async data, or managing component states.

elixir-testing

5
from ratnesh-maurya/cursor-claude-personas

Elixir testing patterns: ExUnit, Mox behaviour-based mocking, StreamData property testing, Phoenix integration tests. Use when writing tests for Elixir/Phoenix applications.

elixir-pro

5
from ratnesh-maurya/cursor-claude-personas

Write idiomatic Elixir code with OTP patterns, supervision trees, and Phoenix LiveView. Masters concurrency, fault tolerance, and distributed systems.