Ash. The hidden champion of low-code tools

Now bear with me. The title may be a little provocative to everyone who already knows about Ash.

I'm a software engineer at heart and I love to write code. As such, I found it a little awkward, even daunting to see all the low-code or no-code tools emerging over the last few years.

Hey... this tool does many things of what I can do with my skills in just a few clicks?
– me (over and over again and will continue to ask this question 😄)

Well, yes. And that's a good thing because they add value. How? They remove friction and do away with all the boilerplate and get you to solve problems faster.

And let me ask this: is there much value in writing yet another API endpoint by hand and dealing with all boilerplate code just to be able to simply create, read, update, or delete (CRUD) an entity in 2024?

Of course, there exists many frameworks that try to deal with reducing boilerplate code and abstract away the repetitive stuff. Also, CRUD endpoints is only one piece of the solution. Having dealt with a couple different frameworks over the years myself, I haven't found a better one than Ash.

The harder the problem, the more creativity is required. You want to be able to focus on new problems that weren't solved before. If the problem was already solved a thousand times before, then there is not much value in spending a lot of time to solve it again.

You need to be able to work on the parts that can't be solved by "out of the box" solutions. With the right tools, you can focus on the most valuable parts of your work. On being creative and solving valuable problems. Delegate, or automate the rest to other tools and services.

Ash allows you to spend time on being creative. One of the biggest value points of Ash lies in removing the boilerplate without locking you in. A framework should not lock you in and allow you to do anything you want and need. It should help and support you, not stand in your way. That's exactly what Ash sets out to do and offers various nice ways that don't feel like a "hack", if you need a thing that isn‘t outright supported by Ash. This really is a strong selling point, but not the biggest one, in my opinion. Keep reading to find out what I think is the biggest one.

You don't have to deal with boilerplate code. You delegate most of the "boring" boilerplate code to the framework and spend your time on solving problems where no "out of the box" solution exists. You can focus on the creative, valuable problems.

How is this possible? That's quite a big topic in itself. But in short: Ash leverages two things perfectly.

  • The power of Elixir itself. Elixir provides a very powerful macro system. You can write code, that will write code at compile time.
  • It stands on top of giants, e.g. Ecto or Absinthe for GraphQL

By combining these two things, Ash provides powerful, yet extensible abstractions where you can describe what you want to do and let Ash figure out how it should be done.

Sounds a little bit abstract, right? Let's get practical for a bit.

In many cases, you'll probably end up with around three files to build a simple CRUD interface that can interact with an entity, leaving out any setup and configuration files. You'll always have those which roughly look and do the same things in all languages and frameworks.

  • An entity that describes what attributes a resource has; e.g. a contact that has a first_name and last_name
  • A service, providing an API to interact with the resource; functions for create, read, update, and delete that either read or manipulate persisted data.
  • A controller that exposes a REST or GraphQL API, using the service to interact with the resource.

It doesn't really matter what framework you'll use. Whether you'll use Phoenix and Ecto, Nest.js/Express.js and TypeORM. The implementation approximately ends up as described above (plus some config files etc.) and you'll be writing the boilerplate code to create a simple CRUD REST API yourself.

What's more, you'll have to write this over and over again, for every entity and method/action you want to provide. And if you are like me, you'll come up with new "versions" and implementation details for writing the next API endpoint. You've learned something new and want to apply it. Requirements may evolve over time. You'll have to refactor the existing code base so everything is aligned again. And that's just speaking of one single code base. When you'll start the next project, you'll need to start all over again, and you'll probably want to take all the learnings from the previous projects with you and add the in the new project as well.

Let's see how this is different when using Ash. In the following gist, you'll see a resource definition for the same entity as described above.

That's about it. You describe your resource. What attributes it has. What Data Layer it uses (in this case, Postgres - more on this a little bit), what actions it provides, and how the actions will be exposed via GraphQL. At first glance, this doesn't look very different than in other frameworks (except maybe for the Domain Specific Language). But lets stop and think for a second here. You describe a resource. Everything else is taken care for you. You'll have a working GraphQL API that allows you to interact with the resource. No boilerplate code to connect the GraphQL Layer with the service and the service with the resource.

