Skip to content
Skills Library
Programming · Building & coding

elixir-meta-programming

/elixir-meta-programming

Use this skill when building an Elixir macro or a declarative DSL — a schema/spec/route/workflow language in the shape of Ecto.Schema, Absinthe, Plug.Router, or ExUnit — with quote/unquote, __using__, module attributes, and @before_compile. It corrects the wrong defaults a model makes once it commits to metaprogramming — putting logic inside the quote block, re-evaluating unquoted expressions, forgetting Macro.escape, fighting hygiene with var!, generating code ad hoc instead of accumulating declarations, and validating the DSL at runtime rather than at compile time. Applies to writing or revi

Repository
dot-skills ↗
Licence
MIT
Last repository change
15.08.2026

Assessment and security

90/ 100Class: good
Format25 / 25
Frontmatter with name and description, valid name, description up to 1,024 characters, name matching the folder.
Discoverability25 / 25
Description explains when to use the skill, has a reasonable length and uses third person.
Content25 / 35
At least 150 words and at most 500 lines, headings, step lists, examples or code.
Hygiene15 / 15
No placeholders such as TODO, no Windows paths, with a licence.
Riskno signals detected in the scan
Files in this package
16
Scripts
0
HTML comments
0
URLs in SKILL.md
0
Deprecation

No signals detected. Repository changed on 15.08.2026, without references to retired models or APIs.

Licence

MIT, open: use and modify while retaining author attribution.

What it does and when to use it

Use this skill when building an Elixir macro or a declarative DSL — a schema/spec/route/workflow language in the shape of Ecto.Schema, Absinthe, Plug.Router, or ExUnit — with quote/unquote, __using__, module attributes, and @before_compile. It corrects the wrong defaults a model makes once it commits to metaprogramming — putting logic inside the quote block, re-evaluating unquoted expressions, forgetting Macro.escape, fighting hygiene with var!, generating code ad hoc instead of accumulating declarations, and validating the DSL at runtime rather than at compile time. Applies to writing or revi

Original author description

Use this skill when building an Elixir macro or a declarative DSL — a schema/spec/route/workflow language in the shape of Ecto.Schema, Absinthe, Plug.Router, or ExUnit — with quote/unquote, __using__, module attributes, and @before_compile. It corrects the wrong defaults a model makes once it commits to metaprogramming — putting logic inside the quote block, re-evaluating unquoted expressions, forgetting Macro.escape, fighting hygiene with var!, generating code ad hoc instead of accumulating declarations, and validating the DSL at runtime rather than at compile time. Applies to writing or revi

How it works

  1. First: is a macro actually warranted?
  2. When to Apply
  3. Rule Categories
  4. Quick Reference
  5. How to Use
  6. Related Skills
  7. Reference Files
Original SKILL.md headings · 78 lines, 681 words
SKILL.md preview
---
name: elixir-meta-programming
description: Use this skill when building an Elixir macro or a declarative DSL — a schema/spec/route/workflow language in the shape of Ecto.Schema, Absinthe, Plug.Router, or ExUnit — with quote/unquote, __using__, module attributes, and @before_compile. It corrects the wrong defaults a model makes once it commits to metaprogramming — putting logic inside the quote block, re-evaluating unquoted expressions, forgetting Macro.escape, fighting hygiene with var!, generating code ad hoc instead of accumulating declarations, and validating the DSL at runtime rather than at compile time. Applies to writing or reviewing macro/DSL code. NOT for deciding whether to use a macro at all — that gate belongs to staff-level-elixir and adversarial-elixir, which say prefer plain functions.
---

# 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 `quote` block versus a runtime function, and how arguments evaluate
- Building a declarative DSL — a `schema`/`spec`/`route`/`workflow` block 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 compile` with 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`](references/macro-thin-delegate-runtime.md) — quote to a single call into a plain function; keep logic testable and out of every call site
- [`macro-defguard-for-guards`](references/macro-defguard-for-guards.md) — `defguard` when the construct must work inside a guard

### 2. Quote & Hygiene

- [`quote-bind-quoted-single-eval`](references/quote-bind-quoted-single-eval.md) — `bind_quoted` so a repeated `unquote` argument evaluates once
- [`quote-escape-runtime-data`](references/quote-escape-runtime-data.md) — `Macro.escape/1` before splicing a computed term into `quote`
- [`quote-rely-on-hygiene`](references/quote-rely-on-hygiene.md) — rely on hygiene; use `var!` only when injecting a name is the documented contract

