On a branch with two changed models there is no reason to rebuild the whole project. dbt can compare
your project with the manifest of another version, usually production, and select only the nodes that
differ. With + it adds everything downstream, and with --defer it reads the unchanged parents from
production instead of building them.
Copy and paste
1. Save the manifest of production. Take it from the last production run, or create it from the main branch:
dbt parse --target prod
mkdir -p prod-state && cp target/manifest.json prod-state/
2. On your branch, preview and build the changes:
dbt ls -s state:modified+ --state prod-state --resource-type model
dbt build -s state:modified+ --state prod-state --defer
What counts as modified
After changing stg_orders and adding a new model, new_model, to the example project:
| Selector | Selects |
|---|---|
state:new |
new_model |
state:modified |
new_model, stg_orders |
state:modified+ |
the two above, plus every model that depends on stg_orders: customer_orders, fct_orders, orders_daily, revenue_by_country, shipped_revenue, int_orders_shipped |
- Any change to the file counts. dbt compares the code, so an added comment or a changed space makes a model modified as well.
- New nodes are included.
state:modifiedcontainsstate:new. - Configuration and properties count too. A new materialization, tag or column description in a
YAML file selects the model. To be more precise, use a sub-selector such as
state:modified.body(only the SQL) orstate:modified.configs(only the configuration). - Deleted models are not selected. There is nothing to build for them.
Why the manifest must come from production
--defer makes every ref() to a model that is neither selected nor built in your schema point to the
relation recorded in the state manifest. That is what lets customer_orders build in an empty
development schema: stg_customers is read from production.
The relations come from the manifest, so it has to be one produced with the production target. A
manifest from your own development run would defer to your own schema. --favor-state makes dbt use
the production relation even when your schema has its own copy of the model.
In CI
A pull request job usually downloads the production manifest saved by the last deployment and runs:
dbt build -s state:modified+ --state prod-state --defer --target ci
The job builds the changed models and their children in a temporary schema, runs their tests and reads everything else from production.
Tested with dbt Core 1.12.