dbt Interview Questions 2026: Models, Tests, Snapshots, Macros and CI — Answered Properly
Sixteen dbt interview questions with the depth a real interviewer expects — from materializations and ref() vs source() to incremental pitfalls, SCD2 snapshots, and slim CI. Written for Indian data engineers and analytics engineers preparing in 2026.
By Durgesh Yadav — Senior Data Engineer @ 7-Eleven · Updated 2026-07-25. Preparation guidance, not a hiring guarantee.
What questions are asked in a dbt interview?
dbt interviews center on models and the four materializations (view, table, incremental, ephemeral), ref() versus source(), generic and singular tests, snapshots for SCD Type 2, macros and Jinja, documentation and lineage, and running dbt in CI. Expect scenario follow-ups on incremental pitfalls, idempotency, and where dbt sits in an ELT stack.
Interviewers use dbt questions to separate tutorial knowledge from production experience. Reciting the four materializations is table stakes; the follow-ups are where candidates fall: why does an incremental model's output drift from a full refresh, what happens when a source column disappears, how would you review a teammate's model PR. The other consistent probe is the boundary between dbt and the warehouse — dbt compiles Jinja-templated SQL and orchestrates execution order, while the warehouse does all the compute. Candidates who describe dbt as an ETL tool that 'processes data' usually don't recover. For analytics-engineer roles, expect emphasis on testing strategy, documentation, and lineage-driven impact analysis. For data-engineer roles, expect dbt alongside orchestration questions: how a scheduler triggers dbt, how environments are separated, and what a sane CI pipeline looks like on a pull request.
Materialization tradeoffs, not just definitions
Incremental correctness: late data, idempotency, deletes
The dbt/warehouse boundary — who actually runs compute
Production habits: tests, CI, environments, docs
How to Prepare Without Paying for Anything
dbt Core is open source, and the DuckDB adapter runs entirely on your laptop, so you can practice the full workflow with zero warehouse cost. Build one small project end to end rather than reading ten blog posts: declare sources in YAML, write staging views, build one incremental fact model with is_incremental(), add a snapshot, write a custom generic test, and generate docs. The single most differentiating habit is reading the compiled SQL in the target/ directory after every run — it teaches you exactly what Jinja becomes and makes debugging questions easy. Then rehearse speaking: a two-minute walkthrough of your DAG from source to mart, out loud, covering why each model has its materialization. Finally, skim the release notes for features interviewers now expect awareness of — dbt build behavior, state-based selection, and model contracts.
dbt Core + DuckDB is a complete free practice stack
Build one project: source, staging, incremental mart, snapshot, test
Read target/compiled after every run until it is boring
Rehearse a two-minute spoken walkthrough of your DAG
Common Mistakes That Sink Candidates
The fastest self-disqualification is calling dbt a data-movement or ETL tool — it transforms data already in the warehouse and moves nothing. Second is treating 'make it incremental' as the answer to every performance question without mentioning the correctness costs: late-arriving rows, non-idempotent logic, and deletes that append or merge strategies never see. Third is Jinja blindness — candidates who have never read their compiled SQL cannot answer 'what does this macro produce', and it shows immediately. Fourth is presenting tests and documentation as optional polish; teams hiring for dbt roles specifically want people who test sources, set severities deliberately, and use lineage before changing shared models. Smaller but memorable errors: hardcoding table names instead of ref()/source(), confusing snapshots with backups (snapshots capture change history; they restore nothing), and claiming ephemeral models are 'free' when they recompute in every consumer.
Describing dbt as ETL — it never moves or computes data itself
Recommending incremental models without their failure modes
Never having read compiled SQL in target/
Hardcoded table names instead of ref() and source()
The Analytics Engineer Angle: Why dbt Rounds Look Different in India
In India, dbt questions show up in three hiring contexts, and the framing differs. Services companies and consultancies often staff warehouse-migration projects — legacy stored procedures moving to a modern stack — so they probe whether you can refactor monolithic SQL into layered, tested dbt models and explain the staging/intermediate/marts convention. GCCs and product companies running Snowflake, BigQuery, or Databricks tend to hire for the analytics-engineer profile, where dbt is the core tool and interviews go deep on testing, CI, lineage, and cost-aware materialization choices. Either way, the dbt round is almost never standalone: expect a hard SQL round beside it, plus warehouse-specific questions like partitioning or clustering that interact with incremental strategies. A strong project story — 'here is a pipeline I restructured, here is what the tests caught' — carries more weight than tool trivia in every one of these settings.
GCC and product roles go deeper on CI, lineage, and cost
dbt rounds pair with SQL and warehouse-specific questions
One concrete project story beats memorized definitions
Question bank
16 interview questions with answers
Real questions from beginner to advanced, each with a concise model answer — practice them, then rehearse live in a mock interview.
EasyWhat is dbt and what problem does it solve?
dbt handles the transform step in an ELT stack. Raw data is already loaded into a warehouse (Snowflake, BigQuery, Redshift, Databricks) by tools like Fivetran, Airbyte, or custom pipelines; dbt then turns SQL SELECT statements into tested, documented, version-controlled tables and views. It compiles Jinja-templated SQL into plain SQL and submits it to the warehouse — dbt itself moves no data and runs no compute. Its real contribution is engineering discipline for analytics: modular models, automatic dependency ordering, tests, documentation, and separate dev/prod environments. dbt Core is the open-source CLI; dbt Cloud adds a scheduler, browser IDE, and hosted docs on the same engine. A good closing line: dbt let analysts work like software engineers, which is why the analytics-engineer title exists.
EasyWhat is a dbt model, and what actually happens when you execute dbt run?
A model is a single SELECT statement saved as a .sql file; the filename becomes the relation name in the warehouse unless you set an alias. When you execute dbt run, dbt parses the project and builds a DAG from every ref() and source() call. It then compiles each model — rendering Jinja and resolving ref('stg_orders') to the correct database.schema.table for the target environment — and wraps the compiled SELECT in DDL/DML matching its materialization: CREATE VIEW AS for views, CREATE TABLE AS for tables, a MERGE or INSERT for incremental models. Models execute in dependency order, parallelized up to the configured thread count. Compiled SQL lands in the target/ directory, which is the first place to look when debugging.
EasyWhat is the difference between ref() and source()?
ref() points at another dbt model; source() points at raw data loaded by something outside dbt. ref('stg_orders') does two jobs: it resolves to the right schema for the current environment (dev versus prod), and it registers a dependency edge so dbt knows build order and can draw lineage. source('shopify', 'orders') resolves to a table declared in a sources YAML block, where you also document ownership and can configure freshness checks (dbt source freshness) that warn when the loader falls behind. The working rule: the only hardcoded table names in a project live inside source definitions; every model reads through ref() or source(). Hardcoding names elsewhere breaks environment separation and silently punches holes in lineage — a red flag interviewers deliberately probe for.
EasyExplain the four main materializations and when you would use each.
View, the default: dbt creates a database view — always current and nearly free to build, but compute is paid on every read; right for staging models. Table: CREATE TABLE AS on every run — fast to query but fully rebuilt each time and stale between runs; right for marts hit by BI tools. Incremental: builds the table once, then processes only new or changed rows on later runs; essential for large event and fact tables where full rebuilds are too slow or expensive. Ephemeral: no database object at all — the model's SQL is inlined as a CTE into each downstream consumer; useful for light reusable logic you don't want cluttering the warehouse. Some adapters add materialized views or dynamic tables, but these four are the portable core every interviewer expects.
EasyWhat is the difference between generic and singular tests in dbt?
Generic tests are parameterized and reusable: defined once, applied to any model or column from YAML. dbt ships four — unique, not_null, accepted_values, and relationships for referential integrity — and packages like dbt_utils and dbt_expectations add many more. Singular tests are one-off SQL files in the tests/ directory: you write a query that returns bad rows, and the test passes only when it returns zero rows — for example, orders whose line items don't sum to the order total. Both run under dbt test. Useful configs: severity: warn for non-blocking checks, warn_if/error_if thresholds, a where clause to scope the test, and store_failures to persist failing rows for debugging. Strong answers mention testing sources too, not only final marts.
MediumHow does an incremental model work mechanically?
On the first run, or with --full-refresh, dbt builds the entire table. On later runs the is_incremental() macro returns true, so you add a filter selecting only new data — commonly where loaded_at > (select max(loaded_at) from {{ this }}), where {{ this }} is the existing target table. How those rows land depends on incremental_strategy: append simply inserts; merge upserts on the configured unique_key; delete+insert removes matching keys then inserts; insert_overwrite replaces whole partitions on adapters that support it, like BigQuery and Spark. The on_schema_change config decides what happens when columns appear or vanish: ignore (the default), append_new_columns, sync_all_columns, or fail. Interviewers almost always follow up with 'what goes wrong here' — have late-arriving data and idempotency ready.
MediumWhat are snapshots, and how do they implement SCD Type 2?
Snapshots record how rows in a mutable table change over time, producing a Type-2 slowly changing dimension. You wrap a SELECT in a snapshot definition (executed by dbt snapshot), pick a strategy — timestamp, which trusts an updated_at column, or check, which compares a configured list of columns — and dbt maintains metadata columns: dbt_valid_from, dbt_valid_to (NULL marks the current row), dbt_scd_id, and dbt_updated_at. When a tracked row changes, the old version is closed out and a new current row inserted, so you can answer questions like 'what plan was this customer on in March?'. Two points worth adding: snapshot raw sources rather than transformed models, and treat snapshot tables as irreplaceable — history cannot be regenerated, so never rebuild them the way you would full-refresh a model.
MediumWhat are macros, and when should you write one?
Macros are Jinja functions that expand into SQL text at compile time — dbt's answer to DRY. Typical uses: a currency-conversion expression reused across models, generating a column list dynamically, standardizing grants via post-hooks, or overriding built-ins like generate_schema_name to control schema naming per environment. Packages matter here: dbt_utils covers most common needs (surrogate_key, pivot, union_relations), so a mature answer is 'check packages before writing my own'. The conceptual point interviewers listen for: Jinja executes during compilation, not in the warehouse. A macro cannot react to query results at run time unless it uses run_query or statement, and that introspective code must sit behind {% if execute %} so project parsing doesn't break. Overused Jinja makes compiled SQL unreadable — reach for it only where repetition genuinely hurts.
MediumHow does dbt determine execution order, and how does lineage help day to day?
dbt builds its DAG by parsing every ref() and source() call — there is no manually declared ordering anywhere. At run time it executes nodes in topological order, running independent nodes in parallel up to the threads setting. The same graph powers selection syntax: --select +fct_orders builds a model plus everything upstream, fct_orders+ takes everything downstream, and state:modified+ picks changed nodes and their children. Lineage becomes documentation through dbt docs generate, which writes manifest.json and catalog.json and renders an interactive graph via dbt docs serve. Exposures extend lineage beyond the warehouse: declare a dashboard as an exposure and you can see which models feed it before you change anything. That impact-analysis story — 'I check downstream consumers before merging' — is what interviewers want from a lineage question.
MediumHow do you structure a dbt project, and where do sources and seeds fit?
The widely used convention is three layers. Staging: one model per source table, named stg_<source>__<table>, doing only renames, casts, and light cleanup — materialized as views, each reading from exactly one source(). Intermediate: joins and business logic shared by several marts, kept away from BI tools. Marts: facts and dimensions for consumption, materialized as tables or incrementals. Alongside these, sources declare raw tables in YAML with freshness config, and seeds are small version-controlled CSVs loaded by dbt seed — lookup data like country codes or a channel mapping, never a data-loading mechanism. This layering keeps lineage readable (raw to staging to intermediate to marts), makes each layer independently testable, and means a source-schema change gets absorbed once in staging instead of rippling through every downstream model.
MediumWhat is the difference between dbt run, dbt test, and dbt build?
dbt run executes models only. dbt test runs tests only — and by that point bad data has already been written and possibly consumed. dbt build interleaves everything in DAG order: seeds, snapshots, models, and tests, testing each model immediately after materializing it. The decisive behavior: if a model's error-severity test fails, dbt build skips that model's downstream dependents, so a broken upstream table never propagates into marts. That makes build the right default for production jobs and CI. Running run and test as separate steps still appears in older projects and is defensible when tests are pure monitoring, but you should be able to name the propagation risk you are accepting. Mentioning severity levels here — warn versus error — shows you have tuned this in practice.
MediumHow would you set up CI for a dbt project?
A solid pull-request pipeline: check out the branch, run dbt deps, then build only what changed — dbt build --select state:modified+ --defer --state <prod-artifacts>. State comparison diffs the branch's manifest against production's manifest.json to find modified nodes; the plus suffix adds everything downstream so the blast radius gets built and tested too. --defer resolves references to unmodified parents against production relations instead of rebuilding them, which is what keeps CI fast and cheap. Build into a PR-specific schema — for example one containing the PR number — and drop it when the PR merges or closes. Add SQLFluff for linting and a dbt parse or compile step to catch broken Jinja early. The signal interviewers want: you know CI should not rebuild the entire project on every commit.
HardWhat are the classic failure modes of incremental models, and how do you handle them?
Four classics. Late-arriving data: filtering on max(timestamp) from {{ this }} drops rows that land after later-stamped rows; fix with a lookback window that reprocesses the last N days, combined with merge on a unique_key so reprocessing stays idempotent. Non-idempotent logic: anything depending on current_timestamp, run timing, or window functions over full history makes incremental output diverge from a full refresh — schedule periodic full refreshes and diff the results to catch drift. Deletes: append and merge never see rows deleted at the source; handle them with insert_overwrite over whole partitions, soft-delete flags from the loader, or snapshots. Schema drift: on_schema_change defaults to ignore, so new source columns are silently absent until someone notices. The meta-answer interviewers want: incremental is an optimization with correctness costs you must actively manage, not a free speedup.
HardWhen do ephemeral models become a bad idea?
Ephemeral models are inlined as CTEs into every consumer, and that is exactly where they bite. First, recomputation: five downstream models means the logic executes five times — if it is an expensive aggregation, a table or view is cheaper. Second, debuggability: there is no relation in the warehouse to query when numbers look wrong; you must read compiled SQL in target/ and reconstruct the CTE by hand. Third, operational limits: you cannot dbt run an ephemeral model directly, they do not support model contracts, and deep chains of ephemerals produce enormous compiled queries that some optimizers handle badly. Reasonable uses: very light, cheap transformations shared by a couple of models, or keeping a thin layer out of a cluttered schema. Beyond that, prefer views — a similar cost profile, but inspectable and individually runnable.
HardHow do you write a custom generic test, and what production concerns apply?
Define it with a test block — {% test positive_values(model, column_name) %} select * from {{ model }} where {{ column_name }} <= 0 {% endtest %} — under tests/generic/ (or macros/ in older projects). The convention: return the failing rows; zero rows means pass. Extra arguments become YAML parameters, so a values_within_range test can take min and max per column, and you apply it in schema YAML exactly like unique or not_null. Production concerns matter more than syntax: set severity or warn_if/error_if thresholds so noisy checks don't block deploys, scope expensive tests with a where config — say, only the last 30 days on a huge table — and enable store_failures so failing rows are queryable during incident triage. Also say you would check dbt_utils and dbt_expectations first; rewriting a test a package already provides is a mild red flag.
HardHow do model contracts, versions, and access settings support multi-team dbt at scale?
Since dbt 1.5, three features target multi-team scale. Model contracts: declare column names and data types in YAML with enforced: true (plus constraints like not_null where the adapter can enforce them); the build fails if the model's output drifts from the contract, turning breaking changes into build-time errors instead of downstream incidents. Access modifiers: mark models private, protected, or public within groups, so other teams can only ref() your published interfaces, never your internals. Model versions: publish fct_orders v2 alongside v1 with a deprecation date, giving consumers a migration window instead of a hard break. Combined with groups, ownership metadata, and exposures, this underpins the multi-project 'dbt Mesh' pattern. The framing interviewers reward: it is API discipline applied to datasets — typed interfaces, visibility rules, and versioned deprecation.
Related Guides
Keep Preparing
Move between roadmaps, interview questions, and tools without losing your preparation thread.
No — dbt is a multiplier on SQL, not a substitute for it. Roles that list dbt (analytics engineer, data engineer on modern stacks) assume strong SQL, solid warehouse knowledge, and data-modeling judgment underneath. What dbt adds to your profile is engineering discipline: testing, version control, CI, and lineage. Pair a small public dbt project with deep SQL preparation and one warehouse (Snowflake, BigQuery, or Databricks) and the combination is genuinely competitive.
Do I need dbt Cloud experience, or is dbt Core enough?
dbt Core is enough for almost every interview. The concepts tested — models, materializations, tests, snapshots, macros, state-based selection — are identical in both. Know what Cloud layers on top so you can articulate the difference: a scheduler, a browser IDE, hosted documentation, and managed CI jobs. If a team runs Core, they orchestrate it themselves with a scheduler like Airflow, which is itself a useful thing to mention.
How much SQL do I need before attempting dbt questions?
A lot — a dbt interview is largely a SQL interview wearing a jacket. Every model compiles down to plain SQL, so weak joins, shaky window functions, or confusion about aggregation edge cases will undermine any dbt claim you make. Be comfortable with CTE-heavy query structure, window functions for deduplication and ranking, and reasoning about grain, because staging-to-mart questions are really questions about grain and joins.
Is the dbt certification worth doing?
Treat it as optional. It can help at resume-screening stage with some employers, and the syllabus is a reasonable study structure if you want one. But interviewers consistently weight a project you can walk through and production-flavored answers — incremental failure modes, CI design, test strategy — far above a certificate. If your employer pays for it, take it; if you are choosing between the exam and building a real project, build the project.
Can freshers expect dbt questions in 2026?
Increasingly yes, especially from teams that hire freshers directly into analytics-engineering or modern-data-stack work. Expectations stay at fundamentals: what dbt does, what a model is, ref() versus source(), the four materializations, and the built-in generic tests. A fresher who has a small dbt project on GitHub — even jaffle-shop extended with a snapshot and a custom test — stands out sharply, because most candidates at that level have only read about the tool.
Next Step
Turn The Guide Into Practice
Use PrepNPlaced tools to turn this learning path into resume proof, targeted practice, and interview-ready explanations.