Module 4 — Run, build, and state#
Gate: the project actually materializes, and state-based selection behaves.
No code changes in this module.
Build the whole project on dbt Core v2#
Finished 'build' successfully for target 'dev' [26.4s]
Processed: 23 models | 59 tests | 1 snapshot | 12 seeds
Summary: 95 total | 92 success | 3 no-op
The same build on dbt Core takes roughly 90 seconds. Note also that static analysis applies to tests, seeds and snapshots — not just models.
Custom materializations#
fct_regulated_potion_sales uses a house materialization, audit_table, which
builds the relation and then stamps a row into a build ledger for Compliance:
It works on both engines. It carries +static_analysis: 'off', which — as you
found in Module 3 — is not actually required here. Worth flagging in your own
project: inherited opt-outs are worth re-testing rather than assuming.
Dynamic materialization#
fct_orders_backfill picks its materialization from a variable:
{% set run_mode = var('PIPELINE_RUN_MODE', 'standard') %}
{{ config(materialized = 'ephemeral' if run_mode == 'backfill' else 'table') }}
Under the default standard it is a table. Under backfill it becomes
ephemeral, and agg_revenue_by_region — which refs it — inlines it as a CTE
instead of reading a table. The shape of the graph changes based on a variable.
Succeeded [ 2.06s] model dbt_trouze.agg_revenue_by_region (table)
Finished 'build' successfully for target 'dev'
This one works, and it is still a liability
Guidance you may have read describes conditional-ephemeral materialization
breaking the upgrade. On dbt-fusion 2.0.0-preview.218 it compiles and
builds cleanly in both engines.
The reason to fix it anyway is that dbt Core v2 builds a stable logical plan ahead of time, and this model has two different plans depending on an environment variable. Anything that reasons about your graph — state comparison, deferral, column lineage, the IDE — is reasoning about whichever plan it happened to be handed. Two materializations behind one name is ambiguity you are choosing to keep.
State comparison#
Save a dbt Core manifest as your production artifact, then ask dbt Core v2 what changed:
dbt parse --no-partial-parse # dbt Core
mkdir -p state && cp target/manifest.json state/manifest.json
dbt list --select state:modified --state state # dbt Core v2
With identical code, nothing is reported. Now touch a model and re-run:
merlinco_apothecaries.marts.dim_potions
merlinco_apothecaries.marts.not_null_dim_potions_potion_sku
merlinco_apothecaries.marts.unique_dim_potions_potion_sku
dbt Core reports exactly the same three nodes. Cross-engine state comparison agrees on models — no spurious modifications.
Takeaways#
- Static analysis covers tests, seeds and snapshots, not just models.
- Custom materializations work; re-test inherited
static_analysisopt-outs. - Dynamic materialization builds fine and is still worth removing — one name should mean one plan.
- Cross-engine state comparison agrees on models.