<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Ash Framework</title>
    <subtitle>Model your domain, derive the rest.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://ash-hq.org/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://ash-hq.org"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-10-15T12:00:00+00:00</updated>
    <id>https://ash-hq.org/atom.xml</id>
    <entry xml:lang="en">
        <title>Concentrated Liability</title>
        <published>2026-10-15T12:00:00+00:00</published>
        <updated>2026-10-15T12:00:00+00:00</updated>
        
        <author>
          <name>
            Zach Daniel
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/blog/concentrated-liability/"/>
        <id>https://ash-hq.org/blog/concentrated-liability/</id>
        
        <content type="html" xml:base="https://ash-hq.org/blog/concentrated-liability/">&lt;p&gt;Peter Ullrich recently ran a massive AI driven security audit on Ash Framework and its package ecosystem. All of the issues have been shared and patched.&lt;/p&gt;
&lt;p&gt;Given the theme of vulnerabilities, I’m going to lead with some of my own, personal in nature. One of the hard parts of dealing with security issues is a combination of the urgency (we care deeply about our users’ security, and our users’ users security) and the effort to resolve. For me, however, that is not the hardest part.&lt;/p&gt;
&lt;p&gt;The hardest part for me is that this shit is embarrassing. People trust me. They trust our team. Not only to build tools that help them build their own applications, but to have thought through our architecture and code in such a way that they can sleep sound at night knowing that they made a good choice of framework. When I get a security issue report, it feels like I’ve betrayed that trust.&lt;/p&gt;
&lt;p&gt;In practice, this just isn’t true. But that doesn’t change how it feels. It doesn’t change the way my stomach drops bottom when I see GHSA (GitHub Security Advisory) in my notifications list. I don’t know in practice how valuable it is for our users to know what it feels like for us, or how much they really need to care, but in an age of AI writing, I thought it might be good to lead with something human, something real.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Ash Framework is not a library (I mean, the name makes it pretty clear). Ash does so many high level things for you that naming them all would be longer than this post by a country mile. I sometimes joke that “Ash is the highest-code low-code framework that exists.” In a typical Ash application, you are defining some behavior in a condensed way, and your APIs, database operations, validations, even authorization logic, are handled by the framework. Our motto is “Model your domain, derive the rest.” You get superpowers from this methodology by virtue of the core framework doing all sorts of clever things for you, sometimes for convenience, sometimes for security, and most often for just pure, raw, capability.&lt;/p&gt;
&lt;p&gt;What this means is that in our users’ applications Ash’s involvement starts right at the edge, going all the way down to the database and ending at the response to the user. All the while it handles automations, side-effects, time-based triggers, you name it. So in practice, your code is this tiny little powerful nugget of description that is expanded into an entire application layer’s worth of behavior.&lt;/p&gt;
&lt;p&gt;It also means that one line of code does the work of hundreds or thousands of lines of code had it been hand-written, or composed out of a bunch of libraries. Ash provides both authentication and authorization primitives. You describe your rules and Ash implements authorization to a level of fidelity that for most teams it is just not practical to reproduce.&lt;/p&gt;
&lt;p&gt;So when we have a bug in some internal tool or mechanism, one can almost always make the case that it constitutes a “security vulnerability”. The various parts of Ash are all interlinked, working together to accomplish a described outcome. It’s less wishy washy than I’m making it sound, it’s all declarative, deterministic code.&lt;/p&gt;
&lt;p&gt;For example, let’s say there was an issue with applying a certain kind of filter under a very specific set of circumstances, where a filter might end up negated, i.e. &lt;code&gt;a == b&lt;/code&gt; became &lt;code&gt;a != b&lt;/code&gt; (this was not one of the vulnerabilities we addressed). If you were just using it as a library to run queries (which you could) it might look like this&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;query&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt; Ash&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;admin &lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt; Ash&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;read!&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’d get incorrect results, but in some tightly scoped area of usage. The data on a page might be displayed incorrectly, or some validation/authorization check in your application might fail. And if that was all that Ash was, you’d almost certainly not see a CVE come from us discovering that bug. We would just fix it and push a new bug fix release. Except that is not all that Ash is.&lt;/p&gt;
&lt;p&gt;Here is an example policy for reading data from a resource in Ash.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;policy action_type&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:read&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  authorize_if actor_attribute_equals&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:is_admin&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  authorize_if expr&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;published &lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rule this is describing is that you can read records of this resource if you are an admin, otherwise, you can read records where they have the attribute &lt;code&gt;published&lt;/code&gt; set to true. In more common terms, you can see all rows of this database table if you are an admin, or if not, you can see rows where &lt;code&gt;published == true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Stepping one level up, you could then do something like this:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;actor &lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;=&lt;/span&gt;&lt;span&gt; %&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;is_admin: true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-2&quot;&gt;Ash&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;read!&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; actor:&lt;/span&gt;&lt;span&gt; actor&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;# SELECT * FROM posts;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you ran the above function, you’d get all of the data in the table. If you ran the following code, some “magic” would happen:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;actor &lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;=&lt;/span&gt;&lt;span&gt; %&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;is_admin: false&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-2&quot;&gt;Ash&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;read!&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; actor:&lt;/span&gt;&lt;span&gt; actor&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;# SELECT * FROM posts WHERE published IS TRUE;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ash takes the policies, figures out what facts are true or false, determines if you can possibly make the request at all, and if you can, it will determine whether or not a filter needs to be applied to only return the data visible to the actor running the request.&lt;/p&gt;
&lt;p&gt;Except it doesn’t stop there. In many usages of Ash, you won’t see the above code at all. Instead, you’d just see something like this:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;json_api &lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  type &lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;quot;posts&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  routes &lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    index &lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;quot;/posts&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; action: :read&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This translates roughly to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a route at &lt;code&gt;/posts&lt;/code&gt; (a full-featured, spec-compliant JSON:API endpoint)&lt;/li&gt;
&lt;li&gt;When that route is called, translate the query arguments and/or body to a call to the &lt;code&gt;:read&lt;/code&gt; action on this resource, using the current user as the actor&lt;/li&gt;
&lt;li&gt;Call that action with the correct inputs&lt;/li&gt;
&lt;li&gt;Take the response and format it for display over the API&lt;/li&gt;
&lt;li&gt;Display that result to the end user&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’re taking what is often hundreds or thousands of lines of code and boiling it down to practically one line (&lt;code&gt;index &quot;/posts&quot;, ...&lt;/code&gt;). This is awesome. It is wildly powerful in terms of both how much you get for free, how quickly you get it, and how secure it is. There is no way to “forget” to apply policies, or to somehow fail to honor the rules of the resource you are calling.&lt;/p&gt;
&lt;p&gt;But it also means that if &lt;code&gt;Ash.Query.filter(admin == true)&lt;/code&gt; is broken, then (at least theoretically) all kinds of other things are broken along with it.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;This pattern plays out across a huge array of integrated features. Ash handles multitenancy for you, either via “attribute based” multitenancy (think an &lt;code&gt;organization_id&lt;/code&gt; column), or “context based” multitenancy (schema-per-tenant in postgres). The tenant is automatically applied to every part of your query. Which may not sound that impressive until you understand how many other integrated features the tenant is automatically applied to. So if filters are broken, so might your tenant isolation be broken. Or if we fail to properly apply the tenant in some edge-case in our query builder, you may very well not notice.&lt;/p&gt;
&lt;p&gt;Which, coincidentally was one of the CVEs that was raised in this audit. &lt;a rel=&quot;external&quot; href=&quot;https://github.com/ash-project/ash_sql/security/advisories/GHSA-ww8j-58rc-f8h3&quot;&gt;GHSA-ww8j-58rc-f8h3: Cross-tenant information disclosure in &lt;code&gt;AshSql.AggregateQuery&lt;/code&gt; in AshSql drops the query prefix when aggregating over a distinct query&lt;/a&gt; showed that when you used &lt;code&gt;Ash.aggregate&lt;/code&gt; to get an aggregate result (i.e. “count of X where Y”), in some cases we might drop the tenant. That’s the kind of thing that, even when tested, you might never really discover unless you intentionally thought “let me write a test where I create multiple tenants with data, and prove that the aggregate value returned only contains one tenant’s data”. The lack of that test is how we didn’t catch it, after all. For most people, they would call that type of test “testing the framework” and conventional wisdom would tell you not to do that.&lt;/p&gt;
&lt;p&gt;The list goes on. Ash handles pagination for you. Which means any issues applying limit or offset (which didn’t happen) could produce incorrect query results. Ash handles pubsub notifications for you, which means any issues constructing topics or events could send information to the wrong place (which kind of but probably never actually happened).&lt;/p&gt;
&lt;p&gt;Ash provides pre-built validations and transformations for you, which also produce error messages that get sanitized at the edge. Unless the error message contains incorrect or privileged information in place designed to be shown to end-users. Which did happen. In this example, if you said:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;destroy &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:delete_project&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  argument &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:confirm_secret_code&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; :string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  validate confirm&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:secret_code&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; :confirm_secret_code&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The action will fail if the provided &lt;code&gt;confirm_secret_code&lt;/code&gt; does not match the record’s &lt;code&gt;secret_code&lt;/code&gt;. The error message, in one specific case, would come back containing the actual &lt;code&gt;secret_code&lt;/code&gt; instead of the &lt;code&gt;confirm_secret_code&lt;/code&gt; that you provided. Ash has some cool features that ensure we don’t leak sensitive data, for example:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;attributes &lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  attribute &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:secret_code&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; :string&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; sensitive?: true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;# and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;destroy &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:delete_project&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  argument &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:confirm_secret_code&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; :string&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; sensitive?: true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  validate confirm&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:secret_code&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; :confirm_secret_code&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In which case the value itself is redacted (replaced with &lt;code&gt;&quot;**redacted**&quot;&lt;/code&gt;). But the issue is that we used the &lt;code&gt;sensitive?&lt;/code&gt; configuration from the argument to decide if we should redact the value, and displayed the value in the attribute. So in this case, if the argument was not sensitive (which maybe it isn’t) but the attribute is then we’ve exposed a sensitive value in an error message.&lt;/p&gt;
&lt;p&gt;Here again you see a bunch of useful framework features that, in concert, make your application significantly more secure. Except when we do it wrong.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The hard thing to internalize as a maintainer of a framework like this is that this is still better than whatever 99% of projects would hand roll. What I mean by this is that we (I) take a pretty brutal ego hit when these security issues come through, but we’ve still contributed to thousands of applications being wildly more secure than they ever would have been without us.&lt;/p&gt;
&lt;p&gt;I’ve created something that is both a “concentrated liability” and a “safety net” all in one go.&lt;/p&gt;
&lt;p&gt;These bugs exist everywhere, even in carefully crafted projects with hundreds of contributors and rigorous code review, and I can tell you with certainty after seeing the results of both open source vulnerability scanning and enterprise-grade vulnerability scanning: every application is riddled with these issues. In OSS, these become CVEs that (if the team is good and still maintaining it) are patched, users are alerted, and the world gets a bit safer. In your own code, they are just “bugs”. Who would tell you about them if you aren’t paying for a security audit?&lt;/p&gt;
&lt;p&gt;The framing here could easily come off as me attempting to protect the image of Ash, but I really do believe that end users should carefully consider the above trade-offs. We’re going to be experiencing an onslaught of AI era vulnerability exploits, and we all need to make the right decisions for us.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Introducing Signals</title>
        <published>2026-10-13T12:00:00+00:00</published>
        <updated>2026-10-13T12:00:00+00:00</updated>
        
        <author>
          <name>
            Zach Daniel
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/blog/introducing-signals/"/>
        <id>https://ash-hq.org/blog/introducing-signals/</id>
        
        <content type="html" xml:base="https://ash-hq.org/blog/introducing-signals/">&lt;p&gt;Signals are new in Ash 3.36! They let one resource react to what happens in another, without either one depending on the other. A resource emits a typed signal, other resources listen for it with their own actions, and each listener decides whether it runs in the same transaction or in a background job.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Introducing Temporal Resources</title>
        <published>2026-10-03T11:40:12+00:00</published>
        <updated>2026-10-03T11:40:12+00:00</updated>
        
        <author>
          <name>
            Zach Daniel
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/blog/introducing-temporal-resources/"/>
        <id>https://ash-hq.org/blog/introducing-temporal-resources/</id>
        
        <content type="html" xml:base="https://ash-hq.org/blog/introducing-temporal-resources/">&lt;p&gt;Temporal resources are a new, experimental feature in Ash: resources that remember every state their records have ever been in, so you can read and write them as of any moment, past, present or future, with a single option.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Matt Beanland</title>
        <published>2026-10-01T00:00:00+00:00</published>
        <updated>2026-10-01T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/community/contributors-of-the-month/matt-beanland/"/>
        <id>https://ash-hq.org/community/contributors-of-the-month/matt-beanland/</id>
        
        <content type="html" xml:base="https://ash-hq.org/community/contributors-of-the-month/matt-beanland/"></content>
        
    </entry>
    <entry xml:lang="en">
        <title>Ash Framework 3.0</title>
        <published>2024-09-15T03:24:22+00:00</published>
        <updated>2024-09-15T03:24:22+00:00</updated>
        
        <author>
          <name>
            Zach Daniel
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/blog/ash-framework-30/"/>
        <id>https://ash-hq.org/blog/ash-framework-30/</id>
        
        <content type="html" xml:base="https://ash-hq.org/blog/ash-framework-30/">&lt;h2 id=&quot;ash-framework-3-0-official-release&quot;&gt;Ash Framework 3.0: Official Release!&lt;/h2&gt;
