The instinct to document macros with comments in the SQL file is reasonable but wrong. YAML documentation in _macros.yml is strictly better: it appears in dbt docs, supports structured argument definitions, and creates a single location to look up any macro in your project. Comments in SQL files are invisible to dbt’s documentation system and scattered across dozens of files.
The _macros.yml Structure
One _macros.yml file at the root of your macros/ directory covers everything. It lives at macros/_macros.yml and follows the same version: 2 format as model YAML files:
version: 2
macros: - name: cents_to_dollars description: | Converts an integer cents column to a decimal dollars value.
## Usage ```sql {{ cents_to_dollars('amount_cents') }} {{ cents_to_dollars('amount_cents', scale=4) }} ```
## Notes - Assumes input is already an integer (no validation) - Uses ROUND for consistent decimal places arguments: - name: column_name type: string description: The column containing cents values - name: scale type: integer description: Number of decimal places. Defaults to 2.The arguments block is what separates YAML documentation from a comment: it gives dbt structured metadata about each parameter’s name, type, and purpose. This appears in dbt docs alongside the description, giving anyone who lands on your documentation page a complete picture of how to use the macro.
Writing Descriptions That Get Used
The description field accepts Markdown. Use it.
The most important thing you can put in a description is a usage example — specifically, copy-pasteable code that shows the macro in a real context. Developers reading documentation are trying to accomplish something. They want to see what the call looks like, modify it for their case, and move on. Abstract argument descriptions don’t help as much as a concrete example they can actually use.
- name: limit_data_in_dev description: | Adds a date filter in development environments to limit data volume. Returns empty string in production.
## Usage Add to WHERE clause with AND: ```sql WHERE 1=1 {{ limit_data_in_dev('created_at', 7) }} ```
In dev: filters to last 7 days. In prod: no filter (clause is empty string). arguments: - name: column_name type: string description: Date column to filter on. Defaults to 'created_at'. - name: days type: integer description: Number of days to include. Defaults to 3.Notice what the description does: it answers the “how do I use this?” question immediately, then explains the behavior difference between environments. The arguments section fills in the details about defaults. Someone reading this can copy the usage example, adjust the column name and day count, and be done.
The notes section (if you include one) is where you put gotchas: assumptions the macro makes, edge cases it doesn’t handle, things to watch out for. Keep it honest. If the macro assumes integer input and doesn’t validate, say so. If it behaves unexpectedly with NULL values, document that.
What to Document for Every Macro
At minimum:
- A one-sentence description of what the macro does
- A usage example showing the simplest call
- All arguments with their types and defaults
For complex macros:
- Multiple usage examples showing different argument combinations
- A notes section for gotchas and assumptions
- Examples of output for different inputs
For override macros (generate_schema_name, generate_alias_name):
- The environment-aware behavior (what happens in dev vs prod)
- The decision logic, since these are called automatically by dbt and the behavior isn’t always obvious
The Common Documentation Failure
The most common failure in macro documentation is writing argument descriptions that restate the obvious:
# Badarguments: - name: column_name description: The column name. - name: scale description: The scale.# Betterarguments: - name: column_name type: string description: Column containing integer cents values (e.g., 'amount_cents'). - name: scale type: integer description: Number of decimal places in the output. Defaults to 2. Use 4 for precision-sensitive financial calculations.The better version tells you what kind of value goes in (integer cents, not dollars), gives a concrete example, and explains when you’d override the default. The bad version is just noise.
Documentation and Discoverability
YAML documentation pairs with good naming to make macros discoverable. The name communicates purpose at a glance; the documentation explains how to call it correctly. A new team member can browse _macros.yml to understand the project’s macro library and use any macro without asking a teammate.