Hello Curious Coders,
If you’ve worked with Elixir for a while, you already know the pull of a clean functional core. You take the messy business of the world, the database calls and the user input and the network failures, and you push it out toward the edges. What’s left in the middle is a pure function: same input, same output, no surprises.
We’ve always argued that this discipline is for us. It makes code easier to reason about and much easier to test. But there’s another beneficiary now, and that’s where this post starts.
The model on the other side of your editor benefits from the same discipline you do. Once you see why, your design choices stop being just an Elixir habit and start being a collaboration strategy.
What Bruce Noticed
After the planning is done, you have an architecture you trust. That’s where Bruce starts in his Word Games project: Connections, Spelling Bee, and Wordle, all sharing the same shape underneath.
Before generating a single line of game code, Bruce stops and builds a skill. Not a subagent, not a slash command, but a project-level skill. The reasoning is worth understanding because it’s the heart of this post.
A subagent is an execution model: it goes off and does work. A slash command is something you trigger manually. A skill is passive. It sits there with a set of patterns and conventions, and it activates when Claude detects the situation it was made for. In this case, the skill auto-triggers whenever Claude detects that the team is building a functional core.
The skill encodes one rule: when you build a core, follow CRC. Construct the type, reduce one move, convert for rendering. Bruce calls it the “prepare to work, do some work, show your work” pattern. It’s the same shape that powers Enum.reduce, GenServer state, and LiveView updates, so the model is being nudged into a structural template that already runs through the rest of the language.
The skill specifies one more thing: run on Haiku.
“The models can reason about pure functional cores better too. And that means that you’re going to be able to apply a less expensive model per token and get better results.” — Bruce Tate
Read that again. A lighter, cheaper model can produce better code on this task, because the task is constrained. The skill removes the ambiguity that would normally require a heavier model to resolve.
🎯 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.
How Bruce Reviews What Came Back
After Claude produces the cores, Bruce opens them like a code review. He looks at the public API first, then the structural pattern, then the boundary between pure and impure.
In Wordle, the constructor builds the type, the reducer takes a game and a move and returns a game, and the converter prepares state for rendering. Then comes the more interesting check. Bruce notices a dictionary_id field in the struct. A dictionary is the kind of thing that should live at the edges, not inside the core.
Here is the mistake we are trying to avoid:
def play(game, guess) do
dictionary = Dictionary.load(game.dictionary_id)
Wordle.Core.play(game, guess, dictionary)
end
That looks convenient, but the core now depends on dictionary loading. The cleaner version receives the words as data:
def play(game, guess, dictionary) do
Wordle.Core.play(game, guess, dictionary)
end
Now the uncertain work stays outside the core. The reducer can be tested with plain values. So Bruce asks Claude directly: is the generated code still pure?
The answer is the part of the transcript I keep coming back to:
“The core never loads the dictionaries itself. The dictionary handling uses dependency injection to maintain purity.” — Claude, reviewing its own output
The core doesn’t fetch, it receives. The impure work, the loading, the database lookup, lives in a shell that hasn’t been built yet. That’s the right answer, and it came from a model that was guided by the skill into producing it.
Why This Matters for the Bill
There’s a counterintuitive argument here, and it’s worth stating plainly. More constrained code makes AI collaboration better, not harder. The same property that makes a pure function easy for you to test makes it easy for the model to generate, easier to verify, and easier to keep correct as the system grows.
“We are building something that could be built with fewer tokens, with better tests, and with a more understandable API.” — Bruce Tate
That last quote is the business case. The token bill drops because the model has less to reason about. Tests get cleaner because pure functions don’t need elaborate setup. The API stays understandable because CRC gives every module the same three doors. Mistakes get caught at the boundary instead of buried five callbacks deep.
Disciplined planning gave us an architecture we trust. The functional core gives Claude a structure it can build well. Now we have a system worth shipping, and the next problem is keeping the conveyor belt clean as the work gets longer.
In the next post, we’ll look at context window management and the layers we build around the core.
See you in the next chapter.
🤖 Master the Ask → Plan → Agent Framework
This comes from Bruce's AI Agents course — the anti-vibe-coding curriculum. Learn the CRC pattern (construct, reduce, convert) for functional cores that Claude can build correctly and cheaply, plus the Ask → Plan → Agent framework for the rest of your workflow. Available via monthly subscription — try it for one month.
— Paulo & Bruce