elixir-patterns-advanced

Advanced Elixir/Phoenix patterns — distributed Erlang clusters, CRDT-based state, Ecto multi-tenancy, event sourcing, Commanded framework, and RFC 7807 API errors.

8 stars

Best use case

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

Advanced Elixir/Phoenix patterns — distributed Erlang clusters, CRDT-based state, Ecto multi-tenancy, event sourcing, Commanded framework, and RFC 7807 API errors.

Teams using elixir-patterns-advanced 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-patterns-advanced/SKILL.md --create-dirs "https://raw.githubusercontent.com/marvinrichter/clarc/main/skills/elixir-patterns-advanced/SKILL.md"

Manual Installation

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

How elixir-patterns-advanced Compares

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

Frequently Asked Questions

What does this skill do?

Advanced Elixir/Phoenix patterns — distributed Erlang clusters, CRDT-based state, Ecto multi-tenancy, event sourcing, Commanded framework, and RFC 7807 API errors.

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 Patterns Advanced

Advanced Elixir patterns for distributed, fault-tolerant production systems.

## When to Activate

- Building distributed Elixir clusters with `libcluster`
- Implementing event sourcing or CQRS with Commanded
- Designing multi-tenant systems with Ecto
- Using CRDTs or distributed state with `delta_crdt`
- Implementing RFC 7807 Problem Details in Phoenix APIs
- Advanced OTP: dynamic supervisors, process registries, partition supervisor

## Distributed Elixir with libcluster

```elixir
# config/runtime.exs
config :libcluster,
  topologies: [
    k8s: [
      strategy: Cluster.Strategy.Kubernetes,
      config: [
        mode: :ip,
        kubernetes_selector: "app=myapp",
        kubernetes_node_basename: "myapp"
      ]
    ]
  ]

# lib/my_app/application.ex
def start(_type, _args) do
  children = [
    {Cluster.Supervisor, [Application.get_env(:libcluster, :topologies), [name: MyApp.ClusterSupervisor]]},
    MyApp.Repo,
    MyAppWeb.Endpoint
  ]
  Supervisor.start_link(children, strategy: :one_for_one)
end
```

## Event Sourcing with Commanded

```elixir
# lib/my_app/domain/order/commands.ex
defmodule MyApp.Order.Commands.PlaceOrder do
  defstruct [:order_id, :user_id, :line_items, :total]
end

# lib/my_app/domain/order/events.ex
defmodule MyApp.Order.Events.OrderPlaced do
  @derive Jason.Encoder
  defstruct [:order_id, :user_id, :total, :placed_at]
end

# lib/my_app/domain/order/aggregate.ex
defmodule MyApp.Order.Aggregate do
  defstruct [:order_id, :status, :total]

  alias MyApp.Order.Commands.PlaceOrder
  alias MyApp.Order.Events.OrderPlaced

  def execute(%__MODULE__{order_id: nil}, %PlaceOrder{} = cmd) do
    %OrderPlaced{
      order_id: cmd.order_id,
      user_id: cmd.user_id,
      total: cmd.total,
      placed_at: DateTime.utc_now()
    }
  end

  def execute(%__MODULE__{status: :placed}, %PlaceOrder{}) do
    {:error, :order_already_placed}
  end

  def apply(%__MODULE__{} = order, %OrderPlaced{} = event) do
    %{order | order_id: event.order_id, status: :placed, total: event.total}
  end
end

# lib/my_app/domain/order/router.ex
defmodule MyApp.OrderRouter do
  use Commanded.Commands.Router

  dispatch PlaceOrder, to: MyApp.Order.Aggregate, identity: :order_id
end
```

## Ecto Multi-Tenancy

```elixir
# lib/my_app/repo.ex — Schema-based multi-tenancy
defmodule MyApp.Repo do
  use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres

  def with_tenant(tenant_id, fun) do
    prefix = "tenant_#{tenant_id}"
    put_dynamic_repo(%{prefix: prefix})
    try do
      fun.()
    after
      put_dynamic_repo(nil)
    end
  end
end

# In controllers / resolvers:
MyApp.Repo.with_tenant(current_tenant.id, fn ->
  MyApp.Products.list_products()
end)

# Schema with prefix
defmodule MyApp.Product do
  use Ecto.Schema

  @schema_prefix "tenant_default"  # overridden at query time
  schema "products" do
    field :name, :string
    field :price, :decimal
    timestamps()
  end
end
```