The example only shows some of the most basic CRUD options. Ash knows how to deal with them out of the box. It‘ll accept attributes, create a changeset and applies the changes. You can do much more than just this. For example, you might want to define your own create action that only accepts a subset of the attributes. Or, you may want to expose a read action that always applies a certain filter, e.g. to provide an action that always returns the contact of the logged-in user. If you need a completely customized action that doesn't strictly fall into the typical CRUD methods you can add a "generic" action as demonstrated by action :merge. It also shows you can provide a custom function implementation. There's pretty much an option for every use case you'll encounter.

💡
Now, like I said, I intentionally left out the config parts; e.g. connection settings for Postgres and setup code for Ash GraphQL. I also did not cover more advanced topics, like validations, calculations and changes.

This is what I call "low code". Look at all the code that I don't have to write anymore. That's amazing but there's more. Data Layers and APIs to interact with your resources.

Ash has a concept called Data Layer. Think of it as an abstraction layer for your data stores you want to use. The most popular one probably is Ash Postgres. But there's also Ash.DataLayer.Ets, Ash Sqlite, Ash CSV. Or the Ash.DataLayer.Simple which won't persist any data. The cool thing about all of this is: you only need to configure your resource to use one of the options. Interacting with your resources stays the same, no matter what Data Layer a resource uses. And that's because of the unified APIs that Ash provides. This allows for powerful options.

💡
Ash provides a unified API for interacting with resources and their underlying Data Layer. However, different Data Layers may differ in what features it actually supports. Imagine you want to build a Data Layer that interacts with a REST API and the API does not offer a create method. It is up to the Data Layer implementation to decide what features are supported.
Model your domain, derive the rest – ash-hq.org

Remember that "you'll probably want to take all the learnings from the previous projects with you and add the in the new project as well"?

Since Ash is declarative and it derives the rest (i.e. the underlying implementation), you'll get the best and latest version of an implementation detail out of the box, every time, in every project. Your job is to describe how a resource looks like. The underlying implementation is generated for you. If someone finds a better way of a certain implementation, a new version of Ash is shipped and as soon as you'll upgrade, you'll profit. Most probably, you won't even have to change anything except for upgrading the version in the mix.exs file.

You can also benefit from Ash's extensibility. You can either write your own, or use some of the existing extensions. The abstraction layers and unified APIs allow for lots of possibilities. May it be simply defining your own base resources that always add "audit attributes", or creating your own Data Layer (e.g. in our business, we created a very simply version of a Hubspot Data Layer that allows to interact with a Hubspot CRM Contact).

And last but not least, the community. It's bunch of very open, friendly and helpful people 🧡 When you find a bug, you'll report it and it might be fixed the next day. Also, new ideas are very welcome and contributing to the project is generally pretty easy. Zach, the creator of Ash, has built something remarkable and is leading the project very beautifully. And it shows.

Now that's a "low-code" tool to my liking:

  • Reduce boilerplate. Describe and configure most. Let me focus on the most valuable parts
  • No lock-in. Provide ways to achieve anything, if not already provided by Ash itself
  • Provide ways to extend your resources and code base in general
  • Active project development
  • Helpful and buzzing community

This all may not fully show the true power of Ash. It is very hard to show, to be honest. Every topic I covered here only shows the top of the iceberg and every aspect might be worth its own post. Profiting from building with Ash is something you can feel best over time when using it yourself. Also, it's always best to not simply trust someone blindly and try things for yourself. It may not be the right approach for you to build apps and services after all. But then again, it could also change the way you approach software engineering.

If I got you interested in checking out Ash, it's best to jump to the docs get going. There's also a Discord server with all the amazing people waiting to help you out or discuss the exciting projects you'll build!

Happy (low) coding!


A word of caution: this letter probably sounds like a love letter (it kind of is one). However, there's no magic software. Ditching everything you've built so far and replacing it with Ash may look like you’ll soon be touching greener grass. It is very green over here. But it's definitely not a silver bullet that solves all of your problems.