Doc blocks, YAML descriptions, exposures, source freshness, and DAG lineage — how dbt projects communicate their data contracts to the world.
This combined domain covers how dbt projects communicate — internally through docs and descriptions, and externally through exposures that show where data is consumed. Plus source freshness to ensure raw data isn't stale.
Model, source, and column descriptions in YAML that power the dbt docs site
Reusable description text defined in .md files and referenced via doc()
Declare downstream consumers of your data (dashboards, ML models, apps)
Check that raw source data was loaded within an expected window
Documentation and exposures are often under-studied. The exam tests that you know: (1) how to use {{ doc('block_name') }} for reusable descriptions, (2) what exposures add to the DAG visualization, and (3) what dbt source freshness does and how to configure warn/error thresholds for it.
description: at the model, source, or column leveldbt docs generate to build the static documentation sitedbt docs serve to view it locally in a browser.md files inside the models/ directory (any subdirectory){% docs block_name %} ... description text ... {% enddocs %}description: "{{ doc('block_name') }}"dashboard, notebook, analysis, ml, applicationfreshness: block on a tableloaded_at_field — the timestamp column that indicates when a row was loadedwarn_after — duration after which dbt emits a warning (e.g., 12 hours)error_after — duration after which dbt fails the freshness check (e.g., 24 hours)dbt source freshness — checks all sources with a freshness configgenerate_schema_name and generate_database_name macros can be overridden to customize where models are materialized{{ ref('model') }} to create links to related models in the docs siteIn .yml under description: key at each level
Explore the generated docs site — lineage, descriptions, test results
{% docs block_name %} ... {% enddocs %} in a .md file inside models/
Apply the same doc block to the same column in two different models
dashboard, notebook, analysis, ml, application — all metadata, no SQL
Define it in a .yml file with type: dashboard, owner, and depends_on
They extend lineage to downstream consumers — metadata only
Requires loaded_at_field pointing to a timestamp column in the source table
See which sources pass, warn, or error based on configured thresholds
Only ref() and source() calls are tracked — hardcoded names are invisible
dbt build --select +exposure:exposure_name builds all upstream dependencies
5 questions on documentation and external dependencies.
3–4 days of targeted study covers this domain well.
Exposures are metadata only
Candidates assume exposures do something at runtime — they don't. Exposures are pure metadata that extend the lineage graph in dbt docs. No SQL runs, nothing is materialized.
Think of exposures as "documentation nodes" in the DAG. They exist only to show what consumes your data and to enable upstream selection (dbt build --select +exposure:name).
Freshness checks require a timestamp column
Configuring freshness: blocks without a loaded_at_field causes dbt source freshness to fail — it doesn't know which column to check for recency.
Always pair freshness: with loaded_at_field: pointing to the timestamp column in the source table that indicates when the row was loaded (e.g., _loaded_at, updated_at).
Copy-pasting descriptions instead of reusing them
The same column (e.g., customer_id) appears in 10 models with slightly different descriptions — inconsistent, hard to maintain, and a sign of poor documentation hygiene.
Create a doc block for customer_id in a .md file and reference it with {{ doc('customer_id') }} in every model's .yml. One change updates all 10 descriptions.
Two separate steps with different purposes
Candidates run dbt docs serve without first running dbt docs generate, or assume docs generate automatically opens a browser. These are two separate commands.
Always run dbt docs generate first (creates catalog.json + manifest.json). Then dbt docs serve to launch a local web server to view the docs. In dbt Cloud, generate runs automatically as part of jobs.
The DAG only shows what ref() and source() can see
A model that uses hardcoded schema.table references won't show those upstream dependencies in the docs DAG. The lineage graph is incomplete, making impact analysis unreliable.
Replace all hardcoded table references with ref() and source(). Complete lineage is a key benefit of dbt — don't sacrifice it for convenience.
dbt docs generate creates two files in the target/ directory: manifest.json (the project's complete model graph, tests, and metadata) and catalog.json (column-level metadata fetched from the warehouse). Both are needed for the full docs site. The manifest.json is also used for state-based selection.dbt build --select +exposure:dashboard_name builds only the models that feed a specific dashboard. This is useful in CI to verify that all changes to models upstream of a critical exposure are valid before merging to production.dbt source freshness. The command only checks tables that have an explicit freshness: configuration with a loaded_at_field. Sources without this config are excluded from freshness reporting.maturity field (low, medium, high) is informational metadata indicating how mature/stable the exposure is. It appears in the docs site to communicate to data consumers whether they should treat this exposure as experimental or production-ready. It does not affect how dbt builds models or runs tests.