elixir-guidelines
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.
- Category: discovered
- Tags: github, community
- Version: 1.0.0
- Author: walid
- Downloads: 0
- GitHub stars: 1
- Updated: 2025-11-21
Download the latest version (v1.0.0) from this page.
SKILL.md
---
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)**:
```elixir
i = 0
mylist = ["blue", "green"]
mylist[i] # This will NOT work
```
**Always do this instead**:
```elixir
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**:
```elixir
# INVALID: rebinding inside the `if` - result never gets assigned
if connected?(socket) do
socket = assign(socket, :val, val)
end
```
**Valid approach**:
```elixir
# 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/2` for changesets
## Date and Time Handling
- Elixir's standard library has everything necessary for date and time manipulation
- Familiarize yourself with `Time`, `Date`, `DateTime`, and `Calendar` interfaces
- **Never** install additional dependencies unless asked or for date/time parsing (use `date_time_parser` package if needed)
## Atom Safety
- **Never** use `String.to_atom/1` on 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_thing` should be reserved for guards
- Example: `active?/1` not `is_active/1`
## OTP Primitives
Elixir's builtin OTP primitives like `DynamicSupervisor` and `Registry` require names in the child spec:
```elixir
{DynamicSupervisor, name: MyApp.MyDynamicSup}
```
Then use:
```elixir
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: :infinity` as an option to avoid timeouts on long-running tasks
```elixir
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 --all` is **almost never needed**
- **Avoid** using it unless you have good reason (e.g., corrupted build artifacts)
- Usually `mix deps.get` and `mix deps.compile` are sufficient