6 min read
Why Elixir Is Built for AI-Assisted Development
Predictable Code Gives AI Agents Less Room to Drift

Hello Curious Coders,

If you are building a Phoenix LiveView application today, you have probably spent time pair-programming with a large language model.

You write a prompt. It gives you code. For a few minutes, everything feels unusually smooth.

Then the friction starts.

The model suggests a library that does not exist. It reaches for an option that was deprecated three versions ago. It writes code that looks plausible, but misses the way state actually moves through your application.

The first time this happened to me, I blamed the prompt. Maybe I had been too vague. Maybe I needed more context. Maybe I needed to ask in a smarter way.

Some of that was true, but it was not the whole story.

There is a deeper question underneath the workflow: if you are describing what you want in English, and the model is writing the code, does the language still matter?

Bruce puts the challenge plainly:

“If I’m going to be coding in a natural language like English anyway, why should I choose Elixir? The large language models are especially good at Elixir, even considering the amount of training data out there for JavaScript and Ruby and Python.”

– Bruce Tate

Notice what he is not saying. He is not claiming Elixir is the best language for AI, full stop. The more useful claim is narrower: Elixir gives the model something it badly needs.

Predictability.

The Counter-Force to Randomness

Every language model brings some randomness into your development process. Ask the same question twice and you may get two different answers. Give it a broad feature request and it may choose a path you did not expect.

Functional programming pushes in the other direction.

A function that adds two numbers returns the same result for the same inputs. A counter behaves differently: press increment a few times and the next value depends on state that may be living somewhere else. That difference matters when you ask a model to reason across a real codebase.

Immutability removes a whole class of hidden changes. Referential transparency makes a function easier to understand from its inputs and outputs. The functional core strategy builds on that idea: keep the business logic predictable, then push stateful work to the edges.

Bruce says it this way:

“The models thrive on this kind of predictability. And since the models themselves inject tons of randomness into our development process, we’ve called that non-determinism, the counter force of predictability matters.”

– Bruce Tate

For years, people said functional code was easier to reason about. That phrase could feel abstract until the reader was not a person, but a model. Now the point is visible: the model also needs fewer hidden moving parts.

That is why smaller models can sometimes do surprisingly well against a clean Elixir core. The architecture is carrying some of the reasoning load.

🎯 Join Groxio's Newsletter

Weekly lessons on Elixir, system design, and AI-assisted development — plus stories from our training and mentoring sessions.

We respect your privacy. No spam, unsubscribe anytime.

Mistakes Need a Safety Net

Predictability does not mean the model stops making mistakes. It means the mistakes have less room to spread.

Elixir’s concurrency model helps here. Processes do not share mutable memory, so one part of the system is not quietly changing data underneath another part. When work is isolated, it is easier for both the developer and the model to understand what can affect what.

OTP adds the next layer. A process can crash, restart in a clean state, and let the rest of the system continue. That does not make bad generated code good, but it does mean the runtime is already designed around recovery.

This matters because AI-assisted development is not a perfection workflow. It is a correction workflow. You ask, inspect, adjust, and keep moving. Elixir gives that loop sharper boundaries.

Metadata Beats Guessing

There is another advantage that shows up once the application is running: Elixir systems know a lot about themselves.

They know their dependencies. They know their routes. Phoenix knows what is on the page. Ecto knows how the data is shaped. Your application already knows how to talk to Postgres.

Without that information, the model has to imagine your system from files and prompts. With tools like Tidewave, it can inspect the running application instead. It can fetch local documentation that matches the exact dependency versions you installed. It can look at the DOM that actually exists. It can use your application’s own database connection instead of guessing from a pasted schema.

This is also where consistency matters. JavaScript and Python have more code in the wild, but volume is not the same as signal. The model has to reconcile many valid ways to build the same thing.

Elixir has fewer common paths, and that is a strength.

“Elixir code is remarkably consistent. There are fewer options but a lot of consistent code written in those options. That consistency is something the model can stand on.”

– Bruce Tate

Pipelines, reducers, Phoenix contexts, supervision trees, and Ecto schemas give the model familiar shapes. It still needs guidance, but it is working inside a system with rails.

One Habit to Start Today

Here is the practical rule: when you ask an AI agent to change Elixir code, point it at a small functional boundary first.

Instead of asking:

“Add billing reminders.”

Ask:

“Write a pure function that takes an invoice and returns the reminder state. Do not touch the database, LiveView, or email delivery yet.”

Once that function is right, connect it to the process, database, or LiveView.

This works because you are giving the model the part of the system Elixir makes easiest to reason about: inputs in, outputs out, no hidden state. After that, the stateful edges become integration work instead of a guessing game.

You can use the same habit with a running app. Instead of saying, “fix the page,” ask the agent to inspect the page, identify the failing element, and propose the smallest change. That keeps the model grounded in what exists.

AI brings randomness into the workflow, and Elixir gives that randomness less room to spread. Once you see that, the language choice stops feeling like syntax preference and starts feeling like design pressure.

Before we add more tools to the workflow, though, we need to handle the next constraint: the model’s memory. A rich Elixir application cannot help much if the useful context keeps falling off the conveyor belt.

That is where we go next.

See you in the next chapter.


🤖 Build With AI Without Letting It Drift

This comes from Bruce's AI Agents course — the anti-vibe-coding curriculum. Learn structured oversight for using Elixir's predictable boundaries with the Ask → Plan → Agent framework, without losing architecture decisions or letting AI become a crutch. Available via monthly subscription — try it for one month.

— Paulo & Bruce

Bruce Tate's avatar
Bruce Tate
System architecture expert and author of 10+ Elixir books.
Paulo Valim's avatar
Paulo Valim
Full-stack Elixir developer and educator teaching modern Elixir and AI-assisted development.