&lt;p&gt;I&#39;m here with the fine folks at &lt;a rel=&quot;external&quot; href=&quot;https://www.gigcityelixir.com&quot;&gt;Gig City Elixir&lt;/a&gt;, pushing the button live on stage 😎&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/gig-city.jpg&quot; alt=&quot;Zach on stage at Gig City Elixir, in front of a waving, cheering audience&quot;&gt;&lt;/img&gt;&lt;/p&gt;
&lt;p&gt;The other packages around Ash will be updated with non-release candidate versions over the next few hours.&lt;/p&gt;
&lt;h2 id=&quot;what-is-changing&quot;&gt;What is changing?&lt;/h2&gt;
&lt;p&gt;While there are far too many things to put them all here, I want to give those who haven&#39;t been following along with our &lt;a rel=&quot;external&quot; href=&quot;https://elixirforum.com/t/ash-3-0-teasers/61857/1&quot;&gt;3.0 teasers&lt;/a&gt; an idea of where the focus is for Ash 3.0. There are three themes that cover most of the changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt;: We want to make sure that Ash is safe and secure by default, and that it is free of foot guns that could lead to security issues, data consistency or other critical failures. Some examples of this are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://elixirforum.com/t/ash-3-0-teasers/61857/28&quot;&gt;Actions no longer accept all public attributes by default&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://elixirforum.com/t/ash-3-0-teasers/61857/28#private-true-is-now-public-false-and-public-false-is-now-the-default-3&quot;&gt;All fields are now private by default&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://elixirforum.com/t/ash-3-0-teasers/61857/22#domainauthorizationauthorize-now-defaults-to-by_default-2&quot;&gt;Authorization is always done by default&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;: Ash is a huge ecosystem, and there is a lot to learn about how it works. Every confusing term and inconsistency increases the mental burden on developers, and increases the likelihood of bugs and wasted time. Some examples of this are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://elixirforum.com/t/ash-3-0-teasers/61857/22#domainauthorizationauthorize-now-defaults-to-by_default-2&quot;&gt;&lt;code&gt;Ash.Api&lt;/code&gt; is now &lt;code&gt;Ash.Domain&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://elixirforum.com/t/ash-3-0-teasers/61857/28&quot;&gt;Not selected attributes show &lt;code&gt;%Ash.NotLoaded{}&lt;/code&gt; instead of &lt;code&gt;nil&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Developer Experience&lt;/strong&gt;: With 2.x versions adding support for atomics and bulk actions, the core functionality of Ash is in a very good place. This gives us the time and mental cycles to devote to developer experience. While many of the 3.0 changes are focused on DX, we also expect to see a much greater focus on this moving forward! Some examples of this are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://elixirforum.com/t/ash-3-0-teasers/61857/36&quot;&gt;Ash.ToTenant protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://elixirforum.com/t/ash-3-0-teasers/61857/28#custom-expressions-4&quot;&gt;Custom Expressions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://elixirforum.com/t/ash-3-0-teasers/61857/30&quot;&gt;Policies &amp;amp; Code Interfaces on the domain&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-do-i-upgrade&quot;&gt;How do I upgrade?&lt;/h2&gt;
&lt;p&gt;It is &lt;strong&gt;very important&lt;/strong&gt; that you read and follow the &lt;a rel=&quot;external&quot; href=&quot;https://hexdocs.pm/ash/3.0.0/upgrading-to-3-0.html&quot;&gt;upgrade guide&lt;/a&gt;! You will need to go through it section by section and consider if there are changes from that section that you must make. Don&#39;t forget that some packages have their own upgrade guides for breaking changes contained in that package. Links to those guides are at the top.&lt;/p&gt;
&lt;h2 id=&quot;what-if-i-need-help&quot;&gt;What if I need help?&lt;/h2&gt;
&lt;p&gt;If you encounter an issue or change that is not covered in the upgrade guide &lt;em&gt;please&lt;/em&gt; contact us or open a PR to add to the guide. Even if it&#39;s just a note reminding folks to look out for something that might be a gotcha. Every little bit helps :)&lt;/p&gt;
&lt;p&gt;Feel free to ask questions here on the forum if you need assistance!&lt;/p&gt;
&lt;h3 id=&quot;what-about-2-x&quot;&gt;What about 2.x?&lt;/h3&gt;
&lt;p&gt;Ash 2.x will receive a &lt;strong&gt;minimum&lt;/strong&gt; of 6 months of critical bug fixes and security upgrades. Critical bug fixes include issues that have no reasonable workaround. Security upgrades includes things like updating potentially vulnerable packages, or fixing any issues related to policies, authentication, etc. We won&#39;t be leaving folks in the dust, but it will absolutely be in your best interest to find a time in the next few months to upgrade, as there are many features in 3.0 to take advantage of and there are already more on the way!&lt;/p&gt;
&lt;p&gt;And don&#39;t forget, the Ash book is coming! Keep an eye out for updates 🥳. Thanks again to @sevenseacat for all her fine work on this book, its coming along spectacularly.&lt;/p&gt;
&lt;figure class=&quot;figure figure--small&quot;&gt;&lt;img src=&quot;/images/book-cover.jpg&quot; alt=&quot;The cover of Building Web Applications with Ash, by Rebecca Le and Zach Daniel&quot; loading=&quot;lazy&quot; /&gt;&lt;/figure&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Jonatan Männchen</title>
        <published>2024-08-01T00:00:00+00:00</published>
        <updated>2024-08-01T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/community/contributors-of-the-month/jonatan-mannchen/"/>
        <id>https://ash-hq.org/community/contributors-of-the-month/jonatan-mannchen/</id>
        
        <content type="html" xml:base="https://ash-hq.org/community/contributors-of-the-month/jonatan-mannchen/"></content>
        
    </entry>
    <entry xml:lang="en">
        <title>Rebecca Le</title>
        <published>2024-06-01T00:00:00+00:00</published>
        <updated>2024-06-01T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/community/contributors-of-the-month/rebecca-le/"/>
        <id>https://ash-hq.org/community/contributors-of-the-month/rebecca-le/</id>
        
        <content type="html" xml:base="https://ash-hq.org/community/contributors-of-the-month/rebecca-le/"></content>
        
    </entry>
    <entry xml:lang="en">
        <title>Riccardo Binetti</title>
        <published>2024-06-01T00:00:00+00:00</published>
        <updated>2024-06-01T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/community/contributors-of-the-month/riccardo-binetti/"/>
        <id>https://ash-hq.org/community/contributors-of-the-month/riccardo-binetti/</id>
        
        <content type="html" xml:base="https://ash-hq.org/community/contributors-of-the-month/riccardo-binetti/"></content>
        
    </entry>
    <entry xml:lang="en">
        <title>Robert Timis</title>
        <published>2024-06-01T00:00:00+00:00</published>
        <updated>2024-06-01T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/community/contributors-of-the-month/robert-timis/"/>
        <id>https://ash-hq.org/community/contributors-of-the-month/robert-timis/</id>
        
        <content type="html" xml:base="https://ash-hq.org/community/contributors-of-the-month/robert-timis/"></content>
        
    </entry>
    <entry xml:lang="en">
        <title>Barnabas Jovanovics</title>
        <published>2023-10-01T00:00:00+00:00</published>
        <updated>2023-10-01T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/community/contributors-of-the-month/barnabas-jovanovics/"/>
        <id>https://ash-hq.org/community/contributors-of-the-month/barnabas-jovanovics/</id>
        
        <content type="html" xml:base="https://ash-hq.org/community/contributors-of-the-month/barnabas-jovanovics/"></content>
        
    </entry>
    <entry xml:lang="en">
        <title>There are macros and there are *macros*</title>
        <published>2023-05-11T20:00:29+00:00</published>
        <updated>2023-05-11T20:00:29+00:00</updated>
        
        <author>
          <name>
            Zach Daniel
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/blog/there-are-macros/"/>
        <id>https://ash-hq.org/blog/there-are-macros/</id>
        
        <content type="html" xml:base="https://ash-hq.org/blog/there-are-macros/">&lt;p&gt;A common criticism of Ash is that it has too many macros. This is an understandable position to take, but I think its important to distinguish between two main kinds of macros. One of them should be used extremely sparingly, and the other I think its okay to use a bit more liberally. We&#39;ll call them &quot;configuration macros&quot; and &quot;metaprogramming macros&quot;. Keep in mind these names are a bit made up and the boundary between the two is not always cut and dry. When people talk about &quot;magic&quot; in the context of macros, they are mostly talking about &quot;metaprogramming macros&quot;. Lets take a look at some examples.&lt;/p&gt;
