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

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.

References