Ensure accessibility in UI components including semantic HTML, ARIA attributes, keyboard navigation, and WCAG 2.2 AA compliance.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Elixir and Phoenix expert including OTP, Ecto, and functional programming
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
설치 방법 보기설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.
When reviewing or writing code, apply these guidelines:
Pattern matching is fundamental to Elixir. Use it for:
Function clauses:
def greet(%User{name: name, role: :admin}), do: "Hello Admin #{name}"
def greet(%User{name: name}), do: "Hello #{name}"
def greet(_), do: "Hello stranger"
Case statements:
case {status, data} do
{:ok, %{id: id} } when id > 0 -> process(id)
{:error, reason} -> handle_error(reason)
_ -> :unknown
end
With statements for chaining:
with {:ok, user} <- fetch_user(id),
{:ok, profile} <- fetch_profile(user),
{:ok, settings} <- fetch_settings(profile) do
{:ok, %{user: user, profile: profile, settings: settings} }
end
Use guards to add constraints to pattern matching:
def categorize(n) when is_integer(n) and n > 0, do: :positive
def categorize(n) when is_integer(n) and n < 0, do: :negative
def categorize(n) when is_integer(n), do: :zero
def process_map(map) when map_size(map) == 0, do: :empty
def process_map(map) when is_map(map), do: :has_data
The pipe operator |> improves readability:
# Instead of nested calls
result = String.trim(String.downcase(String.reverse(input)))
# Use pipes
result =
input
|> String.reverse()
|> String.downcase()
|> String.trim()
Pipe into case for handling results:
user_id
|> fetch_user()
|> case do
{:ok, user} -> process_user(user)
{:error, :not_found} -> create_user()
{:error, reason} -> {:error, reason}
end
GenServer is the foundation for stateful processes:
defmodule Counter do
use GenServer
# Client API
def start_link(initial_value) do
GenServer.start_link(__MODULE__, initial_value, name: __MODULE__)
end
def increment do
GenServer.cast(__MODULE__, :increment)
end
def get do
GenServer.call(__MODULE__, :get)
end
# Server Callbacks
@impl true
def init(initial_value) do
{:ok, initial_value}
end
@impl true
def handle_cast(:increment, state) do
{:noreply, state + 1}
end
@impl true
def handle_call(:get, _from, state) do
{:reply, state, state}
end
end
Supervisors manage process lifecycles:
defmodule MyApp.Application do
use Application
@impl true
def start(_type, _args) do
children = [
# Database
MyApp.Repo,
# PubSub
{Phoenix.PubSub, name: MyApp.PubSub},
# GenServers
{MyApp.Cache, []},
{MyApp.Worker, []},
# Endpoint (starts web server)
MyAppWeb.Endpoint
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
Supervisor strategies:
:one_for_one - restart only failed child:one_for_all - restart all children if one fails:rest_for_one - restart failed child and those started after itFor simple state management:
{:ok, agent} = Agent.start_link(fn -> %{} end)
Agent.update(agent, fn state -> Map.put(state, :key, "value") end)
Agent.get(agent, fn state -> Map.get(state, :key) end)
defmodule MyAppWeb.UserController do
use MyAppWeb, :controller
def index(conn, _params) do
users = Accounts.list_users()
render(conn, :index, users: users)
end
def create(conn, %{"user" => user_params}) do
case Accounts.create_user(user_params) do
{:ok, user} ->
conn
|> put_flash(:info, "User created successfully")
|> redirect(to: ~p"/users/#{user}")
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, :new, changeset: changeset)
end
end
end
For real-time interactive UIs:
defmodule MyAppWeb.CounterLive do
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, count: 0)}
end
@impl true
def handle_event("increment", _params, socket) do
{:noreply, update(socket, :count, &(&1 + 1))}
end
@impl true
def render(assigns) do
~H"""
<div>
<h1>Count: <%= @count %></h1>
<button phx-click="increment">+</button>
</div>
"""
end
end
PubSub for broadcasting:
# Subscribe
Phoenix.PubSub.subscribe(MyApp.PubSub, "updates")
# Broadcast
Phoenix.PubSub.broadcast(MyApp.PubSub, "updates", {:new_data, data})
# Handle in LiveView
@impl true
def handle_info({:new_data, data}, socket) do
{:noreply, assign(socket, :data, data)}
end
For WebSocket communication:
defmodule MyAppWeb.RoomChannel do
use MyAppWeb, :channel
@impl true
def join("room:" <> room_id, _payload, socket) do
{:ok, assign(socket, :room_id, room_id)}
end
@impl true
def handle_in("new_message", %{"body" => body}, socket) do
broadcast!(socket, "new_message", %{body: body})
{:reply, :ok, socket}
end
end
defmodule MyApp.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
field :name, :string
field :email, :string
field :age, :integer
has_many :posts, MyApp.Content.Post
timestamps()
end
def changeset(user, attrs) do
user
|> cast(attrs, [:name, :email, :age])
|> validate_required([:name, :email])
|> validate_format(:email, ~r/@/)
|> validate_number(:age, greater_than: 0)
|> unique_constraint(:email)
end
end
import Ecto.Query
# Basic queries
query = from u in User, where: u.age > 18, select: u
# Composable queries
def for_age(query, age) do
from u in query, where: u.age > ^age
end
def ordered(query) do
from u in query, order_by: [desc: u.inserted_at]
end
# Chain them
User
|> for_age(18)
|> ordered()
|> Repo.all()
# Joins and preloads
from u in User,
join: p in assoc(u, :posts),
where: p.published == true,
preload: [posts: p]
Repo.transaction(fn ->
with {:ok, user} <- create_user(params),
{:ok, profile} <- create_profile(user),
{:ok, _settings} <- create_settings(user) do
user
else
{:error, reason} -> Repo.rollback(reason)
end
end)
defmodule MyApp.AccountsTest do
use MyApp.DataCase, async: true
describe "create_user/1" do
test "creates user with valid attributes" do
attrs = %{name: "John", email: "john@example.com"}
assert {:ok, user} = Accounts.create_user(attrs)
assert user.name == "John"
end
test "returns error with invalid email" do
attrs = %{name: "John", email: "invalid"}
assert {:error, changeset} = Accounts.create_user(attrs)
assert %{email: ["has invalid format"]} = errors_on(changeset)
end
end
end
# Testing LiveView
defmodule MyAppWeb.CounterLiveTest do
use MyAppWeb.ConnCase
import Phoenix.LiveViewTest
test "increments counter", %{conn: conn} do
{:ok, view, _html} = live(conn, "/counter")
assert view |> element("button") |> render_click() =~ "Count: 1"
end
end
Use Elixir releases for production:
# mix.exs
def project do
[
releases: [
myapp: [
include_executables_for: [:unix],
steps: [:assemble, :tar]
]
]
]
end
Build and deploy:
MIX_ENV=prod mix release
_build/prod/rel/myapp/bin/myapp start
# config/runtime.exs
import Config
if config_env() == :prod do
database_url = System.get_env("DATABASE_URL") ||
raise "DATABASE_URL not available"
config :myapp, MyApp.Repo,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
end
# In your router
get "/health", HealthController, :check
# Controller
def check(conn, _params) do
case Repo.query("SELECT 1") do
{:ok, _} -> send_resp(conn, 200, "ok")
_ -> send_resp(conn, 503, "database unavailable")
end
end
This expert skill consolidates 1 individual skills:
Enum functions on potentially large streams — use Stream for lazy evaluation to avoid loading entire collections into memory.iex> examples in @doc) for public functions — doctests are runnable specifications; they document behavior and serve as regression tests.| Anti-Pattern | Why It Fails | Correct Approach |
|---|---|---|
| Nested if/cond chains instead of pattern matching | Harder to read; misses the power of Elixir's pattern matching; doesn't scale | Use function clauses with pattern matching heads and guard clauses |
Bare spawn without supervision | Crashed processes disappear silently; no restart, no visibility | Always use Supervisor trees; use Task.Supervisor for dynamic tasks |
Enum.map/filter on large streams | Loads entire collection into memory; causes OOM on large datasets | Use Stream.map/filter for lazy, memory-efficient pipeline processing |
| Global mutable state via Process dictionary | Process dictionary is implicit state; makes code unpredictable and untestable | Use Agent, GenServer, or ETS explicitly when shared state is required |
| No doctests for public functions | Public API has no runnable specification; behavior drifts from documentation | Always add iex> examples in @doc; run with mix test |
Before starting:
cat .claude/context/memory/learnings.md
After completing: Record any new patterns or exceptions discovered.
ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.
아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.
로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.
Ensure accessibility in UI components including semantic HTML, ARIA attributes, keyboard navigation, and WCAG 2.2 AA compliance.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Use when you want to improve response quality through meta-cognitive reasoning. Applies 15+ reasoning methods to reconsider and refine initial outputs.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
N-round opposing-stance debates for trade-off analysis. Assigns pro/con roles to agents, runs structured debate rounds with quality scoring, and produces a moderator synthesis with confidence-rated recommendation. Generalizable to architecture, technology, security, and design decisions.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Force adversarial code review stance that eliminates confirmation bias — reviewer must find issues or re-analyze
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Creates specialized AI agents on-demand when no existing agent matches a request. Use when the Router cannot find a suitable agent for a task. Enables self-evolution by generating persistent agents.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
LLM-as-judge evaluation framework with 5-dimension rubric (accuracy, groundedness, coherence, completeness, helpfulness) for scoring AI-generated content quality with weighted composite scores and evidence citations
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.