Data Engineering  /  dbt

๐Ÿ”„ dbt โ€” Data Build Tool 60 guides ยท updated 2026

Analytics engineering with SQL โ€” models, tests, sources, and Jinja macros that turn raw warehouse tables into trustworthy, documented data products.

dbt Packages: Importing Reusable Modules Into Your Project

Somewhere between โ€œwrite a macro for itโ€ and โ€œsurely someone has already solved thisโ€ lies a genuinely large ecosystem of dbt packages โ€” pre-built collections of macros, models, and tests that you can install into your own project the way youโ€™d install a library in any other language. Before writing a generic-purpose macro from scratch, itโ€™s worth checking whether a package already does it, tested against thousands of other projects.


What a Package Actually Is

A dbt package is just another dbt project, structured so it can be imported into yours. It typically contributes macros, and sometimes models or tests, that become available in your project as if youโ€™d written them yourself.


Installing a Package

Packages are declared in a packages.yml file at your project root, then installed with a dedicated command.

packages.yml
packages:
- package: dbt-labs/dbt_utils
version: [">=1.1.0", "<2.0.0"]
- package: calogica/dbt_expectations
version: [">=0.10.0", "<0.11.0"]
Terminal window
dbt deps

dbt deps downloads each package into a dbt_packages/ directory locally. That directory should be gitignored โ€” packages are a dependency, not something you commit to your own repo, the same way you wouldnโ€™t commit node_modules.

.gitignore
dbt_packages/

Using Macros From an Installed Package

Once installed, a packageโ€™s macros are called with the package name as a namespace prefix.

select
{{ dbt_utils.generate_surrogate_key(['customer_id', 'order_date']) }} as order_key,
customer_id,
order_date,
amount
from {{ ref('stg_orders') }}

This calls generate_surrogate_key from the dbt_utils package exactly like calling any macro youโ€™d written yourself โ€” the only difference is the dbt_utils. prefix identifying which package it comes from.


Version Pinning and Why It Matters

The version range in packages.yml isnโ€™t a formality โ€” packages do introduce breaking changes between major versions, and an unpinned or overly loose range means a dbt deps run six months from now could silently pull in a version that changes macro behavior underneath you.

packages:
# Loose โ€” risky, could pull in breaking changes unexpectedly
- package: dbt-labs/dbt_utils
version: ">=1.0.0"
# Pinned to a compatible range โ€” the safer default
- package: dbt-labs/dbt_utils
version: [">=1.1.0", "<2.0.0"]

Treat package versions the same way youโ€™d treat dependency versions in any other software project โ€” pin them, and upgrade deliberately with a changelog review, not implicitly on every dbt deps run.


Installing From Git, Not Just the Package Hub

Not every useful package is published to dbtโ€™s public Package Hub. Private, internal, or unpublished packages can be installed directly from a Git repository.

packages:
- git: "https://github.com/your-org/internal-dbt-macros.git"
revision: v2.3.0

Pinning to a specific tag or commit (revision) matters even more here than with published packages, since thereโ€™s no registry enforcing semantic versioning discipline on an internal repo.


Building an Internal Package for Your Own Organization

Once a set of macros is genuinely reusable across multiple dbt projects within your company โ€” not just multiple models within one project โ€” itโ€™s worth extracting them into their own internal package rather than copy-pasting macro files between repos.

internal-dbt-macros/
dbt_project.yml
macros/
generate_surrogate_key.sql
standard_grants.sql
custom_tests/
not_null_proportion.sql

Any other project in the organization can then install this exactly like a public package, keeping shared conventions in one maintained place instead of drifting apart across teamsโ€™ individual projects.


Packages vs. Writing Your Own Macro

The decision isnโ€™t always obvious. A useful heuristic:

SituationRecommended approach
Common, generic need (surrogate keys, date spines, pivots)Check dbt_utils first
Data quality testing beyond dbtโ€™s built-in generic testsCheck dbt_expectations or dbt_utils test macros
Business-logic-specific, unlikely to be reusable elsewhereWrite your own macro
Reused across multiple internal projectsExtract into an internal package

Writing a macro that duplicates something dbt_utils already provides, tested and maintained by a large community, is rarely a good use of time compared to just installing the package.


Upgrading a Package Deliberately

When a genuinely new package version is worth adopting โ€” a bug fix, a new macro you actually need โ€” the safe upgrade path is a dedicated branch, not a routine dbt deps that happens to pull a newer version silently.

# Before
packages:
- package: dbt-labs/dbt_utils
version: [">=1.1.0", "<1.2.0"]
# After, on its own branch, reviewed like any other dependency bump
packages:
- package: dbt-labs/dbt_utils
version: [">=1.2.0", "<1.3.0"]
Terminal window
dbt deps
dbt build

Running the full build after a version bump, before merging, catches any breaking macro signature changes immediately โ€” treating this exactly like upgrading any other software dependency, rather than assuming package updates are always safe by default.

Common Mistakes

Committing dbt_packages/ to version control. This bloats your repo with third-party code that should be fetched fresh via dbt deps, not tracked โ€” always gitignore it.

Forgetting to run dbt deps after adding a package. Editing packages.yml alone doesnโ€™t install anything; it just declares intent. dbt deps is a separate, required step, and CI pipelines need to run it before any dbt run or dbt test.

Unpinned version ranges causing silent breakage. A wide-open version constraint means a routine dbt deps months later can pull in a major version bump with different macro signatures, breaking models that were working fine yesterday.

Summary

ElementPurpose
packages.ymlDeclares which packages your project depends on
dbt depsDownloads declared packages into dbt_packages/
package_name.macro_name()Calls a macro from an installed package
Version pinningPrevents silent breaking changes on future installs

Packages are how the dbt ecosystem avoids everyone reinventing the same surrogate-key macro. Before writing something generic from scratch, check whether dbt_utils โ€” covered next in dbt_utils Package โ€” already solves it.