## Dynamic Supervisor Pattern

```elixir
# lib/my_app/session_supervisor.ex
defmodule MyApp.SessionSupervisor do
  use DynamicSupervisor

  def start_link(init_arg) do
    DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  end

  def init(_init_arg) do
    DynamicSupervisor.init(strategy: :one_for_one, max_children: 1000)
  end

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

  def stop_session(session_id) do
    case Registry.lookup(MyApp.SessionRegistry, session_id) do
      [{pid, _}] -> DynamicSupervisor.terminate_child(__MODULE__, pid)
      [] -> :ok
    end
  end
end

# lib/my_app/session.ex
defmodule MyApp.Session do
  use GenServer, restart: :temporary

  def start_link(opts) do
    session_id = Keyword.fetch!(opts, :session_id)
    GenServer.start_link(__MODULE__, session_id,
      name: {:via, Registry, {MyApp.SessionRegistry, session_id}}
    )
  end

  def init(session_id) do
    Process.send_after(self(), :expire, :timer.minutes(30))
    {:ok, %{id: session_id, data: %{}, created_at: DateTime.utc_now()}}
  end

  def handle_info(:expire, state) do
    {:stop, :normal, state}
  end
end
```

## RFC 7807 Problem Details in Phoenix

```elixir
# lib/my_app_web/problem_details.ex
defmodule MyAppWeb.ProblemDetails do
  import Plug.Conn

  def render_problem(conn, status, title, detail, opts \\ []) do
    body = %{
      type: Keyword.get(opts, :type, "about:blank"),
      title: title,
      status: status,
      detail: detail,
      instance: conn.request_path
    }

    body = if extras = Keyword.get(opts, :extras) do
      Map.merge(body, extras)
    else
      body
    end

    conn
    |> put_resp_content_type("application/problem+json")
    |> send_resp(status, Jason.encode!(body))
    |> halt()
  end
end

# lib/my_app_web/controllers/fallback_controller.ex
defmodule MyAppWeb.FallbackController do
  use MyAppWeb, :controller
  import MyAppWeb.ProblemDetails

  def call(conn, {:error, :not_found}) do
    render_problem(conn, 404, "Not Found", "The requested resource was not found.")
  end

  def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
    errors = Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} ->
      Regex.replace(~r"%{(\w+)}", msg, fn _, key ->
        opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
      end)
    end)

    render_problem(conn, 422, "Unprocessable Entity",
      "Validation failed. See 'errors' for details.",
      extras: %{errors: errors}
    )
  end

  def call(conn, {:error, :unauthorized}) do
    render_problem(conn, 401, "Unauthorized", "Authentication required.")
  end
end
```

## Telemetry and Observability

```elixir
# lib/my_app/telemetry.ex
defmodule MyApp.Telemetry do
  use Supervisor
  import Telemetry.Metrics

  def start_link(arg) do
    Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
  end

  def init(_arg) do
    children = [
      {TelemetryMetricsPrometheus, metrics: metrics()}
    ]
    Supervisor.init(children, strategy: :one_for_one)
  end

  def metrics do
    [
      counter("my_app.orders.placed.count"),
      sum("my_app.orders.placed.total", unit: {:native, :millisecond}),
      last_value("my_app.repo.query.total_time", unit: {:native, :millisecond}),
      distribution("phoenix.router_dispatch.stop.duration",
        unit: {:native, :millisecond},
        reporter_options: [buckets: [10, 50, 100, 250, 500, 1000]]
      )
    ]
  end
end

# Emit custom telemetry events
:telemetry.execute([:my_app, :orders, :placed], %{total: order.total}, %{user_id: user.id})
```

## Broadway Data Pipelines

