Hello Curious Coders,
Here’s what happens when you work with AI coding agents without structure.
You have a project with specific rules: use Elixir, prefer Req over HTTPoison, two-space indentation, no deep nesting. You type these rules into your first prompt. The agent builds something good. Then you move to the next task. You type the same rules again. Then you need a specialized developer to focus on Phoenix frontend. You type the rules a third time, this time adding frontend-specific patterns. By the tenth prompt, you’re copy-pasting axioms that haven’t changed since the beginning.
This is a waste. But there’s a deeper problem: when you don’t codify your preferences, they drift. One prompt mentions a naming convention, another leaves it out, another adds a detail you forgot. The agent does its best with partial information. You end up with code that follows different patterns in different files.
So here’s a question worth sitting with: if you have to explain your preferences in every session, you haven’t built a workflow. You’ve just automated the typing.
The DRY Principle for AI
In software engineering, we have the DRY principle: Don’t Repeat Yourself. When we see the same logic in three different places, we abstract it. The same principle applies to how we organize instructions for AI agents.
“We’re starting to layer the organization of our prompts the same way that you organize programs.”
— Bruce Tate
Once you see it, the path forward is clear: global truths go at the top, roles and specializations go in their own files, repeatable tasks get their own commands.
Here is what that structure looks like in practice:
System or Project Level
│
├── AXIOMS (.claude/claude.md)
│ └── "Language is Elixir"
│ └── "Indentation: 2 spaces"
│ └── "Prefer Req over HTTPoison"
│
├── ROLES (.claude/agents/*.md)
│ └── phoenix-frontend.md
│ └── otp-expert.md
│ └── api-developer.md
│
└── TASKS (.claude/commands/*.md)
└── review.md
└── refactor.md
└── test.md
Three layers. Each one serves a different purpose.
The Three Layers
Axioms are the universal truths of your project. They are the things that are always true, no matter what you are building. In an Elixir project, your axioms might include indentation rules, your preference for Req over HTTPoison, or the requirement that all public functions have @spec tags.
“A
.claude.mdis intended to be a system-wide or project-wide set of axioms that we need to specify once for the overall project and never again for individual prompts.”— Bruce Tate
Roles are specialized personas the AI steps into. Not every prompt needs a CSS designer, and not every refactor needs an OTP expert. By defining roles like “Phoenix Front-end Developer” or “API Architect”, you provide deep context only when it’s relevant. You’re not loading context you don’t need.
Think of context like a budget. Every token you preload is space you cannot use for the current problem. Axioms give you consistency, but only load what earns its cost. A practical test: if a rule should be true everywhere you build, keep it global. If it’s true only in one repo, keep it local. If it changes often, pass it at task time instead.
Tasks are repeatable, parameterized workflows. If you find yourself typing “Review this code for security vulnerabilities” or “Refactor this module into a functional core and an impure shell” more than twice, it shouldn’t be a prompt. It should be a command: /review, /refactor, /test.
🎯 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.
One Thing to Do Today
Use this migration rule: if you repeat the same instruction in three prompts, move it into .claude/claude.md.
The shift looks like this:
# Before (repeated in every session)
"I'm using Elixir. Use two-space indentation. Always use the pipe operator."
# After (in .claude.md, written once)
## Conventions
- Language: Elixir 1.16
- Indentation: 2 spaces
- Prefer pipes to with in the functional core.
- Never use GenServer.call for long-running tasks without a timeout
Start with three sections: your tech stack with specific library choices, your naming conventions, and a short list of patterns you never want to see. A good rule of thumb: if you find yourself correcting the AI on the same point twice, that point belongs in your axioms.
Once the axioms are stable, keep prompts focused on the task:
/review lib/payments/charge.ex
If needed, attach a role for context:
@api-developer /review lib/payments/charge.ex
Before: the same setup text repeated in every session. After: a one-line task call plus a stable rules file.
In the next post, we’ll take this further and turn these roles and tasks into sub-agents and slash commands that do the heavy lifting automatically.
If you want to see how we teach these workflows inside a real Elixir project, the Groxio AI course covers this in depth at grox.io/courses.
🤖 Stop Vibe Coding with Structured Oversight
This comes from Bruce's AI Agents course — the anti-vibe-coding curriculum. Learn when to say no, and when to let AI execute, with the Ask → Plan → Agent framework so you can scale AI use without losing architecture decisions or becoming dependent on a crutch. Available via monthly subscription — try it for one month.
— Paulo & Bruce