The biggest change in dbt-utils history happened with v1.0. Every cross-database macro — dates, strings, type casting, aggregations, set operations — was removed from dbt_utils and moved to the dbt namespace in dbt-core. If your project still calls dbt_utils.datediff(), you’re calling a path that no longer exists in v1.x.
This isn’t an academic concern. A codebase migrating from dbt-utils 0.x to 1.x will break on first run without updating these calls.
What Moved to dbt-core
All of these moved to dbt.* and no longer exist in dbt_utils.*:
Date and time:
dateadd, datediff, date_trunc, last_day, current_timestamp
String:
concat, length, position, replace, right, split_part, string_literal, escape_single_quotes
Type casting:
safe_cast, cast (dbt v1.8+), type_bigint, type_float, type_int, type_numeric, type_string, type_timestamp
Aggregation:
any_value, bool_or, listagg
Set operations:
except, intersect
Other:
hash, cast_bool_to_text, array_append, array_concat, array_construct (dbt-core 1.3+), date (dbt v1.8+)
The full reference for these is in dbt Built-In Cross-Database Macros.
What Was Removed Entirely
A handful of dbt-utils macros were removed without a replacement in dbt-core — they either became obsolete or were absorbed into native dbt functionality:
surrogate_key()→ replaced bygenerate_surrogate_key(). These are not drop-in compatible: null handling changed. See dbt-utils generate_surrogate_key for the gotcha.unique_whereandnot_null_wheretests → the nativewhereconfig on built-inuniqueandnot_nulltests handles this now. Useconfig.whereinstead.insert_by_periodmaterialization → moved to the experimental-features repository. Not part of dbt-utils anymore.conditionargument onexpression_is_true→ replaced by the nativewhereconfig on the test. Any test YAML usingcondition:needs updating.
How to Migrate
Step 1: Update dbt-utils to v1.x in your packages.yml.
packages: - package: dbt-labs/dbt_utils version: [">=1.0.0", "<2.0.0"]Step 2: Find all dbt_utils cross-database macro calls.
Search your project for dbt_utils.dateadd, dbt_utils.datediff, dbt_utils.date_trunc, dbt_utils.concat, dbt_utils.safe_cast, and the rest of the moved list. A global find-and-replace handles most cases. The function signatures are identical — only the namespace changes.
Before: {{ dbt_utils.dateadd('day', 7, 'order_date') }}After: {{ dbt.dateadd('day', 7, 'order_date') }}Step 3: Handle surrogate_key() if you used it.
If your project used the old surrogate_key() macro and you’re upgrading, add this to dbt_project.yml first to preserve backward-compatible null handling:
vars: surrogate_key_treat_nulls_as_empty_strings: trueWithout this, null values hash differently in generate_surrogate_key(), which changes surrogate key values for rows with nulls. This breaks incremental models and snapshots that used the old key. See dbt-utils generate_surrogate_key for the full explanation.
Step 4: Update any tests using condition: argument.
If you have expression_is_true tests with a condition: argument, replace with the native where config:
# Before (v0.x)- dbt_utils.expression_is_true: expression: "invoice__total >= invoice__tax" condition: "status != 'voided'"
# After (v1.x)- dbt_utils.expression_is_true: expression: "invoice__total >= invoice__tax" config: where: "status != 'voided'"Step 5: Update deduplicate calls if you used group_by.
The group_by argument was renamed to partition_by in v1.0. The relation_alias argument was removed. And order_by is now mandatory — pass 1 if you don’t care about ordering.
What Stayed in dbt-utils
After the migration, dbt-utils still owns: SQL generators (date_spine, deduplicate, star, union_relations, generate_surrogate_key, pivot, unpivot), introspective macros (get_column_values, get_relations_by_pattern, get_query_results_as_dict), generic tests, and web macros. See the dbt-utils Hub for the full current scope.
Why This Migration Happened
The cross-database macros lived in dbt-utils because when they were written, dbt-core had no standard mechanism for adapter-specific SQL. Once dbt-core built out adapter.dispatch() as a first-class feature, it made sense to move the universal functions into core where every project can use them without installing a package.
From a maintenance perspective, it also means cross-database function fixes now land in dbt-core releases, which ship more frequently and with broader testing coverage than any community package.
If you’re on a non-standard adapter like Databricks, you may still need spark_utils in your dispatch configuration for macros that dbt-core doesn’t cover. See dbt Dispatch Configuration for how to wire that up.