### 3. DSL Construction

- [`dsl-accumulate-attributes`](references/dsl-accumulate-attributes.md) — record declarations in an `accumulate: true` attribute, generate in `@before_compile`
- [`dsl-using-minimal`](references/dsl-using-minimal.md) — `__using__` does setup only; generation belongs in `@before_compile`
- [`dsl-unquote-fragments`](references/dsl-unquote-fragments.md) — `def unquote(name)(...)` in a comprehension to emit one clause per declaration
- [`dsl-introspection-function`](references/dsl-introspection-function.md) — generate a `__schema__`-style reflection function so callers read data

### 4. Compile-Time Validation

- [`valid-fail-at-compile-time`](references/valid-fail-at-compile-time.md) — validate in the macro body, not the returned `quote`, so errors fire at `mix compile`
- [`valid-locate-errors-at-caller`](references/valid-locate-errors-at-caller.md) — 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](references/_sections.md) — category structure and ordering
- [Rule template](assets/templates/_template.md) — for adding new rules
- [AGENTS.md](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" gate
- `adversarial-elixir` — architecture-level review that flattens unnecessary macro DSLs; the diagnostic counterpart to this skill

## Reference Files

| File | Description |
|------|-------------|
| [references/_sections.md](references/_sections.md) | Category definitions and ordering |
| [assets/templates/_template.md](assets/templates/_template.md) | Template for new rules |
| [metadata.json](metadata.json) | Version and source references |
Files in this package (16)
  • AGENTS.md 3 KB
  • SKILL.md 6 KB
  • metadata.json 1 KB
  • assets/templates/_template.md 1 KB
  • references/_sections.md 2 KB
  • references/dsl-accumulate-attributes.md 2 KB
  • references/dsl-introspection-function.md 1 KB
  • references/dsl-unquote-fragments.md 1 KB
  • references/dsl-using-minimal.md 1 KB
  • references/macro-defguard-for-guards.md 1 KB
  • references/macro-thin-delegate-runtime.md 1 KB
  • references/quote-bind-quoted-single-eval.md 1 KB
  • references/quote-escape-runtime-data.md 1 KB
  • references/quote-rely-on-hygiene.md 2 KB
  • references/valid-fail-at-compile-time.md 1 KB
  • references/valid-locate-errors-at-caller.md 2 KB

Versions

The database has no other skills named elixir-meta-programming.

Database snapshot: 13.11.2026 · package version 570626e37c96 (SHA-256 digest of the entire package)

Downloads in the last 30 days: 0 · ranking

Report a problem with this skill: harmful content, incorrect descriptions or an author’s removal request.

Similar skills

  • 100elixir-ecto-patterns
    Use when Elixir Ecto patterns including schemas, changesets, queries, and transactions. Use when building database-driven Elixir applications.
  • 95ecto-query-patterns
    Use when querying data with Ecto.Query DSL including where clauses, joins, aggregates, preloading, and query composition. Use for building flexible database queries in Elixir applications.
  • 100elixir-test-critic
    Elixir/ExUnit test quality expert. Use when reviewing Elixir test files, auditing Elixir test suites, writing ExUnit tests, or answering questions about Elixir testing best practices. Not for other languages.
  • 100ecto
    Guide for Ecto schemas, changesets, queries, migrations, and Multi. Use when writing Ecto migrations, querying or mutating Ecto schemas, designing changesets, working with Ecto.Multi, or choosing between Ecto query DSL and raw SQL.
  • 95sentry-elixir-sdk
    Full Sentry SDK setup for Elixir. Use when asked to "add Sentry to Elixir", "install sentry for Elixir", or configure error monitoring, tracing, logging, or crons for Elixir, Phoenix, or Plug applications. Supports Phoenix, Plug, LiveView, Oban, and Quantum.
  • 95using-elixir-skills
    This skill should be used when the user works on any .ex or .exs file, mentions Elixir/Phoenix/Ecto/OTP, the project has a mix.exs, or asks "which skill should I use", "new to Elixir", "help with Elixir". Routes to the correct thinking skill BEFORE exploring code. Triggers on…

Feedback and reports

Your message goes to Bartosz Sokoliński. An email address is needed only for a reply. See the privacy policy.