Clean Code
jd-solanki/slidev-theme-dracula
Write readable, maintainable code through disciplined naming, small functions, and clean error handling.
Writes and refactors idiomatic Elixir modules and functions, with rules for pattern matching, error handling, protocols and when a process is really needed.
$ npx skills add georgeguimaraes/elixir-agent-tools --skill elixir -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install georgeguimaraes/elixir-agent-tools elixir --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/georgeguimaraes/elixir-agent-tools.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/elixir-dev/skills/elixir .claude/skills/elixir && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "elixir" agent skill from https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/elixir into .claude/skills/elixir/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "elixir", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/elixirType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add georgeguimaraes/elixir-agent-tools --skill elixir -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install georgeguimaraes/elixir-agent-tools elixir --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/georgeguimaraes/elixir-agent-tools.git skills-src && mkdir -p .agents/skills && cp -r skills-src/plugins/elixir-dev/skills/elixir .agents/skills/elixir && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "elixir" agent skill from https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/elixir into .agents/skills/elixir/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "elixir", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add georgeguimaraes/elixir-agent-tools --skill elixir -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install georgeguimaraes/elixir-agent-tools elixir --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/georgeguimaraes/elixir-agent-tools.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/plugins/elixir-dev/skills/elixir .cursor/skills/elixir && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "elixir" agent skill from https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/elixir into .cursor/skills/elixir/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "elixir", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/georgeguimaraes/elixir-agent-tools.git --path plugins/elixir-dev/skills/elixir--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add georgeguimaraes/elixir-agent-tools --skill elixir -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install georgeguimaraes/elixir-agent-tools elixir --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/georgeguimaraes/elixir-agent-tools.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/plugins/elixir-dev/skills/elixir .gemini/skills/elixir && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "elixir" agent skill from https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/elixir into .gemini/skills/elixir/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "elixir", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install georgeguimaraes/elixir-agent-tools elixirInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add georgeguimaraes/elixir-agent-tools --skill elixir -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/georgeguimaraes/elixir-agent-tools.git skills-src && mkdir -p .github/skills && cp -r skills-src/plugins/elixir-dev/skills/elixir .github/skills/elixir && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "elixir" agent skill from https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/elixir into .github/skills/elixir/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "elixir", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add georgeguimaraes/elixir-agent-tools --skill elixir -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install georgeguimaraes/elixir-agent-tools elixir --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/georgeguimaraes/elixir-agent-tools.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/plugins/elixir-dev/skills/elixir .opencode/skills/elixir && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "elixir" agent skill from https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/elixir into .opencode/skills/elixir/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "elixir", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
elixirWrites and refactors idiomatic Elixir modules and functions, with rules for pattern matching, error handling, protocols and when a process is really needed.
Its guiding rule is no process without a runtime reason: before creating a GenServer, Agent or any process, the code must need persistent mutable state, concurrent execution or fault isolation. If all three answers are no, plain functions and modules are the answer, since modules organize code and processes manage runtime. A table contrasts OOP's coupling of behavior, state and mutability with Elixir's split into modules, data and processes.
Control flow starts with pattern matching in function heads rather than if and case in bodies, notes that an empty map pattern matches any map, avoids nested case in favor of with or separate functions, and uses ok and error tuples for expected failures instead of exceptions. It warns against catch-all clauses that return nil and against nil-punning with the and operator. Let it crash is explained as supervisors restarting processes after unexpected errors.
For polymorphism it prefers behaviours for modules, protocols only when composing data types and message passing only for state that is stateful by design, using the simplest abstraction that works. Process design and supervision are handed to a separate otp skill.
3 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit ac3a5a1. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
brewaptFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Idiomatic Elixir loads about 2.1k tokens when it runs. Until then it costs about 62 tokens; SKILL.md has 879 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from georgeguimaraes/elixir-agent-tools at commit ac3a5a1, republished under its Apache-2.0 licence (© georgeguimaraes). 879 words, ~2,079 tokens.
.claude/skills/elixir/SKILL.md (or your agent's skills folder).Design modules, model data, and handle errors with Elixir's functional idioms.
NO PROCESS WITHOUT A RUNTIME REASONBefore creating a GenServer, Agent, or any process, answer YES to at least one:
All three are NO? Use plain functions. Modules organize code; processes manage runtime.
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.
The misconception: Write careless code. The truth: Supervisors START processes.
{:ok, _} / {:error, _})Pattern matching first:
if/else or case in bodies%{} matches ANY map—use map_size(map) == 0 guard for empty mapscase—refactor to single case, with, or separate functionsError handling:
{:ok, result} / {:error, reason} for operations that can failwith for chaining {:ok, _} / {:error, _} operationsBe explicit about expected cases:
_ -> nil catch-alls—they silently swallow unexpected casesvalue && value.field nil-punning—obscures actual return types{: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| 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.
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: ...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)
endis_thing names for guards onlydefstruct [:name, :age][new | list] not list ++ [new]dbg/1 for debugging—prints formatted value with contextJSON module (Elixir 1.18+) instead of JasonInside 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:
unbuffer mix format path/to/file.ex path/to/test.exs, following the project's formatter configuration.unbuffer mix compile --warnings-as-errors to catch compilation errors and warnings.unbuffer mix test test/path/to/affected_test.exs. Run the broader suite when the change affects shared behavior or the repository requires it.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.
Prefer pattern matching over imperative assertions. Never use assert length + Enum.at/List.last/hd. Pattern match checks length and content in one shot:
# Bad
assert length(students) == 2
assert Enum.at(students, 0).name == "Alice"
assert Enum.at(students, 1).name == "Bob"
# Good
assert [%{name: "Alice"}, %{name: "Bob"}] = studentsSame goes for type-only predicates: assert is_map(user) / assert is_list(posts) pass for almost any non-error return. Pattern match the shape and content together: assert %User{email: "a@b.com"} = user. is_nil/1 is fine when nil-ness is the whole point.
Test behavior, not implementation. Test use cases / public API. Refactoring shouldn't break tests.
Test your code, not the framework. If deleting your code doesn't fail the test, it's tautological.
Keep tests async. async: false means you've coupled to global state. Fix the coupling:
| Problem | Solution |
|---|---|
Application.put_env | Pass config as function argument |
| Feature flags | Inject via process dictionary or context |
| ETS tables | Create per-test tables with unique names |
| External APIs | Use Mox with explicit allowances |
| File system operations | Use @tag :tmp_dir (see below) |
Use tmp_dir for file tests. ExUnit creates unique temp directories per test, async-safe:
@tag :tmp_dir
test "writes file", %{tmp_dir: tmp_dir} do
path = Path.join(tmp_dir, "test.txt")
File.write!(path, "content")
assert File.read!(path) == "content"
endDirectory is auto-cleaned before each run. Works with @moduletag :tmp_dir for all tests in module.
| Excuse | Reality |
|---|---|
| "I need a process to organize this code" | Modules organize code. Processes are for runtime. |
| "GenServer is the Elixir way" | Plain functions are also the Elixir way. |
| "I'll need state eventually" | YAGNI. Add process when you need it. |
| "It's just a simple wrapper process" | Simple wrappers become bottlenecks. |
| "This is how I'd structure it in OOP" | Rethink from data flow. |
Any of these? Re-read The Iron Law.
© georgeguimaraes, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in plugins/elixir-dev/skills/elixir of georgeguimaraes/elixir-agent-tools.
Open the folder on GitHubat commit ac3a5a1
Idiomatic Elixir next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Idiomatic Elixir this skillgeorgeguimaraes/elixir-agent-tools | 184 | — | ~2.1k | Automated safety check: Pass | Apache-2.0 | |
| Clean Codejd-solanki/slidev-theme-dracula | 161 | 1 repos | ~3.8k | Automated safety check: Pass | MIT | |
| Elixir AntipatternsGentleman-Programming/Gentleman-Skills | 657 | — | ~2.1k | Automated safety check: Pass | MIT | |
| fp-go Functional Programming for GoIBM/fp-go | 2k | — | ~8.5k | Automated safety check: Pass | Apache-2.0 | |
| Swiftui View RefactorDimillian/Skills | 4k | 5 repos | ~2k | Automated safety check: Pass | MIT | |
| Rust Best Practicesfarm-fe/farm | 5.6k | 3 repos | ~1.1k | Automated safety check: Pass | MIT |
jd-solanki/slidev-theme-dracula
Write readable, maintainable code through disciplined naming, small functions, and clean error handling.
Gentleman-Programming/Gentleman-Skills
Core catalog of 8 critical Elixir/Phoenix anti-patterns covering error handling, separation of concerns, Ecto queries, and testing.
IBM/fp-go
Entry point for writing and reviewing Go code with the fp-go v2 library: core monad types, data-last composition, type parameter order and import conventions.
Dimillian/Skills
Refactor and review SwiftUI view files with strong defaults for small dedicated subviews, MV-over-MVVM data flow, stable view trees, explicit dependency injection, and correct Observation usage.
farm-fe/farm
Guide for writing idiomatic Rust code based on Apollo GraphQL's best practices handbook.
rtk-ai/rtk
Describes seven Rust design patterns for the RTK CLI filter modules, with when to use each, RTK examples, and notes on when a pattern is overkill.
georgeguimaraes/elixir-agent-tools
Designs and debugs Elixir persistence with Ecto: schemas, changesets, queries, preloads, migrations and multi-tenancy, kept within application contexts.
georgeguimaraes/elixir-agent-tools
Guidance for building and debugging durable background jobs in Elixir with Oban and Oban Pro, covering serialization, retries, uniqueness, chaining, chunking and workflows.
georgeguimaraes/elixir-agent-tools
Guides choices among GenServer, Supervisor, Task, Registry, ETS and Broadway when designing or debugging Elixir concurrency, state and fault recovery.
georgeguimaraes/elixir-agent-tools
Guidance for building and debugging Phoenix web interfaces: where LiveView loads data, scopes, PubSub topics, external polling and component state.
Works with
Categories
Writes and refactors idiomatic Elixir modules and functions, with rules for pattern matching, error handling, protocols and when a process is really needed. Its guiding rule is no process without a runtime reason: before creating a GenServer, Agent or any process, the code must need persistent mutable state, concurrent execution or fault isolation. If all three answers are no, plain functions and modules are the answer, since modules organize code and processes manage runtime.
Idiomatic Elixir fits situations like: writing a new Elixir module with clear data structures and functions; refactoring nested case expressions into with or function heads; deciding whether a feature needs a GenServer at all; choosing between behaviours and protocols.
Run `npx skills add georgeguimaraes/elixir-agent-tools --skill elixir -a claude-code`. Or copy the skill folder (plugins/elixir-dev/skills/elixir in georgeguimaraes/elixir-agent-tools) into .claude/skills/elixir in your project. Claude Code loads it when a task matches its description.
Run `npx skills add georgeguimaraes/elixir-agent-tools --skill elixir -a codex`. Or copy the skill folder (plugins/elixir-dev/skills/elixir in georgeguimaraes/elixir-agent-tools) into .agents/skills/elixir in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add georgeguimaraes/elixir-agent-tools --skill elixir -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/elixir, .gemini/skills/elixir, .github/skills/elixir and .opencode/skills/elixir in your project.
Going by SKILL.md and its folder, Idiomatic Elixir needs the command-line tools its instructions call (brew and apt). Our summary lists: Elixir and Mix for compiling and testing the code.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Idiomatic Elixir is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 2.1k tokens (SKILL.md is roughly 8.3k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Idiomatic Elixir: Clean Code (jd-solanki/slidev-theme-dracula, 161 stars), Elixir Antipatterns (Gentleman-Programming/Gentleman-Skills, 657 stars), fp-go Functional Programming for Go (IBM/fp-go, 2k stars) and Swiftui View Refactor (Dimillian/Skills, 4k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
georgeguimaraes (a GitHub user) maintains it in georgeguimaraes/elixir-agent-tools, which has 184 GitHub stars. The repository holds 5 skills in this directory. The repository was last updated on September 20, 2026.
Source: georgeguimaraes/elixir-agent-tools on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.