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 columnget_latest_partition— returns the most recent partition date
generate_ for macros that create SQL structures:
generate_surrogate_key— produces a hashed key columngenerate_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 patternformat_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_dollarsrather thanconvert_amountextract_domain_from_emailrather thanparse_emailcalculate_days_sincerather thandate_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 helperThe 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.