QFM070: Elixir Reading List - June 2025
Source: Photo by Chris Ried on Unsplash
This month's Elixir Reading List features an extensive Ash Framework tutorial series and core Elixir fundamentals. The multi-part Ash Framework for Phoenix Developers series covers domains, resources, relationships, and aggregations. Phoenix.new: The Remote AI Runtime introduces Phoenix's AI development capabilities.
Control flow tutorials cover with, case and cond, and if and unless.
As always, the Quantum Fax Machine Propellor Hat Key will guide your browsing. Enjoy!

Links
Elixir's case and cond expressions provide powerful control flow mechanisms beyond simple if/else logic: case enables pattern matching against structured data like tuples and maps, allowing you to destructure values and apply guards in a single operation, while cond evaluates multiple boolean conditions sequentially with better error handling than traditional if-else chains. Both constructs are expressions that return values, making them fundamental for writing clean, maintainable code when handling complex business logic, error handling, and state transitions in Elixir applications.
The author builds a minimal blog using pure Elixir with Plug and Cowboy as a lightweight web server, eliminating the need for Phoenix or a traditional database. Notion's API serves as the CMS backend, with the application using only four dependencies (Cowboy, Plug, HTTPoison, and Jason) to fetch and display blog content from a Notion database. The setup involves creating a basic Elixir application with a Plug.Router for routing and a Cowboy HTTP server listening on port 4000, while the Notion integration requires configuring a database with Title, Date, and Body fields.
Phoenix contexts are simply Elixir modules that group conceptually related functions together, serving as the primary organizational pattern for core business logic in the lib/<app_name> directory. Phoenix codebases split functionality into two directories—lib/<app_name> for app-agnostic business logic (database queries, permissions, notifications) and lib/<app_name>_web for web-specific concerns (HTTP handling, routing, HTML rendering)—and contexts define the boundaries between these layers by providing a clean interface through which the web layer communicates with the application layer.
This article introduces a 20-part series on Ash Framework for Phoenix developers, emphasizing the need to "think in Ash" to fully leverage the framework's productivity and flexibility gains. The author, who previously used Laravel, Django, and other frameworks, demonstrates how adopting Ash's declarative paradigm enabled him to build an HR application in three months that would typically require a four-person team, and the series uses a real-world multitenant help-center application to teach core concepts from domain modeling through authorization and background jobs.
A view model pattern in Phoenix LiveView decouples UI state from domain models by wrapping records in a separate struct that adds UI-specific fields like selected and deleted, avoiding the need to pollute domain models with virtual fields or maintain separate tracking lists during render. The approach provides a clean way to access item state directly in templates (eg item_view.selected) while keeping the original model accessible within the view model for full data access.
The Ash framework uses the manage_relationship change within update actions to handle has_many relationship operations, allowing developers to create related records by specifying an argument containing the related data attributes and the relationship name. To implement this pattern, both the parent resource (Category) must define an update action with manage_relationship(:article_attrs, :articles, type: :create), and the child resource (Article) must accept a :create action with the required fields. The operation is executed by building a changeset with Ash.Changeset.for_update/3 and calling Ash.update/1, which atomically creates the parent record and its related child records in a single transaction.
The with special form in Elixir provides a clean, flat syntax for chaining sequential operations where each step depends on the previous one's success, using pattern matching with the <- operator to bind results and immediately stop execution if any pattern fails to match. An optional else clause enables centralized error handling by pattern matching against non-matching values, replacing the deeply nested conditionals typical in procedural languages. The form supports both pattern matching (<-) and regular assignment (=), allowing developers to write readable, maintainable code for complex multi-step operations that may fail at various points.
Directed Acyclic Graphs (DAGs) are a fundamental pattern used across systems like TensorFlow, Apache Airflow, and rule engines to model data-flow dependencies and enable runtime program modification. By representing program logic as graph data structures rather than direct function calls, DAGs allow for deferred execution and dynamic composition, making them valuable for building expert systems, dynamic data pipelines, and other systems requiring flexible runtime behavior. The document provides an overview of how to implement dataflow graphs in Elixir using lazy evaluation principles.
The article demonstrates how to integrate the Ash framework with Phoenix controllers to build a user interface for business logic, specifically by creating a help center homepage that displays data and uses Ash's aggregation features to group and summarize information. Phoenix handles the presentation layer while Ash manages the backend business domain and logic, with the implementation utilizing controller files and Heex templates to render the aggregated data.
Atoms in Elixir are constants whose value is their own name, forming the foundation for symbolic computation alongside booleans and nil. Unlike strings which represent dynamic data, atoms serve as fixed, efficient identifiers used for state representation, configuration keys, and pattern matching, with the key distinction that atoms with the same name are identical in memory. Effective use of atoms is essential for writing idiomatic Elixir code, particularly in return value tuples, configuration, and control flow operations.
Phoenix.new is an LLM coding agent platform built for Elixir and Phoenix that runs entirely in the browser but provides both the user and agent with root access to an isolated ephemeral virtual machine, allowing agents to install system packages and run programs freely without affecting the local machine. The system includes a full headless browser that lets agents verify front-end changes by observing real page content and JavaScript state, and automatically deploys generated applications to the cloud with shareable URLs and integrated Fly.io infrastructure, eliminating the traditional gap between rapid prototyping and production deployment.
In Ash Framework, domains serve as organizational containers that group related resources, and resources represent database tables with their attributes, relationships, and validations defined declaratively. The framework automatically generates migrations, columns, relationships, and validations from the resource definitions, allowing developers to model their entire database schema through Elixir code rather than manual SQL—exemplified by creating a KnowledgeBase domain containing resources like Category that specify tables, attributes, and relationships with cascading delete rules.
Ash Framework supports many-to-many relationships through a join resource by using the many_to_many macro with a through clause, enabling actions like create_with_tags that accept an array of related resources and automatically manage the join table entries. Relationships can be filtered using nested attribute access (eg tags.name == "issues"), allowing queries to traverse multiple levels of associations to narrow results based on related resource attributes.
In Elixir, if and unless are expressions that return values rather than statements, allowing them to be assigned to variables or used directly in function calls and pipelines. Unlike languages such as JavaScript, only false and nil are falsy in Elixir; all other values including 0 and empty lists are truthy. As of Elixir 1.18, unless is discouraged in favor of using if with negated conditions for improved code clarity and maintainability.
Regards,
M@
[ED: If you'd like to sign up for this content as an email, click here to join the mailing list.]
Originally published on quantumfaxmachine.com and cross-posted on Medium.
hello@matthewsinclair.com | matthewsinclair.com | bsky.app/@matthewsinclair.com | masto.ai/@matthewsinclair | medium.com/@matthewsinclair | xitter/@matthewsinclair
Was this useful?