SKILL.md
Developing Elixir
Expert guidance for building robust, scalable Elixir/Phoenix applications.
Quick Start
For immediate help, identify your task type and consult the relevant reference:
| Working On | Reference File | Key Topics |
|---|---|---|
| Controllers, routing, plugs, channels | [phoenix-framework](references/phoenix-framework.md) | MVC patterns, authentication, WebSockets |
| LiveView components, forms | [liveview](references/liveview.md) | Real-time UI, hooks, streams |
| Schemas, queries, migrations | [ecto-database](references/ecto-database.md) | Associations, changesets, transactions |
| Safe migrations, schema changes | [safe-ecto-migrations](references/safe-ecto-migrations.md) | Concurrent indexes, zero-downtime DDL |
| GenServer, Supervisor, processes | [otp-patterns](references/otp-patterns.md) | Fault tolerance, state management |
| Domain modeling, value objects | [functional-modeling](references/functional-modeling.md) | Parse-don't-validate, DDD |
| GraphQL schemas, resolvers | [graphql-absinthe](references/graphql-absinthe.md) | Subscriptions, dataloader |
| ExUnit tests, TDD workflow | testing-elixir skill |
Delegated — ExUnit, assertions, Ecto sandbox, Phoenix test helpers |
Cross-Cutting Principles
These principles apply across all Elixir development:
Functional-First Approach
- Prefer pure functions over stateful processes
- Use explicit state passing through function parameters
- Only introduce OTP patterns when tests require them
- Push side effects to system boundaries
Progressive Abstraction
- Start with primitives and maps
- Introduce structs when structure is genuinely needed
- Add type specs when interfaces stabilize
- Extract domain modules when concepts are proven
Parse, Don't Validate
- Transform unstructured data into guaranteed-valid types at boundaries
- Once data is parsed, it's always valid throughout the system
- Prefer constructors that return
{:ok, value} | {:error, reason}
Error Handling Philosophy
- Use tagged tuples consistently:
{:ok, result}or{:error, reason} - Implement proper error types for domain-specific errors
- Never expose internal implementation details in errors
Examples
Creating a Phoenix context with Ecto:
User: "I need to add a checkout feature for orders"
→ Consult ecto-database.md for schema design, phoenix-framework.md for controller
Implementing real-time updates:
User: "Show live order status updates to the customer"
→ Consult liveview.md for socket state and PubSub patterns
Refactoring to proper domain model:
User: "This order calculation has grown complex with many edge cases"
→ Consult functional-modeling.md
Adding background job processing:
User: "Process order confirmations asynchronously"
→ Consult otp-patterns.md for process decisions, phoenix-framework.md for Oban
Writing a database migration:
User: "I need to add an index to the posts table"
→ Consult safe-ecto-migrations.md for concurrent index creation
Anti-Patterns to Avoid
Premature OTP
- GenServer for data that could be function arguments
- Supervisors for processes that don't need restart strategies
- ETS tables for small, static datasets
- Message passing when direct function calls suffice
Premature Abstraction
- Creating types for single-use values
- Building generic solutions for specific problems
- Introducing abstractions without duplication
- Modeling future requirements
Reference File IDs
For programmatic access (e.g., parallel reviews), use these identifiers:
phoenix-framework · liveview · ecto-database · safe-ecto-migrations · otp-patterns · functional-modeling · graphql-absinthe