&lt;h2 id=&quot;metaprogramming-macros&quot;&gt;Metaprogramming Macros&lt;/h2&gt;
&lt;p&gt;This snippet is from &lt;a rel=&quot;external&quot; href=&quot;https://github.com/elixir-nx/nx/tree/main/nx#readme&quot;&gt;NX&lt;/a&gt;. This function can run as regular elixir code, or it can be compiled to run using multiple backends meant for numerical processing, including running on the GPU. The code that is actually compiled under the hood looks &lt;em&gt;dramatically&lt;/em&gt; different from what you see in front of you. This is not a bad thing! If you had to use or write the actual underlying code, you&#39;d get nothing done. Check out Nx&#39;s documentation for more.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;defn&lt;/span&gt;&lt;span&gt; softmax&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-2&quot;&gt;  Nx&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;exp&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;) /&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt; Nx&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;sum&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Nx&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;exp&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is an example from &lt;code&gt;Nebulex&lt;/code&gt;, an ETS based caching library. In this example, the metaprogramming is quite hidden from you. The actual logic happens in a &lt;a rel=&quot;external&quot; href=&quot;https://hexdocs.pm/elixir/1.12/Module.html#module-compile-callbacks&quot;&gt;&lt;em&gt;compiler callback&lt;/em&gt;&lt;/a&gt;. It involves rewriting the functions to intercept calls and decide if the cached version should be used, or if the value should be computed and cached. This kind of macro is quite magical given how indirect it is, but at the same time in applications where cached functions are common, it stops feeling like magic and starts feeling like a language feature. If I was looking at a large code-base that used this once, I&#39;d likely be concerned. The mental overhead of understanding it for one single case likely isn&#39;t worth it.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;defmodule&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt; MyApp.Example&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  use&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt; Nebulex&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Caching&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-6&quot;&gt;  @decorate&lt;/span&gt;&lt;span&gt; cacheable&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;cache:&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt; MyApp&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Cache&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; key: :cache_key&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  def&lt;/span&gt;&lt;span&gt; get_by_name&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; age&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This one is from &lt;code&gt;Ash&lt;/code&gt;. We do similar magic to the &lt;code&gt;Nx&lt;/code&gt; example from above for this. Specifically, we have an expression that can be run natively to a data layer (i.e &lt;code&gt;SQL&lt;/code&gt;) or in elixir. And Ash reserves the right to decide where it gets run, depending on the context and requirements.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;calculate &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:full_name&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; :string&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; expr&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;first_name &lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;quot; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; &amp;lt;&amp;gt;&lt;/span&gt;&lt;span&gt; last_name&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, all of these examples represent a significant amount of complexity boiled down to very simple macros. This will also be true of the upcoming examples of &quot;configuration macros&quot;, but the big difference here is that each macro call represents very complex code being executed on your behalf.&lt;/p&gt;
&lt;h2 id=&quot;configuration-macros&quot;&gt;Configuration Macros&lt;/h2&gt;
&lt;p&gt;Lets take a look at some examples of macros that I would consider &lt;em&gt;less&lt;/em&gt; magical.&lt;/p&gt;
&lt;p&gt;This one is likely familiar to most people who have worked with Elixir professionally. Here we have an Ecto.Schema. In this example we actually have three separate macros. The &lt;code&gt;schema/2&lt;/code&gt; macro sets up some module attributes, and &lt;code&gt;field/2&lt;/code&gt; adds to those module attributes. &lt;code&gt;timestamps/0&lt;/code&gt; sets up the schema to track &lt;code&gt;inserted_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt; timestamps. On its own, however, an &lt;code&gt;Ecto.Schema&lt;/code&gt; doesn&#39;t really &lt;em&gt;do&lt;/em&gt; anything. There also isn&#39;t a bunch of code hidden behind the scenes that you need to be aware of being executed. This sets up some information on the module that can be read back later. For example: &lt;code&gt;MyApp.MySchema.__schema__(:fields)&lt;/code&gt; would return &lt;code&gt;[:field_name, :inserted_at, :updated_at]&lt;/code&gt; in this case. This is a classic example of what I&#39;d call a &quot;configuration macro&quot;.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;defmodule&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt; MyApp.MySchema&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  schema &lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;quot;table&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    field &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:field_name&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; :type&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    timestamps&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we can see an example from &lt;code&gt;Absinthe&lt;/code&gt;&#39;s documentation. Absinthe is a tool for building GraphQL apis with Elixir. This one slightly straddles the line between &quot;configuration macros&quot; and &quot;metaprogramming macros&quot;, but I think still lands on the side of a &quot;configuration macro&quot;. We configure a field in our GraphQL API, and how to resolve it. There are quite a few macros involved in building an API with &lt;code&gt;Absinthe&lt;/code&gt;. Here we configure what code gets run to resolve a given field, but ultimately the responsibility for what code runs is our own.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;defmodule&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt; BlogWeb.Schema&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  use&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt; Absinthe&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Schema&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  query &lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-6&quot;&gt;    @desc&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;quot;Get all posts&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    field &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:posts&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; list_of&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:post&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      resolve &amp;amp;&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Resolvers&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Content&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;list_posts&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, lets take a look at some code from an &lt;code&gt;Ash.Resource&lt;/code&gt;. I&#39;ll choose a relatively complex example, specifically the defining of an &quot;action&quot;.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;create &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:create&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  primary? &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  accept &lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  argument &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:public&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; :boolean&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    allow_nil? &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    default &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  change &lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;fn&lt;/span&gt;&lt;span&gt; changeset&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; _&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; -&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt; Ash&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Changeset&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;get_argument&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;changeset&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; :public&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-2&quot;&gt;      Ash&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Changeset&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;force_change_attribute&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;changeset&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; :visibility&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; :public&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      changeset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  change relate_actor&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;:author&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For those not familiar with Ash, lets break it down.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;primary? true&lt;/code&gt; means that if something wants to create one of these, this is the action it should call by default. This is used by certain internal actions and can be used by you or other extensions to determine which action to call in lieu of an explicitly configured action.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;accept [:text]&lt;/code&gt; means that you can set the &lt;code&gt;text&lt;/code&gt; attribute when calling this action.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;argument :public, :boolean, …&lt;/code&gt; adds an additional input to the action that doesn’t map to an attribute, called &lt;code&gt;:public&lt;/code&gt; that must have a value, but defaults to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The anonymous function you see with &lt;code&gt;change fn changeset, _ -&amp;gt;&lt;/code&gt; is what we call an &quot;inline change&quot;. A &lt;code&gt;change&lt;/code&gt; is a function that takes a changes and returns a changeset. If you’ve worked with Phoenix or with Plug, you’ll recognize this pattern, it works effectively the same as &lt;code&gt;Plug&lt;/code&gt;, working on a &lt;code&gt;Conn&lt;/code&gt;. Except in this case, its a &lt;code&gt;change&lt;/code&gt; working on an &lt;code&gt;Ash.Changeset&lt;/code&gt;. We use this argument to map a boolean toggle input to an enumerated attribute.
&lt;code&gt;change relate_actor(:author)&lt;/code&gt; refers to a &quot;built in change&quot;. This change is provided by Ash out of the box, and this relates the actor (A.K.A current user) to the thing that was just created as the &lt;code&gt;:author&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are all macros! However, they again map to an introspectable structure, acting more as configuration than as a traditional macro.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;iex&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;iex(1)&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt; Ash&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Info&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Twitter&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Tweets&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Tweet&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; :create&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;%&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Ash&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Actions&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;  name: :create&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;  primary?: true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;  type: :create&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  …&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;  arguments:&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    %&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Ash&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Actions&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Argument&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;      name: :public&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      …&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;  changes:&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    %&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Ash&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Change&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{...},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    %&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Ash&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Change&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;      change:&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; {&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Ash&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;Change&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-2&quot;&gt;RelateActor&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;       [&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;allow_nil?: false&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; relationship: :author&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      …&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  …&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This inversion of control is something we&#39;re familiar with in other tools like &lt;code&gt;Plug&lt;/code&gt; or &lt;code&gt;Phoenix.Endpoint/Router&lt;/code&gt;. They are great examples of how this pattern can allow for code reuse, and make complex chains of behavior easier to reason about.&lt;/p&gt;
&lt;h2 id=&quot;its-not-necessarily-about-macros&quot;&gt;Its not necessarily about macros&lt;/h2&gt;
&lt;p&gt;Above I&#39;ve made a case for macros not necessarily being what gives Ash its relatively steep learning curve. We&#39;ve also create a large suite of tools to mitigate the difficulties that these configuration macros can have. Take a look at &lt;a rel=&quot;external&quot; href=&quot;https://hex.pm/packages/spark&quot;&gt;spark&lt;/a&gt; for more information on those tools.&lt;/p&gt;
&lt;p&gt;With that said, in that one example above, we had to add five new words to our vocabulary. This is what can make Ash difficult. When you set out to write your own application patterns, you can let your own vocabulary evolve. This provides a natural learning curve. I&#39;d also argue that it gives you tons of opportunities to repeat the mistakes of every developer who went through that same process.&lt;/p&gt;
&lt;h2 id=&quot;therein-lies-the-rub&quot;&gt;Therein lies the rub&lt;/h2&gt;
&lt;p&gt;Ash presents a huge suite of integrated tools, a suite that gets bigger and bigger every day and, in order to leverage it, you need to learn a whole new way of doing things. This is a very valid reason not to use Ash. Our biggest proponents will tell you that it’s worth it to put in the effort to learn these tools. As would I. And I don’t mean for the idea of what the framework may become, but for the benefits you can get right now.&lt;/p&gt;
&lt;p&gt;With that said, I think a diversity of mindsets is the most important thing that we can foster as a community, and there is a fine line between telling people &quot;Hey, I think we’ve got some good ideas over here, I think you should check them out&quot;, and saying &quot;Hey, this is the right way to do things, stop doing things your way and do them our way&quot;. So while I’m of course a proponent of Ash, my plan is not to &lt;em&gt;talk anyone into using it&lt;/em&gt;. I have one simple goal, which is to continue to expand this integrated suite of tools, adding new capabilities that are only possible for things built in this way. Ash is a snowball, rolling down a hill, and it has barely even begun to gather snow. By doing this, we move the threshold by which cost of learning the &quot;Ash way&quot; is offset by the benefits. For many users (more every day) that threshold has already been crossed. For others, it may never be crossed, and thats okay 😊. If the tools we build can help even one person find success on their Elixir journey, then I’m a happy camper.&lt;/p&gt;
&lt;h2 id=&quot;parting-words&quot;&gt;Parting Words&lt;/h2&gt;
&lt;p&gt;The cost of learning something like Ash is not trivial. It depends on you, your problem space, your experience level, and many other factors as to whether or not that juice is worth the squeeze. My goal, and the core team&#39;s goal, is to continue to provide leverage for those already using Ash. To take the patterns we&#39;ve set down and take them even further, increasing the value of our existing users&#39; investments. Partially because we count among those users, but also because we believe in what we&#39;re building and its ability to help us and others build bigger and better software.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Happy Holidays from Ash Framework!</title>
        <published>2022-12-22T23:49:03+00:00</published>
        <updated>2022-12-22T23:49:03+00:00</updated>
        
        <author>
          <name>
            Zach Daniel
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/blog/happy-holidays-2022/"/>
        <id>https://ash-hq.org/blog/happy-holidays-2022/</id>
        
        <content type="html" xml:base="https://ash-hq.org/blog/happy-holidays-2022/">&lt;figure class=&quot;figure figure--small&quot;&gt;&lt;img src=&quot;/images/three-trees.svg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; /&gt;&lt;/figure&gt;
&lt;h2 id=&quot;happy-holidays&quot;&gt;Happy Holidays!&lt;/h2&gt;
&lt;p&gt;This year has been a wild ride 🎢. So much has gone into Ash Framework this year, and our user base has increased &lt;em&gt;massively&lt;/em&gt; in that time frame, and there is &lt;em&gt;so much&lt;/em&gt; more to come. We have some huge things just around the river bend 🎉, and I can&#39;t wait for you all to see it!&lt;/p&gt;
&lt;div class=&quot;callout callout--note&quot; role=&quot;note&quot;&gt;
    &lt;svg
  class=&quot;icon&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;
  xmlns=&quot;http://www.w3.org/2000/svg&quot;
  width=&quot;24&quot;
  height=&quot;24&quot;
  viewBox=&quot;0 0 24 24&quot;
  fill=&quot;none&quot;
  stroke=&quot;currentColor&quot;
  stroke-width=&quot;2&quot;
  stroke-linecap=&quot;round&quot;
  stroke-linejoin=&quot;round&quot;
&gt;
  &lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot; /&gt;
  &lt;path d=&quot;M12 16v-4&quot; /&gt;
  &lt;path d=&quot;M12 8h.01&quot; /&gt;
&lt;/svg&gt;
    &lt;div class=&quot;callout__content&quot;&gt;
        &lt;p class=&quot;callout__title&quot;&gt;&lt;span class=&quot;visually-hidden&quot;&gt;Note: &lt;/span&gt;A strangely necessary foreword:&lt;/p&gt;
        &lt;div class=&quot;callout__body&quot;&gt;
This message was not built by or with the use of any ML algorithms. There is a place for generated content, but a thank you to my users and the community is not that place.
&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;p&gt;When I set out to build Ash, &lt;strong&gt;it was about technology&lt;/strong&gt;💻. It was about what I felt was missing from the Elixir ecosystem and from the programming ecosystem in general. It was something I believed in, and something that I thought other people would want to use, but it was fundamentally about what I wanted. Then the users started trickling in, and &lt;strong&gt;it became about a whole lot more&lt;/strong&gt;. This work has become so much more meaningful than I ever thought it could be and, perhaps a bit delusionally, I think this project can make a &lt;strong&gt;real difference&lt;/strong&gt; on the lives and livelihoods of the people that I&#39;ve come to care so deeply about. Just like the amazing people who built Elixir and its community up to what it is today. That is what this project is about for me now, and it is what keeps me going.&lt;/p&gt;
&lt;h2 id=&quot;community&quot;&gt;Community&lt;/h2&gt;
&lt;p&gt;I&#39;d like to start by thanking the community 🫂 in general, which is exemplified by the &lt;a rel=&quot;external&quot; href=&quot;https://discord.gg/D7FNG2q&quot;&gt;Ash Framework Discord&lt;/a&gt;. It has become a thriving place, filled with wonderful and helpful people. I thought it was going to be a place for Ash Framework support, but its turned into a place for people with similar ideas about how things ought to be built to talk about anything under the sun. A few fast friendships have sprung from the community, and its been an amazing pleasure to help you all and to see you help each other. If you want to meet and learn about all these amazing folks, join us!&lt;/p&gt;
&lt;iframe src=&quot;https://discord.com/widget?id=711271361523351632&amp;theme=dark&quot; width=&quot;350&quot; height=&quot;500&quot; allowtransparency=&quot;true&quot; frameborder=&quot;0&quot; sandbox=&quot;allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;contributors&quot;&gt;Contributors&lt;/h2&gt;
&lt;p&gt;Contributions come in many forms, but I&#39;d like to kick this off by thanking everyone who contributed directly to Ash. To all those who added features, fixed bugs, or improved documentation, you have my eternal gratitude. I&#39;ve never been more busy than I have this last year, so contributions like those that take work directly off my plate are &lt;em&gt;invaluable&lt;/em&gt;. More importantly than helping me, these contributions compound over time and help &lt;em&gt;every Ash user&lt;/em&gt;, and as such even small contributions have an incalculable impact on the trajectory of Ash. I hope you feel empowered to contribute again, and I hope that anyone else who is interested feels just as empowered to do so.&lt;/p&gt;
&lt;p&gt;Another way that people contribute is by creating issues, making feature requests, or just making us aware of issues. It would be easy to see these as a burden. After all, they do typically mean more work for me to do. Maybe on my most cynical days, I feel this way 😆. In reality, it couldn&#39;t be further from the truth. I&#39;d like to thank everyone who took the time to point out issues, either by writing up a Github issue or by letting us know in the discord. With each issue resolved, the experience for the next person is that much better. With each suggestion of what Ash could do better or differently, we come to a better shared understanding of where Ash is headed and how it can be made better. Thank you all for your time, effort, and ideas!&lt;/p&gt;
&lt;h2 id=&quot;sponsors&quot;&gt;Sponsors&lt;/h2&gt;
&lt;figure class=&quot;figure figure--small&quot;&gt;&lt;a href=&quot;https://alembic.com.au&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;img src=&quot;/images/alembic-logo.png&quot; alt=&quot;Alembic&quot; loading=&quot;lazy&quot; /&gt;
  &lt;span class=&quot;visually-hidden&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;Last but not least, I&#39;d like to thank my sponsors, the largest of which (if sponsor is even the correct term) is my employer &lt;a rel=&quot;external&quot; href=&quot;https://alembic.com.au/&quot;&gt;Alembic&lt;/a&gt;. They were among the first to see the potential of what Ash could be and where it is headed. Thanks to them, I can work on or with Ash effectively full time, either by continuing to add to the framework or by helping with their various client projects using Ash. Their insight, input, and investment has been invaluable, and is absolutely the driving factor behind the Ash ecosystem. If you have any software projects that you&#39;d like built at breakneck speed with the latest and greatest tools, if you have any stalled initiatives due to tech debt or lack of resources, if you are building with Ash and want the experts to help, contact us. I&#39;ve worked with a lot of technical teams in the past, and the staff at Alembic is the most pragmatic, intelligent, and value-driven that I&#39;ve ever had the pleasure of working with. Thank you to all of those at Alembic who have welcomed me and helped with Ash, and for all of your effort helping Ash become everything we know that it can be!&lt;/p&gt;
&lt;figure class=&quot;figure figure--small&quot;&gt;&lt;a href=&quot;https://coinbits.app&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;img src=&quot;/images/coinbits-logo.png&quot; alt=&quot;Coinbits&quot; loading=&quot;lazy&quot; /&gt;
  &lt;span class=&quot;visually-hidden&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;In addition to Alembic, there are other sponsors who fund my work, both individuals and companies like &lt;a rel=&quot;external&quot; href=&quot;https://coinbits.app/&quot;&gt;Coinbits&lt;/a&gt;. Ask anyone who knows, and they&#39;ll tell you that Ash is essentially two full time jobs for me, and these sponsorships are one of the ways that I manage the level of support and effort that I put into this project. Knowing that my work is impactful, to the point that you want to buy me a coffee (or more), is worth more than I can say. Thank you all for your support.&lt;/p&gt;
&lt;p&gt;I can&#39;t wait to see what 2023 holds for Ash and its community. If 2022 is anything to go by, we&#39;ve got a huge year ahead of us 😎. I hope you all have a wonderful holiday season, and keep your eyes peeled because we have a very big announcement coming up!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zach&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Ash Framework 2.0 Release</title>
        <published>2022-11-01T04:48:42+00:00</published>
        <updated>2022-12-21T19:50:53+00:00</updated>
        
        <author>
          <name>
            Zach Daniel
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ash-hq.org/blog/ash-framework-2-0-release/"/>
        <id>https://ash-hq.org/blog/ash-framework-2-0-release/</id>
        
        <content type="html" xml:base="https://ash-hq.org/blog/ash-framework-2-0-release/">&lt;h2 id=&quot;ash-framework&quot;&gt;Ash Framework&lt;/h2&gt;
&lt;figure class=&quot;figure figure--small&quot;&gt;&lt;img src=&quot;/images/ash-logo-side.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; /&gt;&lt;/figure&gt;
&lt;h2 id=&quot;what-is-ash&quot;&gt;What is Ash?&lt;/h2&gt;
&lt;p&gt;Ash Framework is a declarative, resource-oriented application development framework for Elixir. A resource can model anything, like a database table, an external API, or even custom code. Ash provides a rich, and extensive set of tools for interacting with and building on top of these resources. By modeling your application as a set of resources, other tools know exactly how to use them, allowing extensions like &lt;code&gt;AshGraphql&lt;/code&gt; and &lt;code&gt;AshJsonApi&lt;/code&gt; to provide top tier APIs with minimal configuration. With filtering, sorting, pagination, calculations, aggregations, pub/sub, policy authorization, rich introspection, and &lt;em&gt;much&lt;/em&gt; more built-in, as well as a comprehensive suite of tools to allow you to build your own extensions, the possibilities are endless.&lt;/p&gt;
&lt;p&gt;For those familiar with Phoenix, you can think of Ash as a declarative application modeling layer designed to replace your Phoenix contexts.&lt;/p&gt;
&lt;h2 id=&quot;ash-framework-2-0&quot;&gt;Ash Framework 2.0&lt;/h2&gt;
&lt;p&gt;Ash Framework 2.0 has been released! This begins the official stable release cycle (although it was already quite stable). Thanks to everyone in the community who helped make this possible, from the contributors, the curious, to those already using Ash in prod. I&#39;m eternally grateful for all of your support.&lt;/p&gt;
&lt;p&gt;Additionally, I&#39;d like to thank Alembic, who have brought me on to work on Ash full time. Alembic is building complex software with small teams in record time by leveraging tools like Ash Framework and Phoenix LiveView.&lt;/p&gt;
&lt;p&gt;Along with the 2.0 release of core, the &lt;code&gt;AshPostgres&lt;/code&gt;, &lt;code&gt;AshPhoenix&lt;/code&gt; and &lt;code&gt;AshArchival&lt;/code&gt; packages have had 1.0 version released as well. &lt;code&gt;AshGraphql&lt;/code&gt; is next up, and should be released in the next few weeks. Feel free to dive in to it before then, though :).&lt;/p&gt;
&lt;h2 id=&quot;is-ash-an-alternative-to-x&quot;&gt;Is Ash an alternative to X?&lt;/h2&gt;
&lt;p&gt;Ash is not meant to be an alternative to Phoenix, Ecto, or Absinthe. Ash uses Ecto under the hood, &lt;code&gt;AshGraphql&lt;/code&gt; uses Absinthe. Phoenix is absolutely the recommended way to build web interfaces on top of your Ash application (there is a whole package dedicated to it, &lt;code&gt;AshPhoenix&lt;/code&gt;). Ash is not meant to be the only way that you ever interact with your data, so it is almost a certainty that you will need to use &lt;code&gt;Ecto&lt;/code&gt; in some cases. For instance, Ash does not currently support bulk actions or atomic updates. For this reason, you can implement custom actions for things that can be encapsulated in your resource, and you have all of Elixir at your disposal to implement custom behavior outside of your resources, with a wide array of escape hatches in between.&lt;/p&gt;
&lt;h2 id=&quot;extensions&quot;&gt;Extensions&lt;/h2&gt;
&lt;h3 id=&quot;extensions-in-1-0&quot;&gt;Extensions in 1.0+&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AshPostgres&lt;/code&gt; - Back a resource with postgres. Rich querying capabilities, supporting aggregates, calculations, and fragments. Comes with a migration generator to get you up and running in record time!&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AshPhoenix&lt;/code&gt; - Helpers to integrate Ash Resources with Phoenix. Tools like &lt;code&gt;AshPhoenix.Form&lt;/code&gt; allow you to build forms over your resources, and manage complex nested related data with one data structure.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AshArchival&lt;/code&gt; - A tiny but powerful extension. Get archival (A.K.A soft deletion) with one line of code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;extensions-1-0&quot;&gt;Extensions &amp;lt;1.0&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AshGraphql&lt;/code&gt; - Create a GraphQL from your resources with only a few lines of code. Backed by the excellent Absinthe library. It comes with its own fully implemented dataloader, and automatically derives all the types, fields, and mutations automatically. Getting a relay compatible GraphQL API is as easy as setting the &lt;code&gt;relay?&lt;/code&gt; toggle.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AshJsonApi&lt;/code&gt; - Create a JSON:API spec compliant API in minutes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AshAdmin&lt;/code&gt; - A rich admin UI automatically derived from your resource definitions.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AshCsv&lt;/code&gt; - Back your resource with a CSV file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Spark&lt;/code&gt; - The core declarative DSL library that backs Ash and its extensions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;unreleased-extensions&quot;&gt;Unreleased Extensions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AshPaperTrail&lt;/code&gt; - Creates and manages a versions table for a resource, and writes all changes to that version resource. With one line of code.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AshJsonApiWrapper&lt;/code&gt; - Back your resource with an external API using finch and configuration to describe how your resource maps to the response from the external service.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;your-own-extensions&quot;&gt;Your Own Extensions&lt;/h3&gt;
&lt;p&gt;All of the extensions above are created with a set of tools that are free to use to create your own extensions. They can all be used as a basis point, or as inspiration. Many users have created their own extensions for various reasons. An extension can both add to the resource&#39;s DSL and programatically restructure the resource. For example, &lt;code&gt;AshArchival&lt;/code&gt; adds an attribute, modifies every destroy action, and adds a &quot;base filter&quot; to the resource. This allows for extremely powerful extensions.&lt;/p&gt;
&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://ash-hq.org&quot;&gt;New and improved doc!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://www.youtube.com/watch?v=BchANMO1f8s&quot;&gt;Office hours/Q&amp;amp;A release livestream&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://discord.gg/D7FNG2q&quot;&gt;Official discord Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/sponsors/zachdaniel/&quot;&gt;Sponsors Dashboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://twitter.com/AshFramework&quot;&gt;Ash Framework Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/ash-project&quot;&gt;Source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/orgs/ash-project/projects/3&quot;&gt;Roadmap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://alembic.com.au/&quot;&gt;Alembic&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Expect to see a lot more content before long, like tutorial and short, topical videos.&lt;/p&gt;
&lt;h2 id=&quot;where-to-start&quot;&gt;Where to start?&lt;/h2&gt;
&lt;p&gt;Ash is quite broad, and covers a considerable amount of ground. My suggestion is to follow the tutorials for a kicking off point. There are getting started guides for each individual package as well that, combined, will get you up and running from scratch.&lt;/p&gt;
&lt;p&gt;😎 Happy hacking! 😎&lt;/p&gt;
</content>
        
    </entry>
</feed>
