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 | MVC patterns, authentication, WebSockets |
| LiveView components, forms | liveview | Real-time UI, hooks, streams |
| Schemas, queries, migrations | ecto-database | Associations, changesets, transactions |
| Safe migrations, schema changes | safe-ecto-migrations | Concurrent indexes, zero-downtime DDL |
| GenServer, Supervisor, processes | otp-patterns | Fault tolerance, state management |
| Domain modeling, layering, value objects | functional-modeling | Layers, parse-don't-validate, DDD |
| GraphQL schemas, resolvers | graphql-absinthe | 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 the Lifecycle and Workers layers
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 the Boundary layer
- Once data is parsed, it's always valid throughout the system
- Prefer constructors that return
{:ok, value} | {:error, reason}at that layer. Constructors below it take already-parsed values and stay total.
Error Handling Philosophy
- Tagged tuples belong where a question can be answered "no", which is the Boundary layer. Pure computation below it returns plain values. See functional-modeling.
- 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
Scan to join WeChat group