ServicesAboutNotesContact Get in touch →
EN FR
Note

dbt Macro Naming Conventions

Naming patterns for dbt macros that make them discoverable, communicative, and well-organized — verb prefixes, descriptive names, internal helper conventions, and the one-macro-per-file rule.

Planted
dbtdata modelingdata engineering

A macro’s name is its documentation at the call site. {{ cents_to_dollars('amount_cents') }} explains what happens without opening the macro file. {{ process_amount('amount_cents', true, 0.1) }} requires looking up the implementation. Macro names are read far more often than any description in _macros.yml.

Verb Prefixes for Actions

Action macros — ones that retrieve data, generate SQL, or transform output — should start with a verb that signals the type of action:

get_ for macros that retrieve data or values:

  • get_column_values — fetches distinct values from a column
  • get_latest_partition — returns the most recent partition date

generate_ for macros that create SQL structures:

  • generate_surrogate_key — produces a hashed key column
  • generate_schema_name — dbt’s override macro for schema naming

format_ for macros that transform or present values:

  • format_timestamp — standardizes a timestamp to a given pattern
  • format_currency — adds currency symbols and formatting

The verb prefix tells you what to expect before you’ve even read the arguments. get_ means the macro is going to query something or look something up. generate_ means it’s producing SQL. format_ means it’s doing presentation logic. These distinctions matter when you’re scanning a model trying to understand what’s happening.

Descriptive Names for Transformations

For transformation macros, choose names that describe the transformation itself rather than a generic category:

  • cents_to_dollars rather than convert_amount
  • extract_domain_from_email rather than parse_email
  • calculate_days_since rather than date_diff_helper

The name should answer “what does this do?” without requiring you to open the file. convert_amount could convert dollars to euros, cents to dollars, gross to net, or a dozen other things. cents_to_dollars does exactly one thing and its name says what.

Resist the urge to name things generically because you think it’ll be reused more broadly. A macro named convert_amount that does cents-to-dollars work is just a macro with a lying name. If you later need a different kind of amount conversion, write a new macro with a specific name.

Underscore Prefix for Internal Helpers

When a macro is a helper called by other macros — not intended for direct use in models — prefix it with an underscore:

{% macro _build_join_condition(columns) %}
{# Internal helper, not for direct use in models #}
{% for col in columns %}
source.{{ col }} = target.{{ col }}
{% if not loop.last %} AND {% endif %}
{% endfor %}
{% endmacro %}
{% macro generate_merge_statement(source, target, columns) %}
{# Public macro that uses _build_join_condition internally #}
MERGE {{ target }} AS target
USING {{ source }} AS source
ON {{ _build_join_condition(columns) }}
...
{% endmacro %}

The underscore signals “don’t call this directly.” Your team can use generate_merge_statement without understanding the internals of how join conditions get assembled. They don’t need to know _build_join_condition exists.

This is especially valuable in larger projects where the macros/ folder has dozens of files. The underscore prefix makes the public API of your macro library immediately visible — it’s every file that doesn’t start with underscore.

One Macro Per File, Filename Matches Macro Name

This is one of the most practical conventions you can adopt, and it comes from the Brooklyn Data style guide.

When you need cents_to_dollars, you know it lives in cents_to_dollars.sql. No hunting through a utils.sql file. No scrolling through a 500-line file with twelve unrelated macros. Just open the file named after the macro.

This convention interacts well with the underscore prefix. Your macros/utils/ folder might look like:

utils/
├── cents_to_dollars.sql
├── limit_data_in_dev.sql
├── add_audit_columns.sql
└── _build_where_clause.sql # internal helper

The internal helper sits alongside the public macros but its underscore prefix makes its status clear. When someone new joins the team and browses macros/utils/, they immediately see what’s public (cents_to_dollars, limit_data_in_dev, add_audit_columns) and what’s internal (_build_where_clause).

The alternative — grouping multiple macros in one file — creates several problems: fuzzy-find in your editor shows the wrong filename, the convention breaks down when macros move between files, and it’s tempting to keep adding macros to an existing file instead of thinking carefully about where they belong.

What to Avoid

Generic helper names. utils, helper, misc are not names. If a macro goes in utils/, its individual filename still needs a specific name.

Verb-free transformation names. timestamp_format, currency_converter, email_parser — these read like nouns. Use format_timestamp, convert_currency, extract_domain_from_email. Verbs communicate action.

Abbreviations. calc_mrr, gen_sk, fmt_ts — save a few characters, lose all clarity. The compiled SQL is going to run on a warehouse that doesn’t care about filename length. Spell it out.

Version numbers in names. If you find yourself writing cents_to_dollars_v2, you’ve hit a breaking change scenario that should use the deprecation pattern, not a versioned name in production.

The Connection to Documentation

Naming and documentation work together. A well-named macro needs less documentation because the name communicates purpose. A poorly-named macro requires long descriptions to compensate. Even good names don’t replace a _macros.yml entry with usage examples — see dbt Macro Documentation YAML for documentation patterns that complement naming rather than repeat it.