Elixir
Included with Lifetime
$97 forever
Execute these commands after EVERY implementation (see AGENT_AUTOMATION module for full workflow).
languageslanguageslanguage
What this skill does
<!-- ELIXIR:START -->
# Elixir Project Rules
## Agent Automation Commands
**CRITICAL**: Execute these commands after EVERY implementation (see AGENT_AUTOMATION module for full workflow).
```bash
# Complete quality check sequence:
mix format --check-formatted # Format check
mix credo --strict # Linting
mix dialyzer # Type checking
mix test # All tests (100% pass)
mix test --cover # Coverage (95%+ required)
mix compile --warnings-as-errors # Build
# Security audit:
mix hex.audit # Vulnerability scan
mix hex.outdated # Check outdated deps
```
## Elixir Configuration
**CRITICAL**: Use Elixir 1.16+ with OTP 26+.
- **Version**: Elixir 1.16+
- **OTP**: 26+
- **Formatter**: Built-in `mix format`
- **Linter**: Credo
- **Type Checker**: Dialyzer
### mix.exs Requirements
```elixir
defmodule YourProject.MixProject do
use Mix.Project
def project do
[
app: :your_project,
version: "1.0.0",
elixir: "~> 1.16",
start_permanent: Mix.env() == :prod,
deps: deps(),
# Documentation
name: "Your Project",
source_url: "https://github.com/your-org/your-project",
docs: [
main: "readme",
extras: ["README.md", "CHANGELOG.md"]
],
# Testing
test_coverage: [tool: ExCoveralls],
preferred_cli_env: [
coveralls: :test,
"coveralls.detail": :test,
"coveralls.post": :test,
"coveralls.html": :test
],
# Dialyzer
dialyzer: [
plt_add_apps: [:mix, :ex_unit],
plt_file: {:no_warn, "priv/plts/dialyzer.plt"},
flags: [:error_handling, :underspecs]
]
]
end
def application do
[
extra_applications: [:logger],
mod: {YourProject.Application, []}
]
end
defp deps do
[
# Development & Testing
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
{:excoveralls, "~> 0.18", only: :test},
{:ex_doc, "~> 0.31", only: :dev, runtime: false}
]
end
end
```
## Code Quality Standards
### Mandatory Quality Checks
**CRITICAL**: After implementing ANY feature, you MUST run these commands in order.
**IMPORTANT**: These commands MUST match your GitHub Actions workflows to prevent CI/CD failures!
```bash
# Pre-Commit Checklist (MUST match .github/workflows/*.yml)
# 1. Format check (matches workflow - use --check-formatted!)
mix format --check-formatted
# 2. Lint (MUST pass with no warnings - matches workflow)
mix credo --strict
# 3. Type check with Dialyzer (matches workflow)
mix dialyzer
# 4. Run all tests (MUST pass 100% - matches workflow)
mix test --cover
# 5. Check coverage (MUST meet threshold)
mix test --cover --export-coverage default
mix test.coverage
# If ANY fails: ❌ DO NOT COMMIT - Fix first!
```
**If ANY of these fail, you MUST fix the issues before committing.**
**Why This Matters:**
- CI/CD failures happen when local commands differ from workflows
- Example: Using `mix format` locally but `mix format --check-formatted` in CI = failure
- Example: Missing `--cover` flag = CI coverage failures
### Formatting
- Use built-in `mix format`
- Configuration in `.formatter.exs`
- Format before committing: `mix format`
Example `.formatter.exs`:
```elixir
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
line_length: 100
]
```
### Linting
- Use Credo for code analysis
- Configuration in `.credo.exs`
- Must pass strict mode: `mix credo --strict`
Example `.credo.exs`:
```elixir
%{
configs: [
%{
name: "default",
files: %{
included: ["lib/", "test/"],
excluded: [~r"/_build/", ~r"/deps/"]
},
strict: true,
color: true,
checks: %{
enabled: [
{Credo.Check.Readability.ModuleDoc, []},
{Credo.Check.Design.AliasUsage, priority: :low}
]
}
}
]
}
```
### Testing
- **Framework**: ExUnit (built-in)
- **Location**: `test/` directory
- **Coverage**: ExCoveralls
- **Coverage Threshold**: 95%+
Example test structure:
```elixir
defmodule YourProject.MyModuleTest do
use ExUnit.Case, async: true
doctest YourProject.MyModule
describe "function_name/1" do
test "handles valid input" do
assert YourProject.MyModule.function_name("input") == {:ok, "result"}
end
test "returns error for invalid input" do
assert YourProject.MyModule.function_name("") == {:error, :invalid_input}
end
end
end
```
### Type Specifications
- Use `@spec` for all public functions
- Use `@type` for custom types
- Run Dialyzer regularly
Example:
```elixir
defmodule YourProject.MyModule do
@moduledoc """
Documentation for MyModule.
"""
@type result :: {:ok, String.t()} | {:error, atom()}
@spec process(String.t()) :: result()
def process(input) when is_binary(input) and input != "" do
{:ok, String.upcase(input)}
end
def process(_), do: {:error, :invalid_input}
end
```
## Documentation
- Use `@moduledoc` for module documentation
- Use `@doc` for function documentation
- Include examples with doctests
- Generate docs with `mix docs`
Example:
```elixir
defmodule YourProject.MyModule do
@moduledoc """
Provides functionality for processing data.
## Examples
iex> YourProject.MyModule.process("hello")
{:ok, "HELLO"}
"""
@doc """
Processes the input string.
Returns `{:ok, result}` on success or `{:error, reason}` on failure.
## Examples
iex> YourProject.MyModule.process("test")
{:ok, "TEST"}
iex> YourProject.MyModule.process("")
{:error, :invalid_input}
"""
@spec process(String.t()) :: {:ok, String.t()} | {:error, atom()}
def process(input) when is_binary(input) and input != "" do
{:ok, String.upcase(input)}
end
def process(_), do: {:error, :invalid_input}
end
```
## Project Structure
```
project/
├── mix.exs # Project configuration
├── .formatter.exs # Formatter configuration
├── .credo.exs # Credo configuration
├── README.md # Project overview (allowed in root)
├── CHANGELOG.md # Version history (allowed in root)
├── LICENSE # Project license (allowed in root)
├── lib/
│ ├── your_project.ex # Main module
│ └── your_project/
│ ├── application.ex # OTP application
│ └── ...
├── test/
│ ├── test_helper.exs # Test configuration
│ └── your_project/
│ └── ...
├── config/
│ ├── config.exs # General config
│ ├── dev.exs # Development config
│ ├── test.exs # Test config
│ └── prod.exs # Production config
├── priv/ # Private assets
└── docs/ # Project documentation
```
## Error Handling
- Use tagged tuples: `{:ok, value}` and `{:error, reason}`
- Use `with` for multiple operations
- Create custom error modules when needed
Example:
```elixir
defmodule YourProject.Errors do
defmodule ValidationError do
defexception [:message, :field]
end
end
defmodule YourProject.MyModule do
alias YourProject.Errors.ValidationError
def validate(data) do
with {:ok, cleaned} <- clean_data(data),
{:ok, validated} <- check_format(cleaned) do
{:ok, validated}
else
{:error, :empty} ->
raise ValidationError, message: "Data cannot be empty", field: :data
{:error, reason} ->
{:error, reason}
end
end
end
```
## OTP Best Practices
- Use Supervisors for fault tolerance
- Implement GenServers for stateful processes
- Use Task for concurrent operations
Example Supervisor:
```elixir
defmodule YourProject.Application do
use Application
@impl true
def start(_type, _args) do
children = [
{YourProject.MyWorker, []},
{Task.Supervisor, name: YourProject.TaskSupervisor}
]
opts = [strategy: :one_for_one, name: YourProject.Supervisor]Related in languages
csharp-expert
IncludedExpert-level C# development with .NET 8+, ASP.NET Core, LINQ, async/await, and enterprise patterns
languages
java-expert
IncludedExpert-level Java development with Java 21+ features, Spring Boot, Maven/Gradle, and enterprise best practices
languages
pcl-expert
IncludedExpert in Persona Control Language (PCL) - language design, compiler architecture, runtime systems, and ecosystem development
languages
php-expert
IncludedExpert-level PHP development with PHP 8+, Laravel, Composer, and modern best practices
languages
rust-expert
IncludedExpert-level Rust development with ownership, lifetimes, async, error handling, and production-grade patterns
languages
go-expert
IncludedExpert-level Go development with Go 1.22+ features, concurrency, standard library, and production-grade best practices
languages