```elixir
# lib/my_app/pipelines/order_pipeline.ex
defmodule MyApp.Pipelines.OrderPipeline do
  use Broadway

  def start_link(_opts) do
    Broadway.start_link(__MODULE__,
      name: __MODULE__,
      producer: [
        module: {BroadwaySQS.Producer, queue_url: System.fetch_env!("ORDER_QUEUE_URL")},
        concurrency: 2
      ],
      processors: [
        default: [concurrency: 10, max_demand: 5]
      ],
      batchers: [
        db: [concurrency: 2, batch_size: 50, batch_timeout: 1000]
      ]
    )
  end

  def handle_message(_, %Broadway.Message{data: data} = msg, _context) do
    case Jason.decode(data) do
      {:ok, payload} ->
        msg
        |> Broadway.Message.update_data(fn _ -> payload end)
        |> Broadway.Message.put_batcher(:db)
      {:error, reason} ->
        Broadway.Message.failed(msg, reason)
    end
  end

  def handle_batch(:db, messages, _batch_info, _context) do
    payloads = Enum.map(messages, & &1.data)
    case MyApp.Orders.bulk_insert(payloads) do
      {:ok, _} -> messages
      {:error, reason} ->
        Enum.map(messages, &Broadway.Message.failed(&1, reason))
    end
  end
end
```

## Related Skills

- **`elixir-patterns`** — Core patterns: GenServer, Supervisors, Phoenix contexts, LiveView
- **`elixir-testing`** — ExUnit, Mox, StreamData property testing
- **`message-queue-patterns`** — SQS, RabbitMQ, Broadway integration

Related Skills

zero-trust-patterns

8
from marvinrichter/clarc

Zero-Trust security patterns — mTLS between microservices (Istio/SPIFFE), SPIRE workload identity, OPA/Envoy authorization, NetworkPolicy default-deny-all, short-lived credentials, service mesh security, and Kubernetes RBAC hardening.

webrtc-patterns

8
from marvinrichter/clarc

WebRTC patterns — peer connection setup, ICE/STUN/TURN configuration, signaling server design, SFU vs mesh topology, screen sharing, media track management, and reconnect/ICE restart handling.

webhook-patterns

8
from marvinrichter/clarc

Webhook patterns for receiving, verifying (HMAC), and idempotently processing third-party events. Covers Stripe, GitHub, and generic webhook patterns, delivery guarantees, retry handling, and testing.

wasm-patterns

8
from marvinrichter/clarc

WebAssembly patterns: wasm-pack, wasm-bindgen (JS↔Wasm interop), WASI, Component Model, wasm-opt, Rust-to-WASM compilation, JS integration (web workers, streaming instantiation), and production deployment (CDN, Content-Type headers).

ux-micro-patterns

8
from marvinrichter/clarc

UX micro-patterns for every product state: Empty States, Loading States (skeleton screens, spinners, optimistic UI), Error States, Success States, Confirmation Dialogs, Onboarding Flows, and Progressive Disclosure. These patterns apply to every feature — done wrong, they're the biggest source of user confusion.

typescript-patterns

8
from marvinrichter/clarc

TypeScript patterns — type system best practices, strict mode, utility types, generics, discriminated unions, error handling with Result types, and module organization. Core patterns for production TypeScript.

typescript-patterns-advanced

8
from marvinrichter/clarc

Advanced TypeScript — mapped types, template literal types, conditional types, infer, type guards, decorators, async patterns, testing with Vitest/Jest, and performance. Extends typescript-patterns.

typescript-monorepo-patterns

8
from marvinrichter/clarc

TypeScript monorepo patterns with Turborepo + pnpm workspaces. Covers package structure, shared configs, task pipeline caching, build orchestration, and publishing strategy.

terraform-patterns

8
from marvinrichter/clarc

Infrastructure as Code with Terraform — project structure, remote state, modules, workspace strategy, AWS/GCP patterns, CI/CD integration, and security hardening. The standard for managing production infrastructure.

tdd-workflow-advanced

8
from marvinrichter/clarc

TDD anti-patterns — writing code before tests, testing implementation details instead of behavior, using waitForTimeout as a sync strategy, chaining tests that share state, mocking the system under test instead of its dependencies.

swiftui-patterns

8
from marvinrichter/clarc

SwiftUI architecture patterns, state management with @Observable, view composition, navigation, performance optimization, and modern iOS/macOS UI best practices.

swift-patterns

8
from marvinrichter/clarc

Core Swift patterns — value vs reference types, protocols, generics, optionals, Result, error handling, Codable, and module organization. Foundation for all Swift development.