programming-elixir · diff
git:20260421.b8af739 to git:20260503.f77bcc0
0 added, 13 removed. Audit A to A.
---
name: programming-elixir
title: "Elixir Development"
description: "Elixir/OTP conventions, pattern matching, GenServer, supervision trees, Phoenix, and functional programming best practices. Auto-activates in Elixir projects."
license: Apache-2.0
compatibility: "Requires Elixir and Erlang/OTP."
domains: developer
rules:
- file(mix.exs)
- content(elixir)
---
# Elixir Development
## Conventions
- Idiomatic Elixir — pattern matching over conditionals, pipes over nesting
- "Let it crash" — don't defensively catch errors; let supervisors restart
- `{:ok, _}` / `{:error, _}` tuples — NOT exceptions for control flow
- Immutability — all data is immutable; "updating" returns new data
- OTP first — use GenServer/Supervisor/Agent/Task
- `@impl true` on ALL behaviour callbacks
- `defp` for internal functions — only expose what's needed
- `@spec` on all public functions
## Pattern Matching
- Multi-clause functions over `if`/`else`/`case` nesting
- Destructure in function arguments — not inside body
- Guards (`when is_binary/is_integer`) for type constraints
## Pipe Operator
- Data flows left to right: `user |> Map.get(:email) |> String.downcase()`
- First argument is always piped
- Use `then/1` when piped value isn't the first argument
## Error Handling
- `with` blocks for chaining failable operations
- `{:ok, _}` / `{:error, _}` for expected failures
- `raise`/`rescue` ONLY at system boundaries
- Provide context: `{:error, {:not_found, id}}`
## OTP Patterns
- GenServer: always provide client API wrapping `GenServer.call`/`cast`
- Supervisor: `:one_for_one` default, `:rest_for_one` for dependent children
- Agent: for simple state when GenServer is overkill
- Task / Task.Supervisor: for async one-off work
- DynamicSupervisor: for children started at runtime
## Testing
- ExUnit with `async: true` for concurrent tests
- `describe` blocks to group related tests
- `setup` / `setup_all` for fixtures
- `assert` with pattern matching: `assert {:ok, %User{}} = fetch_user(id)`
- Mox for behaviour-based mocking
## Common Pitfalls
- `%{map | key: val}` only updates EXISTING keys — use `Map.put/3` for new
- Atoms from user input are never GC'd — use `String.to_existing_atom/1`
- Enum vs Stream: use Stream for large/lazy collections
- ## Tooling
-
- | Command | Purpose |
- |---------|---------|
- | `mix compile` | Compile |
- | `mix test` | Run tests |
- | `mix format` | Format code |
- | `mix format --check-formatted` | CI format check |
- | `mix credo --strict` | Lint |
- | `mix dialyzer` | Static type analysis |
- | `mix deps.get` | Fetch dependencies |
- | `mix phx.server` | Phoenix dev server |
-