Ash Framework: How It Works and When To Use It?

Adding a feature is often the easy part. Keeping its rules consistent across a LiveView screen, an API, and a background job takes more work.
That’s the problem Ash Framework tries to solve. You describe your resources and the actions people can take on them, then reuse those definitions across your application. Its extensions connect that logic to forms, databases, APIs, and AI tools.
For an Elixir team, that can mean less repeated code and fewer places to update when a rule changes. But there is a learning curve. Ash has its own way of organizing an application, and your team needs time to understand it.
So, is it worth adding another framework? Let’s look at what Ash actually does, where it helps, and what to try before committing.
Table of Contents
- What does the Ash Framework actually model?
- Key features that shape your architecture
- How do the ecosystem integrations fit together?
- Ash Signals: letting other parts of the app react
- How does Ash sit alongside Phoenix?
- When should you actually choose Ash?
- Getting started: a practical pilot path
- Where to learn and get community support
- Key Takeaways
- Is Ash worth the abstraction cost?
- Elixirator’s Ash adoption services
- Useful sources and next reading
- Recommended
- FAQ
What does the Ash Framework actually model?
Ash organizes your application around resources, actions, and domains.
A resource describes something your app works with, such as a customer, an order, or a support ticket. It can include attributes, relationships, and business rules. A resource doesn’t have to represent a database table.
An action describes an operation: open a ticket, approve an order, or publish a post. You define the inputs and the behavior that belongs to that operation. Changes and validations can be attached to individual actions or shared across relevant actions.
A domain groups related resources and gives the rest of your app a way to work with them. For example, a Support domain might contain tickets and the people assigned to them.
The useful part is that these definitions are available to other tools. An extension can read them and build an interface around them. You still write the business logic; Ash provides a common structure for using it. The official What is Ash? guide explains this approach.
Key features that shape your architecture
Ash covers several jobs that would otherwise need separate plumbing.
Persistence: AshPostgres connects resources to PostgreSQL. It can generate migrations from changes to your resource definitions. You still review and run those migrations. For a quick prototype, Ash.DataLayer.Ets stores data in memory, but that data won’t survive an application restart.
API generation: AshGraphql builds GraphQL APIs using Absinthe. AshJsonApi builds REST APIs that follow the JSON:API specification. You choose which actions to expose and configure their API representation. Defining an action alone doesn’t make it a public endpoint.
Authorization: Ash’s policy system lets you define who can perform an action and which records or fields they can see. These policies are configured on resources and can apply to particular actions. You need to enable the policy authorizer and pass the right actor, usually the current user, through each entry point. Background jobs need an explicit authorization plan too. See the policy guide.
Extensions also cover state machines, encryption, authentication, and background jobs. You can add the pieces your application needs.
The trade-off is learning how those pieces work together. Debugging an action may involve its validations, changes, policies, and data layer. The first few problems can take longer to trace while you’re still learning that lifecycle.
How do the ecosystem integrations fit together?
Each extension builds on the same resources, but handles a different job.

