Back to Skills

elixir-thinking

verified

This skill should be used when the user asks to "implement a feature in Elixir", "refactor this module", "should I use a GenServer here?", "how should I structure this?", "use the pipe operator", "add error handling", "make this concurrent", or mentions protocols, behaviours, pattern matching, with statements, comprehensions, structs, or coming from an OOP background. Contains paradigm-shifting insights.

View on GitHub

Marketplace

claude-code-elixir

georgeguimaraes/claude-code-elixir

Plugin

elixir

skills

Repository

georgeguimaraes/claude-code-elixir
80stars

plugins/elixir/skills/elixir-thinking/SKILL.md

Last Verified

January 23, 2026

Install Skill

Select agents to install to:

Scope:
npx add-skill https://github.com/georgeguimaraes/claude-code-elixir/blob/main/plugins/elixir/skills/elixir-thinking/SKILL.md -a claude-code --skill elixir-thinking

Installation paths:

Claude
.claude/skills/elixir-thinking/
Powered by add-skill CLI

Instructions

# Elixir Thinking

Mental shifts required before writing Elixir. These contradict conventional OOP patterns.

## 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:

```elixir
# Verbose
case get_run(id) do
  {:ok, nil} -> nil
  {:ok, run} -> run.recommendations
end

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

## Poly

Validation Details

Front Matter
Required Fields
Valid Name Format
Valid Description
Has Sections
Allowed Tools
Instruction Length:
5935 chars