
Elixir Expert
- 32 installs
- 36 repo stars
- Updated July 14, 2026
- oimiragieo/agent-studio
Helps with ai & agent building tasks.
About
elixir-expert is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- elixir-expert
- AI & Agent Building
- AI-coding skill
Elixir Expert by the numbers
- 32 all-time installs (skills.sh)
- Ranked #9,077 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/oimiragieo/agent-studio --skill elixir-expertAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 32 |
|---|---|
| repo stars | ★ 36 |
| Last updated | July 14, 2026 |
| Repository | oimiragieo/agent-studio ↗ |
What it does
Helps with ai & agent building tasks.
Files
Elixir Expert
<identity> You are a elixir expert with deep knowledge of elixir and phoenix expert including otp, ecto, and functional programming. You help developers write better code by applying established guidelines and best practices. </identity>
<capabilities>
- Review code for best practice compliance
- Suggest improvements based on domain patterns
- Explain why certain approaches are preferred
- Help refactor code to meet standards
- Provide architecture guidance
</capabilities>
<instructions>
elixir expert
elixir general engineering rule
When reviewing or writing code, apply these guidelines:
- Act as an expert senior Elixir engineer.
- When writing code, use Elixir, Phoenix, Docker, PostgreSQL, Tailwind CSS, LeftHook, Sobelow, Credo, Ecto, ExUnit, Plug, Phoenix LiveView, Phoenix LiveDashboard, Gettext, Jason, Swoosh, Finch, DNS Cluster, File System Watcher, Release Please and ExCoveralls.
</instructions>
<examples> Example usage:
User: "Review this code for elixir best practices"
Agent: [Analyzes code against consolidated guidelines and provides specific feedback]</examples>
Elixir Language Patterns
Pattern Matching
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
endWith 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} }
endGuards
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_dataPipe Operator
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}
endOTP (Open Telecom Platform) Patterns
GenServer
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
endSupervisor
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
endSupervisor 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 it
Agent
For 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)Phoenix Framework
Controllers
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
endPhoenix LiveView
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
endPubSub 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)}
endChannels
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
endEcto Database Patterns
Schemas and Changesets
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
endQueries
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]Transactions
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)Testing with ExUnit
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
endDeployment Best Practices
Releases
Use Elixir releases for production:
# mix.exs
def project do
[
releases: [
myapp: [
include_executables_for: [:unix],
steps: [:assemble, :tar]
]
]
]
endBuild and deploy:
MIX_ENV=prod mix release
_build/prod/rel/myapp/bin/myapp startConfiguration
# 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")
endHealth Checks
# 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
endConsolidated Skills
This expert skill consolidates 1 individual skills:
- elixir-expert
Iron Laws
1. ALWAYS use pattern matching and guards for control flow instead of nested if/case — idiomatic Elixir communicates intent through pattern matching; imperative conditionals fight the language. 2. NEVER use shared mutable state — always communicate through message passing between processes; shared mutable state in Elixir requires explicit ETS or Agent, which should be the exception not the rule. 3. ALWAYS use supervision trees for fault tolerance — never spawn bare processes that aren't supervised; unsupervised processes crash silently without recovery. 4. NEVER use Enum functions on potentially large streams — use Stream for lazy evaluation to avoid loading entire collections into memory. 5. ALWAYS write doctests (iex> examples in @doc) for public functions — doctests are runnable specifications; they document behavior and serve as regression tests.
Anti-Patterns
| 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 |
Memory Protocol (MANDATORY)
Before starting:
cat .claude/context/memory/learnings.mdAfter completing: Record any new patterns or exceptions discovered.
ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.
Invoke the elixir-expert skill and follow it exactly as presented to you
'use strict';
/**
* Post-execute hook for elixir-expert
* Auto-generated by enterprise-bundle-scaffolder
*
* Records metrics after skill execution.
*/
function postExecute(_context) {
// Record execution metrics
return { ok: true, skill: 'elixir-expert' };
}
module.exports = { postExecute };
'use strict';
/**
* Pre-execute hook for elixir-expert
* Auto-generated by enterprise-bundle-scaffolder
*
* Validates inputs before skill execution.
*/
function preExecute(context) {
// Validate skill invocation context
if (!context || typeof context !== 'object') {
return { allow: true, message: 'elixir-expert: no context to validate' };
}
return { allow: true };
}
module.exports = { preExecute };
elixir-expert Research Requirements
Generated: 2026-02-28
Skill Description
Elixir and Phoenix expert including OTP, Ecto, and functional programming
Research Areas
- Current best practices for elixir-expert
- Industry standards and tooling
- Integration patterns
Source References
- To be populated by skill-updater research phase
elixir-expert Rules
Purpose
Elixir and Phoenix expert including OTP, Ecto, and functional programming
Best Practices
- Follow domain-specific conventions
- Apply patterns consistently
- Prioritize type safety and testing
Integration Points
See SKILL.md for complete documentation.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "elixir-expertInput",
"description": "Input schema for Elixir and Phoenix expert including OTP, Ecto, and functional programming",
"type": "object",
"additionalProperties": true,
"properties": {
"target": {
"type": "string",
"description": "Target file or path for the skill to operate on"
},
"options": {
"type": "object",
"description": "Additional options for skill execution",
"additionalProperties": true
}
}
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "elixir-expertOutput",
"type": "object",
"additionalProperties": true,
"properties": {
"ok": {
"type": "boolean"
},
"summary": {
"type": "string"
}
}
}
#!/usr/bin/env node
'use strict';
/**
* elixir-expert - Enterprise Skill Script
* Auto-generated by enterprise-bundle-scaffolder
*/
const fs = require('fs');
const path = require('path');
// Parse arguments
const args = process.argv.slice(2);
const options = {};
for (let i = 0; i < args.length; i++) {
if (args[i].startsWith('--')) {
const key = args[i].slice(2);
const value = args[i + 1] && !args[i + 1].startsWith('--') ? args[++i] : true;
options[key] = value;
}
}
if (options.help) {
console.log(`
elixir-expert - Enterprise Skill
Usage:
node main.cjs --check <file> Check a file against guidelines
node main.cjs --list List all guidelines
node main.cjs --help Show this help
Description:
Elixir and Phoenix expert including OTP, Ecto, and functional programming
`);
process.exit(0);
}
if (options.list) {
console.log('Guidelines for elixir-expert:');
console.log('See SKILL.md for full guidelines');
process.exit(0);
}
console.log('elixir-expert skill loaded. Use with Claude for code review.');
elixir-expert Implementation Template
Goal
- Define target outcome and acceptance criteria.
TDD
1. Red 2. Green 3. Refactor
Verification
- lint
- format
- targeted tests