Skip to content
Backend
Skill

/elixir

Write and refactor idiomatic Elixir functions, modules, and data structures. Use for pattern matching, control flow, error handling, protocols, behaviours, and deciding whether a process is needed. Use otp for process design and supervision.

From plugin
elixir-agent-tools
1715 skills
Install
$ npx -y skills add georgeguimaraes/claude-code-elixir --skill elixir --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/elixir

Context preview

The summary Claude sees to decide when to auto-load this skill.

Write and refactor idiomatic Elixir functions, modules, and data structures. Use for pattern matching, control flow, error handling, protocols, behaviours, and deciding whether a process is needed. Use otp for process design and supervision.

SKILL.md

elixir.SKILL.md
name: elixir
description: Write and refactor idiomatic Elixir functions, modules, and data structures. Use for pattern matching, control flow, error handling, protocols, behaviours, and deciding whether a process is needed. Use otp for process design and supervision.

Elixir

Design modules, model data, and handle errors with Elixir's functional idioms.

The Iron Law

NO PROCESS WITHOUT A RUNTIME REASON

Before creating a GenServer, Agent, or any process, answer YES to at least one: 1. Do I need mutable state persisting across calls? 2. Do I need concurrent execution? 3. Do I need fault isolation?

**All three are NO?** Use plain functions. Modules organize code; processes manage runtime.

The Three Decoupled Dimensions

OOP couples behavior, state, and mutability together. Elixir decouples them:

| OOP Dimension | Elixir Equivalent | |---------------|-------------------| | Behavior | Modules (functions) | | State | Data (structs, maps) | | Mutability | Processes (GenServer) |

Pick only what you need. "I only need data and functions" = no process needed.

"Let It Crash" = "Let It Heal"

The misconception: Write careless code. The truth: Supervisors START processes.

  • Handle expected errors explicitly (`{:ok, _}` / `{:error, _}`)
  • Let unexpected errors crash → supervisor restarts

Control Flow

**Pattern matching first:**

  • Match on function heads instead of `if/else` or `case` in bodies
  • `%{}` matches ANY map—use `map_size(map) == 0` guard for empty maps
  • Avoid nested `case`—refactor to single `case`, `with`, or separate functions

**Error handling:**

  • Use `{:ok, result}` / `{:error, reason}` for operations that can fail
  • Avoid raising exceptions for control flow
  • Use `with` for chaining `{:ok, _}` / `{:error, _}` operations

**Be explicit about expected cases:**

  • Avoid `_ -> nil` catch-alls—they silently swallow unexpected cases
  • Avoid `value && value.field` nil-punning—obscures actual return types
  • When a case has `{:ok, nil} -> nil` alongside `{:ok, value} -> value.field`, use `with` instead:
# Verbose
case get_run(id) do
  {:ok, nil} -> nil
  {:ok, run} -> run.recommendations
end

# Prefer
with {:ok, %{recommendations: recs}} <- get_run(id), do: recs

Polymorphism

| For Polymorphism Over... | Use | Contract | |--------------------------|-----|----------| | Modules | Behaviors | Upfront callbacks | | Data | Protocols | Upfront implementations | | Processes | Message passing | Implicit (send/receive) |

**Behaviors** = default for module polymorphism (very cheap at runtime) **Protocols** = only when composing data types, especially built-ins **Message passing** = only when stateful by design (IO, file handles)

Use the simplest abstraction: pattern matching → anonymous functions → behaviors → protocols → message passing. Each step adds complexity.

**When justified:** Library extensibility, multiple implementations, test swapping. **When to stay coupled:** Internal module, single implementation, pattern matching handles all cases.

Data Modeling Replaces Class Hierarchies

OOP: Complex class hierarchy + visitor pattern. Elixir: Model as data + pattern matching + recursion.

{:sequence, {:literal, "rain"}, {:repeat, {:alternation, "dogs", "cats"}}}

def interpret({:literal, text}, input), do: ...
def interpret({:sequence, left, right}, input), do: ...
def interpret({:repeat, pattern}, input), do: ...

Defaults and Options

Use `/3` variants (`Keyword.get/3`, `Map.get/3`) instead of case statements branching on `nil`:

# WRONG
case Keyword.get(opts, :chunker) do
  nil -> chunker()
  config -> parse_chunker_config(config)
end

# RIGHT
Keyword.get(opts, :chunker, :default) |> parse_chunker_config()

Don't create helper functions to merge config defaults. Inline the fallback:

# WRONG
defp merge_defaults(opts), do: Keyword.merge([repo: Application.get_env(:app, :repo)], opts)

# RIGHT
def some_function(opts) do
  repo = opts[:repo] || Application.get_env(:app, :repo)
end

Idioms

  • Process dictionary is typically unidiomatic—pass state explicitly
  • Reserve `is_thing` names for guards only
  • Use structs over maps when shape is known: `defstruct [:name, :age]`
  • Prepend to lists `[new | list]` not `list ++ [new]`
  • Use `dbg/1` for debugging—prints formatted value with context
  • Use built-in `JSON` module (Elixir 1.18+) instead of Jason

Verification

**Inside coding agents, always prefix `mix` commands with `unbuffer`** to get ANSI colors and prevent stdout block-buffering in non-TTY environments (e.g. `unbuffer mix test`). Install: `brew install expect` (macOS) or `apt install expect` (Linux). If `unbuffer` is unavailable, report the missing prerequisite instead of silently dropping it.

After changing Elixir code, verify the completed change before reporting it as done. Run commands from the relevant Mix project using its pinned Elixir/OTP versions. Follow the repository's contribution instructions and existing check aliases. Prefer an alias when it covers the checks below, and run any uncovered checks separately:

1. Format changed files with `unbuffer mix format path/to/file.ex path/to/test.exs`, following the project's formatter configuration. 2. Compile with `unbuffer mix compile --warnings-as-errors` to catch compilation errors and warnings. 3. Run relevant tests with `unbuffer mix test test/path/to/affected_test.exs`. Run the broader suite when the change affects shared behavior or the repository requires it. 4. Run `unbuffer mix credo` when Credo is configured, using the repository's flags and configuration.

Fix failures introduced by the change and rerun the affected checks. Report the commands actually run and their results, including any checks that were skipped or blocked and why. An unrun or blocked check hasn't passed.

Testing

**Prefer pattern matching over imperative assertions.** Never use `assert length` + `Enum.at`/`List.last`/`hd`. Pattern match checks le

Read more
Ships withelixir-agent-tools

Elixir development guidance for coding agents, with optional Mix checks and Expert language server integration. Install the elixir-dev plugin for five skills covering language idioms, Phoenix interfaces, Ecto persistence, OTP processes, and Oban jobs.

Get the whole plugin
Stats
171
Stars
15
Forks
Active
Maintenance
Python
Language
Apache-2.0
License
19h ago
Last commit
8mo ago
Created

Repo: georgeguimaraes/claude-code-elixir

Other skills on elixir-agent-tools.