| name | elixir-guidelines |
| description | Provides Elixir language best practices and common patterns. Use when writing Elixir code, working with lists, structs, OTP primitives, date/time handling, or debugging Mix tasks. |
Elixir Guidelines
This skill provides Elixir language best practices to help you write correct, idiomatic Elixir code.
List Access
Elixir lists do not support index based access via the access syntax.
Never do this (invalid):
i = 0
mylist = ["blue", "green"]
mylist[i] # This will NOT work
Always do this instead:
i = 0
mylist = ["blue", "green"]
Enum.at(mylist, i) # Use Enum.at, pattern matching, or List module
Variable Rebinding in Block Expressions
Elixir variables are immutable, but can be rebound. For block expressions like if, case, cond, etc., you must bind the result of the expression to a variable if you want to use it. You cannot rebind the result inside the expression.
Invalid approach:
# INVALID: rebinding inside the `if` - result never gets assigned
if connected?(socket) do
socket = assign(socket, :val, val)
end
Valid approach:
# VALID: rebind the result of the `if` to a new variable
socket =
if connected?(socket) do
assign(socket, :val, val)
end
Module Organization
- Never nest multiple modules in the same file as it can cause cyclic dependencies and compilation errors
- Each module should be in its own file
Struct Access
- Never use map access syntax (
changeset[:field]) on structs as they do not implement the Access behaviour by default - For regular structs, you must access fields directly:
my_struct.field - Use higher level APIs when available:
Ecto.Changeset.get_field/2for changesets
Date and Time Handling
- Elixir's standard library has everything necessary for date and time manipulation
- Familiarize yourself with
Time,Date,DateTime, andCalendarinterfaces - Never install additional dependencies unless asked or for date/time parsing (use
date_time_parserpackage if needed)
Atom Safety
- Never use
String.to_atom/1on user input (creates memory leak risk) - Atoms are not garbage collected, so dynamic atom creation can exhaust memory
Naming Conventions
- Predicate function names should not start with
is_and should end in a question mark - Names like
is_thingshould be reserved for guards - Example:
active?/1notis_active/1
OTP Primitives
Elixir's builtin OTP primitives like DynamicSupervisor and Registry require names in the child spec:
{DynamicSupervisor, name: MyApp.MyDynamicSup}
Then use:
DynamicSupervisor.start_child(MyApp.MyDynamicSup, child_spec)
Concurrent Processing
- Use
Task.async_stream(collection, callback, options)for concurrent enumeration with back-pressure - Most times you'll want to pass
timeout: :infinityas an option to avoid timeouts on long-running tasks
Task.async_stream(items, &process_item/1, timeout: :infinity)
|> Enum.to_list()
Mix Guidelines
Reading Documentation
- Read the docs and options before using tasks:
mix help task_name - Mix help provides comprehensive information about available options
Debugging Test Failures
- Run tests in a specific file:
mix test test/my_test.exs - Run all previously failed tests:
mix test --failed - Run a specific test by line number:
mix test test/my_test.exs:42
Dependency Management
mix deps.clean --allis almost never needed- Avoid using it unless you have good reason (e.g., corrupted build artifacts)
- Usually
mix deps.getandmix deps.compileare sufficient