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

Published
Ash Framework: How It Works and When To Use It? article image cover

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?

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.

Elixir mascot connecting resource, workflow, and AI tools through a shared gear

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.

ScenarioAsh fit
GraphQL, JSON:API, and LiveView sharing business logicStrong candidate for a pilot
Lots of business rules and access permissionsStrong candidate, with careful policy testing
Adding a feature to an existing Phoenix appPractical to try within a clear boundary
Small CRUD app with one interfaceDepends on team experience and expected growth
Team new to Elixir and AshAllow 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.

  1. Create a sandbox branch or a small test project. Keep the pilot easy to review.
  2. Install Ash.
  3. Create a domain and one or two resources. Give actions meaningful names, such as :approve or :close, where that matches the workflow.
  4. Add storage. Use ETS for an initial experiment, or install and configure AshPostgres if database behavior is part of what you’re evaluating.
  5. Connect one interface. Build a LiveView form or expose selected actions through an API extension.
  6. Test the actions directly. Cover valid inputs, invalid inputs, and allowed and denied users. Then add a small integration test for the chosen interface.
  7. 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.

PointDetails
Shared business logicResources and actions give related behavior a common home.
Useful extensionsAdd storage, forms, APIs, jobs, or AI tools around those definitions.
Configuration still mattersYou choose exposed actions, define policies, and review migrations.
There is a learning curveTest debugging and maintenance during the pilot, alongside feature delivery.
Gradual adoption is possibleStart with a clear feature boundary and keep existing code where it works.
Elixirator’s roleTalk 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

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.

elixir
ash framework
phoenix
postgresql
ai development

Ready to Build with Elixirator?

Prefer a quick call?
Photo of Alex Danyliak, Client Partner at Elixirator

Alex Danyliak

Client Partner at Elixirator

“Whether it’s a one-off consulting gig or a full dedicated team, let’s chat about how Elixirator can help you build something reliable, performant, and future-proof.”

Ready to Build with Elixirator?

Prefer a quick call?
Photo of Alex Danyliak, Client Partner at Elixirator

Alex Danyliak

Client Partner at Elixirator

“Whether it’s a one-off consulting gig or a full dedicated team, let’s chat about how Elixirator can help you build something reliable, performant, and future-proof.”