In this post we are going to cover governance, performance and dbt v2, through 7 real dbt interview questions for data engineers, asked the way they come up on a real project. The last one is a scenario: the dbt project has grown to two thousand models across many teams, and you have to keep it fast and governed.
Governance is who owns which models and who is allowed to build on them. Performance is finding the slow models with facts, not guesses. And dbt v2 is the current major version of dbt, with a new engine and a stricter parser. Interviewers ask about these because they are the problems of a project that has grown up: many teams, many models, and an upgrade to plan.
Part 9 of the Real Time dbt Interview Questions and Answers series on TechBrothersIT.
- The short answer
- Let's create the project and some sample data
- Question 1: Another team builds on your internal models and breaks when you refactor them. How do access levels and groups help?
- Question 2: You must change a widely used model without breaking its consumers. How do model versions work?
- Question 3: The dbt run is slow and you do not know which model is the problem. How do you find the slow models?
- Question 4: A teammate asks what changed in dbt v2 and whether to upgrade. What do you tell them?
- Question 5: Your manager asks why the upgrade to dbt v2 fails on YAML that used to work. How do you prepare a project for v2?
- Question 6: Analysts want lineage from source to dashboard. How do exposures, docs and lineage help?
- Scenario 7: The dbt project has grown to two thousand models across many teams. How do you keep it fast and governed?
- The full script
The short answer
Put models in groups with owners, keep helpers private and make a few models public, with contracts and versions. Find slow models from run_results.json and the warehouse, not by guessing. dbt v2 is GA and strict: clean the YAML with the v2 parser and dbt-autofix before you switch, and add exposures so lineage reaches the dashboards.
Let's create the project and some sample data
Our project starts small: two seeds, raw_orders with five orders and raw_customers with three customers. Models are views by default, and the finance folder builds tables. We add the models question by question. Here are the starting files.
name: shop
version: '1.0'
profile: shop
models:
shop:
+materialized: view
finance:
+materialized: tableorder_id,customer_id,amount,status,order_date
1,10,120.00,completed,2026-10-01
2,11,80.00,completed,2026-10-01
3,10,45.50,refunded,2026-10-02
4,12,200.00,completed,2026-10-03
5,11,60.00,completed,2026-10-03customer_id,name,country
10,Ali Khan,US
11,Sara Lee,UK
12,John Smith,USQuestion 1: Another team builds on your internal models and breaks when you refactor them. How do access levels and groups help?
Put models in groups with an owner, keep the helpers private, and make only the stable models public. Then dbt itself stops other teams from building on your internals.
By default every model is protected, so any model in the project can ref it, and your helper models quietly become another team's dependency. The fix starts with groups. We add two in groups.yml, finance and marketing, each with an owner. Then each model gets an access level: the helper int_revenue is private to the finance group, and fct_revenue, the model we promise to keep stable, is public. Now a marketing model refs the private helper. Let's run dbt build.
groups:
- name: finance
owner:
name: Finance Data Team
email: finance-data@example.com
- name: marketing
owner:
name: Marketing Analytics
email: marketing@example.comselect order_id, customer_id, amount
from {{ ref('raw_orders') }}
where status = 'completed'select customer_id, sum(amount) as revenue
from {{ ref('int_revenue') }}
group by customer_idmodels:
- name: int_revenue
config:
group: finance
access: private
- name: fct_revenue
config:
group: finance
access: publicselect customer_id, amount
from {{ ref('int_revenue') }}
where amount > 100models:
- name: mkt_top_customers
config:
group: marketing$ dbt build
=================== Errors and Warnings ====================
[error] [AccessDenied (dbt1066)]: Node 'model.shop.mkt_top_customers' attempted to reference node
'model.shop.int_revenue', which is not allowed because the referenced node is private to the
'finance' group
--> models/marketing/mkt_top_customers.sql:2:6
Finished 'build' with 1 error for target 'dev'dbt v2 stops with AccessDenied: the referenced node is private to the 'finance' group. dbt v1 fails the parse too, so nothing gets built. The fix is to ref the public model instead, and build with dbt build -s +mkt_top_customers.
select customer_id, revenue
from {{ ref('fct_revenue') }}
where revenue > 100$ dbt build -s +mkt_top_customers
Succeeded seed main.raw_orders (table)
Succeeded model main.int_revenue (table)
Succeeded model main.fct_revenue (table)
Succeeded model main.mkt_top_customers (view)
Finished 'build' successfully for target 'dev'
Processed: 3 models | 1 seed
Summary: 4 total | 4 successThe build succeeds, 4 total | 4 success, and finance can refactor the helper without breaking anyone. The three access levels are:
- private: same group only
- protected: the default, same project
- public: any group, package or project
Keep the public list short, and every public model gets a contract.
Question 2: You must change a widely used model without breaking its consumers. How do model versions work?
Versions let a public model change its shape while its consumers move at their own pace. Each version is its own model, with its own contract, and the old one gets a deprecation date.
Here dim_customers renames name to customer_name. Each version is its own file: dim_customers_v1 and dim_customers_v2. The YAML lists both, with latest_version: 2. Version 2 includes all the columns except name, and adds customer_name. The contract enforces each version's shape, and version 1 gets deprecation_date: 2027-03-31. A marketing consumer pins version 1. Let's build them.
select customer_id, name
from {{ ref('raw_customers') }}select customer_id, name as customer_name
from {{ ref('raw_customers') }}models:
- name: dim_customers
latest_version: 2
config:
group: finance
access: public
contract: {enforced: true}
latest_version_pointer:
enabled: true
columns:
- {name: customer_id, data_type: integer}
- {name: name, data_type: varchar}
versions:
- v: 1
deprecation_date: 2027-03-31
- v: 2
columns:
- {include: all, exclude: [name]}
- {name: customer_name, data_type: varchar}select * from {{ ref('dim_customers', v=1) }}$ dbt build -s +dim_customers mkt_customers
Succeeded seed main.raw_customers (table)
Succeeded model main.dim_customers_v1 (table)
Succeeded model main.dim_customers_v2 (table)
Succeeded model main.mkt_customers (view)
=================== Errors and Warnings ====================
[warning] [UpcomingReferenceDeprecation (dbt1073)]: While compiling 'mkt_customers': Found a reference
to dim_customers.v1, which is slated for deprecation on '2027-03-31T00:00:00+00:00'. A new version of
'dim_customers' is available. Try it out: {{ ref('shop', 'dim_customers', v='2') }}.
Finished 'build' with 1 warning for target 'dev'
Processed: 3 models | 1 seed
Summary: 4 total | 4 successEverything builds, but dbt v2 warns UpcomingReferenceDeprecation: version 1 is slated for deprecation on 2027-03-31, and it prints the exact ref to migrate to, ref('shop', 'dim_customers', v='2').
Now the consumer moves to a plain ref. Let's compile a plain ref with dbt compile --inline and check which relations exist.
select * from {{ ref('dim_customers') }}$ dbt compile --inline "select * from {{ ref('dim_customers') }}"
Compiled inline node is:
select * from "dev"."main"."dim_customers_v2"
Finished 'compile' successfully for target 'dev'
Processed: 1 model
Summary: 1 total | 1 successselect table_name, table_type from information_schema.tables where table_name like 'dim_customers%' order by 1| table_name | table_type |
|---|---|
| dim_customers | VIEW |
| dim_customers_v1 | BASE TABLE |
| dim_customers_v2 | BASE TABLE |
A ref without a version resolves to the latest, "dev"."main"."dim_customers_v2", so the warning is gone. The table check shows dim_customers as a VIEW next to the two BASE TABLE versions. That is latest_version_pointer, new in v1.12: a view named after the model, on the latest version, and dbt v2 built it.
Ship version 2 as a prerelease first, with latest_version still 1, because unpinned refs switch at once. Delete version 1 after its deprecation date.
Question 3: The dbt run is slow and you do not know which model is the problem. How do you find the slow models?
Start with facts, not guesses: run_results.json has the execution time of every node. Then confirm in the warehouse before you rewrite anything.
Every run writes target/run_results.json, with one result per node:
- unique_id:
model.shop.int_order_events - status:
success - execution_time: seconds
- timing: compile and execute, start and end
- relation_name, adapter_response
Let's build a chain of two models. The suspect is int_order_events, which cross joins every order to thirty million rows. We also add a small query, scripts/slowest_models.sql, that reads run_results.json and sorts the models by execution time. Let's run dbt run -s +fct_order_events and then the query.
{{ config(materialized='table') }}
-- every order cross joined to a big range: the slow one
select o.order_id, count(*) as events
from {{ ref('raw_orders') }} as o
cross join range(30000000) as e(i)
where hash(o.order_id, e.i) % 7 = 0
group by o.order_id{{ config(materialized='table') }}
select count(*) as orders, sum(events) as events
from {{ ref('int_order_events') }}-- rank the models of the last run by execution time
select
r ->> 'unique_id' as unique_id,
r ->> 'status' as status,
json_extract_string(r, '$.timing[*].name') as timing
from (
select unnest(results) as r
from read_json('target/run_results.json')
)
order by (r ->> 'execution_time')::double desc
limit 1$ dbt run -s +fct_order_events
Succeeded model main.int_order_events (table)
Succeeded model main.fct_order_events (table)
Finished 'run' successfully for target 'dev'
Processed: 2 models
Summary: 2 total | 2 success-- rank the models of the last run by execution time
select
r ->> 'unique_id' as unique_id,
r ->> 'status' as status,
json_extract_string(r, '$.timing[*].name') as timing
from (
select unnest(results) as r
from read_json('target/run_results.json')
)
order by (r ->> 'execution_time')::double desc
limit 1| unique_id | status | timing |
|---|---|---|
| model.shop.int_order_events | success | ['compile', 'execute'] |
Times change on every run, so we don't quote them. The query ranks the models, and the slowest one is model.shop.int_order_events, with status success and a timing list of ['compile', 'execute'].
Then fix the cause:
- Big tables: incremental or microbatch
- Filter early: before the joins
- No deep stacks of views
- dbt v2 traces:
--otel-parquet-file-namewrites OpenTelemetry traces as Parquet, for a timeline of every node
Execution time is wall clock time, so on a busy warehouse it includes queueing. Confirm it in the warehouse query history, and read the query plan before you rewrite anything.
Question 4: A teammate asks what changed in dbt v2 and whether to upgrade. What do you tell them?
dbt v2 is the current major version, GA since September 2026: a new engine written in Rust, formerly called the Fusion engine. The language is the same, but v2 is strict.
Let's go through what changed:
| dbt v1 (1.12) | dbt v2 | |
|---|---|---|
| ENGINE | Python: dbt-core plus an adapter | Rust: one binary, adapters bundled |
| UNKNOWN YAML KEYS | a warning, or nothing | a parse error |
| DBT BUILD | unit tests in lineage order | all unit tests first (per the v2 docs) |
| FRESHNESS | sources: dbt source freshness | sources and models: dbt freshness |
| DISTRIBUTIONS | dbt-core, Apache 2.0 | dbt (free license, lint, LSP) or dbt OSS (Apache 2.0) |
First, the engine. You install one binary, with the adapters bundled. Then the parser: YAML that v1 ignored, or only warned about, is now a parse error. The v2 docs say dbt build runs unit tests first. And freshness covers models too, with the dbt freshness command.
There are two distributions. dbt is free under the dbt license, and adds SQL comprehension, dbt lint and the language server. dbt OSS is the Apache 2.0 build, and it is the one that runs every command in this series.
On speed, dbt Labs says "faster parse and compile times, especially on large projects." Measure your own project before you promise a number.
So, should you upgrade?
- New project: start on dbt v2, recommended for all new projects
- Existing project: v1 is still fully supported, so upgrade when your adapter is supported, the v2 parse is clean, and your packages are compatible
Never quote a speedup multiplier. Use dbt Labs' own words, and say you would measure it on your project.
Question 5: Your manager asks why the upgrade to dbt v2 fails on YAML that used to work. How do you prepare a project for v2?
Because v1 was forgiving and v2 is strict. You prepare on v1.12 with the v2 parser flag and dbt-autofix, and fix the YAML before you switch.
Let's look at YAML that has worked for years: a typo in the descriptoin key, a custom owner key, and accepted_values with its values at the top level. We run dbt parse on dbt v2.
select order_id, customer_id, amount, status, order_date
from {{ ref('raw_orders') }}models:
- name: stg_orders
descriptoin: One row per order
owner: finance
columns:
- name: status
tests:
- accepted_values:
values: ['completed', 'refunded']$ dbt parse
=================== Errors and Warnings ====================
[error] [UnusedConfigKey (dbt1060)]: While parsing config: Ignored unexpected key `"descriptoin"`. YAML
path: `descriptoin`.
--> models/staging/_stg_orders.yml:3:5
[error] [UnusedConfigKey (dbt1060)]: While parsing config: Ignored unexpected key `"owner"`. YAML path:
`owner`.
--> models/staging/_stg_orders.yml:4:5
[error] [DbtYamlValidationError (dbt1159)]: Deprecated test arguments: ["values"] at top-level
detected. Please migrate to the new format under the 'arguments' field:
https://docs.getdbt.com/reference/deprecations#missingargumentspropertyingenerictestdeprecation.
--> models/staging/_stg_orders.yml:9:23
suggestion: Run 'dbt deps' to see the latest fusion compatible packages. For compatibility errors, try
the autofix script: https://github.com/dbt-labs/dbt-autofix
Finished 'parse' with 3 errors for target 'dev'dbt v2 fails with three errors: two UnusedConfigKey errors for "descriptoin" and "owner", and Deprecated test arguments that must move under arguments. On dbt v1.12 with DuckDB, the same file parses with one deprecation warning for the test arguments, and nothing for the typo or the custom key.
Now the fixed file: description spelled right, owner under config and meta, and data_tests, not the old tests alias, with values under arguments. Let's parse again.
models:
- name: stg_orders
description: One row per order
config:
meta:
owner: finance
columns:
- name: status
data_tests:
- accepted_values:
arguments:
values: ['completed', 'refunded']$ dbt parse
Finished 'parse' successfully for target 'dev'The parse finishes successfully. To prepare a real project, stay on v1.12 and run dbt parse --use-v2-parser. It is beta, and on our project it reports the same three errors. Then dbt-autofix rewrites most deprecations, and its packages command finds v2-compatible versions:
# on dbt v1.12, before you switch to v2
dbt parse --use-v2-parser # beta: v2 parse rules
uvx dbt-autofix deprecations --dry-run
uvx dbt-autofix deprecations # rewrites YAML and SQL
uvx dbt-autofix packages # v2-compatible versions
dbt deps && dbt parse --use-v2-parserAutofix moves unknown keys under meta, so a typo can land in meta instead of becoming a description. Review the diff, and parse again until it's clean.
Question 6: Analysts want lineage from source to dashboard. How do exposures, docs and lineage help?
dbt only knows what it builds, until you add an exposure: who uses your models outside dbt. Then the lineage reaches the dashboard.
Let's add one in exposures.yml: exec_revenue_dashboard, with a type, a URL, an owner, and depends_on, the refs it reads. Now we select the exposure and everything upstream with dbt ls -s +exposure:exec_revenue_dashboard.
exposures:
- name: exec_revenue_dashboard
label: Executive Revenue
type: dashboard
maturity: high
url: https://bi.example.com/dashboards/42
depends_on:
- ref('fct_revenue')
- ref('dim_customers')
owner:
name: Finance Data Team
email: finance-data@example.com$ dbt ls -s +exposure:exec_revenue_dashboard
=======================Selected nodes=======================
shop.exec_revenue_dashboard
shop.finance.dim_customers.v2
shop.finance.fct_revenue
shop.finance.int_revenue
shop.raw_customers
shop.raw_orders
Finished 'list' successfully for target 'dev'dbt lists the dashboard, fct_revenue, the private helper int_revenue, dim_customers.v2, and the two seeds. It works the other way too. Let's select fct_revenue with a plus after it.
$ dbt ls -s fct_revenue+
=======================Selected nodes=======================
shop.exec_revenue_dashboard
shop.finance.fct_revenue
shop.marketing.mkt_top_customers
Finished 'list' successfully for target 'dev'Now we see what a change to fct_revenue reaches: the marketing model mkt_top_customers and the executive dashboard. That is your impact analysis, and your list of people to tell.
Then dbt docs generate turns the same graph into a docs site.
$ dbt docs generate
Running compile; pass `--no-compile` to export the existing index instead.
Succeeded seed main.raw_customers (table)
Succeeded seed main.raw_orders (table)
Succeeded model main.dim_customers_v1 (table)
Succeeded model main.dim_customers_v2 (table)
Succeeded model main.int_order_events (table)
Succeeded model main.int_revenue (table)
Succeeded model main.fct_order_events (table)
Succeeded model main.fct_revenue (table)
Succeeded model main.mkt_customers (view)
Succeeded model main.mkt_top_customers (view)
Generated target (reading info_schema/v1/) — no column lineage; rerun with `--static-analysis strict`
to include it
Host the contents of target on any static file server. Note that this publishes everything else in the
target directory too, including compiled SQL and any stored test failures.
Finished 'docs' successfullyIn dbt v2 it is dbt Docs v2, a static site you can host on any file server, as the output says. It also says there is no column lineage: that needs --static-analysis strict, which comes with the full dbt distribution and needs a dbt login.
Every dashboard that matters gets an exposure with an owner, so impact analysis is one command.
Scenario 7: The dbt project has grown to two thousand models across many teams. How do you keep it fast and governed?
At that size, the problems are ownership and speed. Solve ownership with groups, private by default and a small public interface, and speed with slim CI and building less.
Ownership first. Each domain is a group with an owner, private by default. Here dbt_project.yml makes the finance folder private, and the YAML keeps a few models public. Let's list the interface with dbt ls -s access:public.
name: shop
version: '1.0'
profile: shop
models:
shop:
+materialized: view
finance:
+materialized: table
+group: finance
+access: private$ dbt ls -s access:public
=======================Selected nodes=======================
shop.finance.dim_customers.v1
shop.finance.dim_customers.v2
shop.finance.fct_revenue
Finished 'list' successfully for target 'dev'The public interface is fct_revenue and the two versions of dim_customers. Those get contracts and versions, and everything else can change freely. When a domain needs its own release cycle, split it into its own project with dbt Mesh on the dbt platform. Other projects can only ref its public models.
Then speed. Slim CI builds only state:modified+ and defers to production for the rest. Big tables are incremental, and selectors keep each job to its own domain. And dbt v2 brings faster parse and compile times, especially on large projects.
So the full plan is:
- Domains as groups: an owner each, private by default
- A small public interface: contracts and versions
- dbt Mesh: a project per domain, on dbt Enterprise or Enterprise+
- Slim CI:
state:modified+with--defer - Build less: incremental, microbatch, selectors per job
- Guardrails in CI: the v2 parse,
dbt_project_evaluatorfor naming and structure rules, and CODEOWNERS on every domain folder
Key points to remember
- Groups with owners: helpers private, a few models public
- Versions and a deprecation_date for breaking changes
- Slow models: run_results.json, then the warehouse query history
- dbt v2 is GA and strict: prepare with the v2 parser and dbt-autofix
- Exposures so lineage reaches the dashboards
The full script
This Python script writes the whole project and runs every dbt command in order; run pip install dbt first.
Show the full script that builds this project and runs every step
# ========================================================================
# dbt Interview Q&A #9 - Governance, performance and dbt v2
# TechBrothersIT. Sample data is invented for teaching.
# Every step below was run with dbt v2 (dbt OSS 2.0.8) on DuckDB; the outputs in the video and the
# blog post are the real outputs. To run it yourself, in an empty folder:
# pip install dbt (dbt v2; adapters are bundled, and DuckDB needs no account or server)
# python dbt_Interview_QA_09_Code.py
# ========================================================================
import os, subprocess
def write(path, text):
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(text.strip("\n") + "\n")
def remove(path):
if os.path.exists(path):
os.remove(path)
def dbt(cmd, expect_error=False):
print("\n$ " + cmd, flush=True)
rc = subprocess.run(cmd, shell=True).returncode
if rc != 0 and not expect_error:
raise SystemExit("stopped: this command was expected to succeed")
# Step 0: the project and its sample data
write('dbt_project.yml', """\
name: shop
version: '1.0'
profile: shop
models:
shop:
+materialized: view
finance:
+materialized: table
""")
write('seeds/raw_orders.csv', """\
order_id,customer_id,amount,status,order_date
1,10,120.00,completed,2026-10-01
2,11,80.00,completed,2026-10-01
3,10,45.50,refunded,2026-10-02
4,12,200.00,completed,2026-10-03
5,11,60.00,completed,2026-10-03
""")
write('seeds/raw_customers.csv', """\
customer_id,name,country
10,Ali Khan,US
11,Sara Lee,UK
12,John Smith,US
""")
write('profiles.yml', """\
shop:
target: dev
outputs:
dev:
type: duckdb
path: dev.duckdb
threads: 1
""")
# Step 1: Groups and access: a marketing model refs a private finance helper
write('models/groups.yml', """\
groups:
- name: finance
owner:
name: Finance Data Team
email: finance-data@example.com
- name: marketing
owner:
name: Marketing Analytics
email: marketing@example.com
""")
write('models/finance/int_revenue.sql', """\
select order_id, customer_id, amount
from {{ ref('raw_orders') }}
where status = 'completed'
""")
write('models/finance/fct_revenue.sql', """\
select customer_id, sum(amount) as revenue
from {{ ref('int_revenue') }}
group by customer_id
""")
write('models/finance/_finance.yml', """\
models:
- name: int_revenue
config:
group: finance
access: private
- name: fct_revenue
config:
group: finance
access: public
""")
write('models/marketing/mkt_top_customers.sql', """\
select customer_id, amount
from {{ ref('int_revenue') }}
where amount > 100
""")
write('models/marketing/_marketing.yml', """\
models:
- name: mkt_top_customers
config:
group: marketing
""")
dbt('dbt build', expect_error=True) # EXPECTED TO FAIL: the video shows this error
# Step 2: The fix: ref the public model
write('models/marketing/mkt_top_customers.sql', """\
select customer_id, revenue
from {{ ref('fct_revenue') }}
where revenue > 100
""")
dbt('dbt build -s +mkt_top_customers')
# Step 3: Model versions: v1 and v2 of dim_customers, and a consumer pinned to v1
write('models/finance/dim_customers_v1.sql', """\
select customer_id, name
from {{ ref('raw_customers') }}
""")
write('models/finance/dim_customers_v2.sql', """\
select customer_id, name as customer_name
from {{ ref('raw_customers') }}
""")
write('models/finance/_dim_customers.yml', """\
models:
- name: dim_customers
latest_version: 2
config:
group: finance
access: public
contract: {enforced: true}
latest_version_pointer:
enabled: true
columns:
- {name: customer_id, data_type: integer}
- {name: name, data_type: varchar}
versions:
- v: 1
deprecation_date: 2027-03-31
- v: 2
columns:
- {include: all, exclude: [name]}
- {name: customer_name, data_type: varchar}
""")
write('models/marketing/mkt_customers.sql', """\
select * from {{ ref('dim_customers', v=1) }}
""")
dbt('dbt build -s +dim_customers mkt_customers')
# Step 4: The consumer moves to a plain ref, which resolves to the latest version; the pointer view
write('models/marketing/mkt_customers.sql', """\
select * from {{ ref('dim_customers') }}
""")
dbt('dbt compile --inline "select * from {{ ref(\'dim_customers\') }}"')
# Step 5: Find the slow model: run a chain, then rank run_results.json
write('models/events/int_order_events.sql', """\
{{ config(materialized='table') }}
-- every order cross joined to a big range: the slow one
select o.order_id, count(*) as events
from {{ ref('raw_orders') }} as o
cross join range(30000000) as e(i)
where hash(o.order_id, e.i) % 7 = 0
group by o.order_id
""")
write('models/events/fct_order_events.sql', """\
{{ config(materialized='table') }}
select count(*) as orders, sum(events) as events
from {{ ref('int_order_events') }}
""")
write('scripts/slowest_models.sql', """\
-- rank the models of the last run by execution time
select
r ->> 'unique_id' as unique_id,
r ->> 'status' as status,
json_extract_string(r, '$.timing[*].name') as timing
from (
select unnest(results) as r
from read_json('target/run_results.json')
)
order by (r ->> 'execution_time')::double desc
limit 1
""")
dbt('dbt run -s +fct_order_events')
# Step 6: YAML that worked on v1: dbt v2 fails the parse
write('models/staging/stg_orders.sql', """\
select order_id, customer_id, amount, status, order_date
from {{ ref('raw_orders') }}
""")
write('models/staging/_stg_orders.yml', """\
models:
- name: stg_orders
descriptoin: One row per order
owner: finance
columns:
- name: status
tests:
- accepted_values:
values: ['completed', 'refunded']
""")
dbt('dbt parse', expect_error=True) # EXPECTED TO FAIL: the video shows this error
# Step 7: The v2-ready YAML parses
write('models/staging/_stg_orders.yml', """\
models:
- name: stg_orders
description: One row per order
config:
meta:
owner: finance
columns:
- name: status
data_tests:
- accepted_values:
arguments:
values: ['completed', 'refunded']
""")
dbt('dbt parse')
# Step 8: An exposure, and the lineage from the dashboard back to the seeds
write('models/exposures.yml', """\
exposures:
- name: exec_revenue_dashboard
label: Executive Revenue
type: dashboard
maturity: high
url: https://bi.example.com/dashboards/42
depends_on:
- ref('fct_revenue')
- ref('dim_customers')
owner:
name: Finance Data Team
email: finance-data@example.com
""")
dbt('dbt ls -s +exposure:exec_revenue_dashboard')
# Step 9: The other direction: what a change to fct_revenue reaches
dbt('dbt ls -s fct_revenue+')
# Step 10: dbt docs generate
dbt('dbt docs generate')
# Step 11: Private by default for the finance folder; list the public interface
write('dbt_project.yml', """\
name: shop
version: '1.0'
profile: shop
models:
shop:
+materialized: view
finance:
+materialized: table
+group: finance
+access: private
""")
dbt('dbt ls -s access:public')
Part 10: Real-World Manager Scenarios
The sample data on this page is invented for teaching. Every terminal output is the real output of the dbt command above it, run with dbt v2 (dbt OSS 2.0.8) on DuckDB; run times and node counters are left out, and node lines are listed in DAG order. Part of the Real Time dbt Interview Questions and Answers series on TechBrothersIT. Features, limits and their status (GA or preview) are current as of October 2026; check the official dbt documentation (docs.getdbt.com) for anything you quote in an interview.



No comments:
Post a Comment
Note: Only a member of this blog may post a comment.