AshPostgres handles PostgreSQL storage and queries. Its migration tools track resource snapshots and generate database changes. It’s a natural choice for a Postgres app, though the ecosystem also includes other data layers, such as AshSqlite.
AshPhoenix connects Ash to Phoenix. AshPhoenix.Form manages forms built around resource actions, including validation, submission, and errors. You still build the screen and handle its interaction. The form documentation shows the full flow.
AshGraphql and AshJsonApi provide two ways to expose your application to external clients. They can share underlying actions, while keeping their own API configuration. Check whether GraphQL or JSON:API matches what your clients actually need before choosing one.
AshOban connects resource actions to Oban background jobs. It supports running actions in the background and triggering work based on data conditions. You still need to configure Oban and think through retries and repeated execution.
Ash AI adds tools for LLM features. You can expose selected actions as tools, serve them through MCP, and define prompt-backed actions with structured outputs. This makes it possible to connect an assistant to existing application behavior. You still decide what it can access and which operations need human approval. Start with the current Ash AI documentation, since this part of the ecosystem is evolving.
The official ecosystem page is a useful map. Check each extension’s documentation and dependency requirements before adding it.
Ash Signals: letting other parts of the app react
Ash Signals lets an action announce what happened, while other resources define how to respond. For example, placing an order can emit an :order_placed signal. Separate listeners can award loyalty points or send a receipt, without adding those calls to the order action.
Signals have typed fields and compile-time checks. Listeners are Ash actions, so their validations and policies still apply.
Delivery matters: listeners run inline by default. Within a database transaction, a listener failure can roll back the originating action. With AshOban, a listener can run as a background job instead. When Oban and the action share a repository, the job is committed with the transaction and can be retried.
Use signals when other parts of the app need to react. Keep direct calls when you need a result back.
The feature was introduced in Ash 3.36, with background delivery in AshOban 0.10 - Here’s a Signals guide.
How does Ash sit alongside Phoenix?
Phoenix handles your web layer: requests, routing, and UI. Ash handles the resources and operations behind it. You can use them together, and Ash can also run without Phoenix.
A LiveView can call functions exposed by an Ash domain, such as an open_ticket function you define through a code interface. You don’t need an extra Phoenix context just to wrap that call. Keep a separate context when it has a clear job in your application’s design. The code interface guide explains the options.
You can also keep existing Phoenix and Ecto code while introducing Ash in one part of the app. Custom changes and manual actions let you implement behavior in Elixir when the built-in options don’t fit. Manual actions still use Ash’s action structure; they don’t remove the framework from the flow.
Direct Ecto or SQL access remains possible. Just remember that bypassing an Ash action also bypasses the rules that action would have applied. Decide clearly where each operation’s checks belong.
When should you actually choose Ash?
Our recommendation is to look at repeated behavior, rather than count database tables.
A product with permissions, several client apps, and frequent workflow changes has a clear reason to evaluate Ash. A smaller app can benefit too, especially if the team already knows the framework.
| Scenario | Ash fit |
|---|---|
| GraphQL, JSON:API, and LiveView sharing business logic | Strong candidate for a pilot |
| Lots of business rules and access permissions | Strong candidate, with careful policy testing |
| Adding a feature to an existing Phoenix app | Practical to try within a clear boundary |
| Small CRUD app with one interface | Depends on team experience and expected growth |
| Team new to Elixir and Ash | Allow extra learning time before judging productivity |
Be more cautious when the deadline is tight and nobody has used Ash before. Also test unusual queries, integrations, or workflow requirements early. Those are more useful checks than building several straightforward screens and leaving the difficult part until later.
Getting started: a practical pilot path
Choose one feature that is small enough to finish but realistic enough to teach you something.
- Create a sandbox branch or a small test project. Keep the pilot easy to review.
- Install Ash.
- Create a domain and one or two resources. Give actions meaningful names, such as
:approveor:close, where that matches the workflow. - Add storage. Use ETS for an initial experiment, or install and configure AshPostgres if database behavior is part of what you’re evaluating.
- Connect one interface. Build a LiveView form or expose selected actions through an API extension.
- Test the actions directly. Cover valid inputs, invalid inputs, and allowed and denied users. Then add a small integration test for the chosen interface.
- Review the result with the team. Check debugging, query behavior, migrations, and how easy the code is for another engineer to change.
Follow the official installation guide for the full setup, including new Phoenix projects.
Pro Tip: Include one awkward requirement in the pilot. A legacy API call or a permission edge case will show you where custom code is needed and whether the team is comfortable maintaining it.
Where to learn and get community support
Start with the official documentation and its introductory Livebook tutorial. Then read the guides for the specific pieces you plan to use, rather than trying to learn the whole ecosystem at once.
The Ash GitHub repository has the source, issues, and release history. For questions, the official docs link to ElixirForum and Discord. The community page is a useful starting point.
If you prefer a book, the Ash website links to a longer learning resource from The Pragmatic Bookshelf. For any tutorial or example, check which major version it targets before copying code into your project.
Key Takeaways
Ash is worth evaluating when you keep rebuilding the same rules across your application. How much it helps depends on the project and the team’s experience.
| Point | Details |
|---|---|
| Shared business logic | Resources and actions give related behavior a common home. |
| Useful extensions | Add storage, forms, APIs, jobs, or AI tools around those definitions. |
| Configuration still matters | You choose exposed actions, define policies, and review migrations. |
| There is a learning curve | Test debugging and maintenance during the pilot, alongside feature delivery. |
| Gradual adoption is possible | Start with a clear feature boundary and keep existing code where it works. |
| Elixirator’s role | Talk through your use case and the scope of a useful evaluation. |
Is Ash worth the abstraction cost?
It can be, especially when a change to one business rule currently means editing several parts of the app.
Our view is that the decision should come from a working example. After the pilot, ask: did Ash make the feature easier to change? Can another engineer understand it? Can we explain why a request was rejected or a query was slow?
If the answers are positive, you have a reason to expand its use. If the team is spending most of its time fighting the model, take that seriously too.
Using Ash alongside plain Elixir and Ecto can be a deliberate long-term choice. The important part is having clear ownership of each workflow, so the same rule doesn’t end up implemented in two places.
Elixirator’s Ash adoption services
Thinking about Ash for an existing Elixir product? A useful first step is to review one workflow and decide what a pilot should prove.
At Elixirator, we help product teams with Elixir engineering, integrations, scaling, and AI features. We can discuss where Ash might fit into that work and what you would need to evaluate before adopting it.
Bring us the feature you’re considering, the parts that are difficult today, and the constraints your team works with. Get in touch with Elixirator.
Useful sources and next reading
- Ash Framework official site — Features, ecosystem, and community links.
- Get Started with Ash — Installation and the first resources and actions.
- What is Ash? — The ideas behind the framework.
- Ash policies — Resource, action, and field access rules.
- Actors and authorization — How identity and authorization settings reach an action.
- AshPostgres — PostgreSQL integration and migration guides.
- AshPhoenix.Form — Form validation and submission.
- AshGraphql and AshJsonApi — API extensions.
- AshOban — Background jobs and triggers.
- Ash AI — LLM tools, MCP, and prompt-backed actions.
- Ash on GitHub — Source code, issues, and changes.
FAQ
1. Does Ash replace Phoenix or Ecto?
Ash works alongside Phoenix, which still handles the web experience. With AshPostgres, Ecto is used underneath for database work. You’ll usually work through Ash resources and actions in that part of the application, while existing Ecto code can remain elsewhere.
2. Can we add Ash without rewriting our application?
Yes. Start with one feature or domain and keep its boundaries clear. Custom changes and manual actions can help connect existing behavior. You don’t need to migrate every context or table to find out whether Ash helps.
3. Does Ash automatically secure every API and background job?
No. You need to configure authorization, write policies, and provide the appropriate actor and context. Shared actions give you a place to reuse those rules. Test each entry point, particularly background work and any code that bypasses Ash.
4. Can Ash help us build AI features?
Yes. Ash AI can expose selected application actions as LLM tools and through MCP, and supports prompt-backed actions with structured outputs. That can help an assistant work with existing business operations. Your team still controls permissions, tool exposure, and approval requirements for sensitive actions.

