Articles
dbt State Selection Interview Questions: Defer, Changed Models, and Safe CI
Practise dbt state selection interviews with manifest baselines, defer resolution, stale CI parents, cross-environment tests, and separate freshness checks.
A dbt pull request changes one revenue model. CI finishes quickly, tests pass, and nobody notices that the query read an old table left in the CI schema. The selector was correct; the environment was not. That is the distinction to practise for dbt state selection interview questions: identify what changed, then prove which relations supply the data.
This guide covers classic manifest-based local state and Slim CI. Official facts come from dbt documentation checked on October 1, 2026; verify the engine, adapter, and version used in your interview. Practice scenarios and recommendations below are original reasoning exercises. They are not candidate reports, leaked questions, or claims about any employer's process. Managed dbt State features can have different setup and behavior.
What should a strong answer establish first?
Here, explain three decisions before giving a command: the comparison baseline, the selected graph, and the relations used by unselected parents. A manifest describes project resources; it is not a backup of warehouse rows. Officially, even a partial invocation produces a manifest containing the project's enabled resources, while execution-specific properties can be present only for executed nodes. A manifest's existence therefore does not establish that every relation was successfully built. dbt manifest reference
Recommended interview approach: ask who produced the artifact, from which commit, against which environment, and after what successful job. Then draw the dependency graph. This makes assumptions visible before a small command hides them.
For this original exercise, use four materialized models:
raw.orders → stg_orders → int_orders → fct_revenue
↑
dim_customers
Only int_orders changes. Assume it references both parents with ref(), all names remain stable, and no macro, config, or test changes independently expand selection. Production has every relation. The fresh CI schema initially has none. These constraints matter: a different graph or materialization changes the prediction.
Which models does state:modified+ select?
Official fact: state:modified includes new nodes and changed existing nodes. It compares the current project with the manifest supplied through --state. It is broader than a Git filename diff. Node selector methods
The trailing + adds descendants; a leading + adds ancestors. @ also brings in ancestors of descendants, which can make it a substantially wider selection. Graph operators
In our model-only exercise, state:modified+ selects int_orders and fct_revenue. It does not select stg_orders or dim_customers just because they are necessary inputs. That is why defer is useful: you can build the changed downstream slice without rebuilding unchanged upstream tables.
A good follow-up answer distinguishes selected resources from executed resources. Exposures can describe downstream consumers without becoming warehouse tables built by the command. Tests also have selection rules of their own. Do not promise that every node drawn below a changed model will execute as SQL.
Preview the actual project rather than trusting the simplified drawing:
dbt ls --select 'state:modified+' \
--state artifacts/prod-baseline \
--target ci --target-path target/ci-list \
--output json
Officially, dbt ls uses target-specific profile information but does not connect to the database or execute queries. Treat its output as evidence about selection, not proof that a referenced table exists or contains the right data. dbt list reference
Save the node list with the commit and artifact identity. If a shared macro changes, expect a wider affected set; inspect it before deciding the selector is broken.
When does defer use production instead of CI?
Official fact: with deferral enabled, an unselected, non-ephemeral node can resolve through the state manifest when its relation is absent from the current target. --favor-state gives the state definition precedence for unselected nodes even when a local relation exists. Selected nodes stay in the current target; ephemeral models are not deferred. Defer reference
Use this decision matrix to predict the exercise. “State relation” means the relation named by the supplied artifact, which must actually be usable.
| Referenced node | CI relation exists? | favor-state? | Expected resolution in this exercise |
|---|---|---|---|
| Selected materialized model | Either | Either | Current CI target |
| Unselected materialized parent | No | No | State relation |
| Unselected materialized parent | Yes | No | Existing CI relation |
| Unselected materialized parent | Yes | Yes | State relation |
| Ephemeral parent | Not applicable | Either | Compiled into downstream SQL |
Now introduce the failure: an earlier job left ci_pr_42.dim_customers with only customers from last month. The next run changes int_orders and reads new orders from production. Without a policy that removes or overrides the old parent, the CI calculation can omit new customers or produce null attributes. Adding --defer alone does not establish the intended input cohort.
Inference from the rules: isolated schemas and intentional artifact precedence reduce this ambiguity. Neither guarantees correctness by itself. A production parent may also be stale, or the new model may require a column that production has not deployed. Inspect compiled relation names and check schema compatibility before interpreting a test result.
How are state and defer-state different?
The comparison baseline answers “What changed relative to this project definition?” The deferral artifact answers “Which existing relations should unselected references use?” These are separate questions, even when one production manifest answers both.
Official dbt documentation supports separate --state and --defer-state paths. If the latter is omitted, deferral uses the state artifact. Separate state for comparison and deferral
Consider an original release exercise. Manifest A represents the accepted comparison baseline. Manifest B represents the warehouse deployment whose upstream relations you want to read. Comparing against A while deferring to B can be deliberate. It can also be dangerous if B's tables do not support the changes being tested. Explain why the artifacts differ and how their compatibility was checked; do not treat two paths as automatically safer than one.
A parse-only artifact can describe a proposed relation without creating it. Likewise, an artifact from a failed deployment may name a mixture of intended and successfully applied resources. Recommended practice: bind the deferral artifact to a known deployment and inspect the corresponding run results and warehouse state. Record the deployment identity rather than relying on a mutable folder called latest.
The interviewer can now ask a sharper question: “If the same commit is deployed twice with different target schemas, which manifest would you use?” Answer with the intended relation namespace and deployment, not just the Git SHA.
What makes a Slim CI command safe to review?
For the original DAG, assume a trusted production manifest, an isolated writable CI schema, and read-only access to the production inputs. This is a suggested workflow, not a universal production configuration:
dbt build --select 'state:modified+' \
--state artifacts/prod-baseline \
--defer --favor-state \
--target ci --target-path target/ci-build
If the applied relation artifact intentionally differs, add --defer-state artifacts/prod-applied. The profile and schema-generation logic must actually route selected writes into CI; naming a target ci is not a permission boundary.
Keep downloaded baseline artifacts outside current output folders. Officially, parsing can overwrite manifest.json; using the same directory for --state and --target-path undermines the comparison. State comparison caveats
The official build command combines supported resources and tests in dependency order. Test failures can affect downstream execution, so inspect statuses rather than counting selected names as completed builds. dbt build reference
For this exercise, preserve four pieces of evidence: the selector output, compiled SQL relation names, invocation results, and the fixture's expected revenue. Suppose the fixture contains two valid orders worth 30 and 70. Both customers are present in the intended parent. The expected total is 100. An observed 30 is a useful clue, but it does not uniquely identify stale deferral: filters, joins, or aggregation errors could also remove the second order.
Change one factor at a time. First inspect where dim_customers resolved. Then compare its keys with the intended cohort. Finally rerun with the corrected input policy. These steps turn “CI passed incorrectly” into a testable explanation.
Why can a relationships test cross environments?
An original test scenario compares CI orders against a production customer dimension. Order 200 references customer 9, created in a CI fixture but absent from production. A relationships test reports an orphan. The query may be behaving correctly against the two relations it received while failing to represent the cohort you intended to test.
Official test selection supports indirect modes including eager, cautious, buildable, and empty. Their treatment of tests with multiple parents differs; preview the selected tests under the mode you actually use. Test selection examples
Recommended response: identify every test parent and its resolved environment before weakening the assertion. For a coherent integration test, build the required parent slice in CI or use an intentionally consistent fixture. A cross-environment test can be useful when the requirement really is “new output must reference deployed production keys.” State that requirement explicitly.
Do not automatically exclude all relationships tests to make the job green. That can remove the only check detecting a real foreign-key defect. If a test is excluded for a documented cohort mismatch, retain equivalent coverage elsewhere and explain the remaining blind spot.
Does no modified model mean the data is fresh?
No. Imagine that production receives 5,000 new orders overnight but nobody edits the project. A state-based code selector can select nothing while the scheduled transformation still needs to process those rows. This is a difference between code deployment and data processing.
Official caveats explicitly distinguish project changes from changed warehouse data. They also warn about variable-driven lineage and explain why changes to tags or meta do not count as modifications. State comparison limitations
Freshness needs its own configured check. Version matters: the current dbt v2 documentation introduces top-level dbt freshness for sources and models, while dbt source freshness remains a legacy source-only command. Match commands and artifacts to the installed version. dbt freshness and source commands
For the exercise, distinguish three results: no code nodes selected, upstream data within its freshness threshold, and revenue successfully rebuilt through the required watermark. Any one can be true while another is false. A practical answer names the scheduled build policy and the data checks separately.