Elixir Metaprogramming & DSLs
How to build Elixir macros and declarative DSLs correctly — for the case where a macro is genuinely warranted and a plain function won't do. Each rule corrects a specific wrong default a capable model makes once it starts writing quote/unquote, __using__, and @before_compile; nothing here restates macro basics the model already knows.
First: is a macro actually warranted?
The default answer is no — reach for a higher-order function, a behaviour, or data-plus-an-interpreter first. That gate is not this skill's job; it lives in the sibling skills and you should apply it before opening this one:
staff-level-elixir→data-functions-over-macros(solve it with a function first)adversarial-elixir→meta-functions-not-macro-dsl,meta-use-is-not-import,meta-no-compile-time-coupling
A macro earns its place only when you need something a function cannot give — new syntax, control over whether/when arguments evaluate, or code that must exist at compile time (compiling a schema, generating clauses from a declaration list). Once you've crossed that threshold, this skill is how you do it right.
When to Apply
- Writing a macro — deciding what goes in the
quoteblock versus a runtime function, and how arguments evaluate - Building a declarative DSL — a
schema/spec/route/workflowblock that collects declarations and generates a module - Wiring
use MyDSL— what__using__should inject and what belongs in@before_compile - Generating functions or clauses from a list of declarations at compile time
- Making a DSL fail at
mix compilewith an error that points at the user's source - Reviewing existing macro/DSL code for evaluation, hygiene, escaping, and compile-time-validation bugs
Rule Categories
| # | Category | Prefix | Covers |
|---|---|---|---|
| 1 | Macro Design | macro |
The shape of a single macro — thin macro over a runtime function; defguard for guard-safe constructs |
| 2 | Quote & Hygiene | quote |
Compile-time semantics traps — multiple evaluation, Macro.escape, hygiene vs var! |
| 3 | DSL Construction | dsl |
Assembling a block DSL — accumulating attributes + @before_compile, minimal __using__, unquote fragments, introspection |
| 4 | Compile-Time Validation | valid |
Failing well — validate while compiling, locate errors at the caller's line |
Quick Reference
1. Macro Design
macro-thin-delegate-runtime— quote to a single call into a plain function; keep logic testable and out of every call sitemacro-defguard-for-guards—defguardwhen the construct must work inside a guard
2. Quote & Hygiene
quote-bind-quoted-single-eval—bind_quotedso a repeatedunquoteargument evaluates oncequote-escape-runtime-data—Macro.escape/1before splicing a computed term intoquotequote-rely-on-hygiene— rely on hygiene; usevar!only when injecting a name is the documented contract
3. DSL Construction
dsl-accumulate-attributes— record declarations in anaccumulate: trueattribute, generate in@before_compiledsl-using-minimal—__using__does setup only; generation belongs in@before_compiledsl-unquote-fragments—def unquote(name)(...)in a comprehension to emit one clause per declarationdsl-introspection-function— generate a__schema__-style reflection function so callers read data
4. Compile-Time Validation
valid-fail-at-compile-time— validate in the macro body, not the returnedquote, so errors fire atmix compilevalid-locate-errors-at-caller— raise from the declaration macro / capture__CALLER__so errors cite the user's line
How to Use
Read a reference file when its decision comes up. Each rule names the wrong default it corrects, then shows the canonical way (with an Incorrect/Correct contrast only where the wrong way is a real trap).
- Section definitions — category structure and ordering
- Rule template — for adding new rules
- AGENTS.md — auto-built table of contents across all rules
Related Skills
staff-level-elixir— the broader Elixir/OTP/Ecto/Phoenix judgment skill; owns the "prefer a function over a macro" gateadversarial-elixir— architecture-level review that flattens unnecessary macro DSLs; the diagnostic counterpart to this skill
Reference Files
| File | Description |
|---|---|
| references/_sections.md | Category definitions and ordering |
| assets/templates/_template.md | Template for new rules |
| metadata.json | Version and source references |