Implementing and Maintaining External Dependencies Study Guide — dbt Analytics Engineering Certification

14% of the exam · domain 6 of 7 · dbt Analytics Engineering Certification · Free Implementing and Maintaining External Dependencies practice questions

0 of 46 topics covered
What the exam asks here — tap to tick off0 / 5 covered
Domain 6 of 7

Implementing and Maintaining External Dependencies

This domain covers everything your dbt project consumes but does not itself build: sources (raw tables loaded by EL tools), packages (reusable dbt code installed from the dbt Hub, git, tarballs, or local paths), and cross-project dependencies in dbt Mesh. It is weighted at roughly 14% of the exam — about one question in seven. Questions are heavily YAML-syntax-driven: you will be shown a sources: block, a packages.yml, or a dependencies.yml and asked what it resolves to, why it errors, or which of four near-identical snippets is correct. Expect a cluster of questions on freshness configuration, version pinning semantics, and the difference between installing a project as a package versus referencing it across projects.


Sources: declaring, documenting, and testing raw source tables

A source is a declaration of a table that already exists in the warehouse and that dbt never creates, never builds, and never drops. Declaring it gives you three things: the {{ source() }} function, lineage from raw data into your DAG, and a place to hang documentation, tests, and freshness rules.

Declaration syntax

Sources are defined in .yml files inside a configured path (model-paths, by default models/). There is no way to define a source in dbt_project.yml other than a handful of configs (+enabled, +meta, +tags, +freshness).

version: 2

sources:
  - name: jaffle_shop                 # the name used in source('jaffle_shop', ...)
    description: Raw app data landed by Fivetran
    database: raw                     # defaults to target.database
    schema: jaffle_shop_prod          # defaults to the source `name`
    loader: fivetran                  # documentation only
    loaded_at_field: _etl_loaded_at
    tags: [raw, pii]
    meta:
      owner: data-engineering
    freshness:
      warn_after: {count: 12, period: hour}
      error_after: {count: 24, period: hour}
      filter: _etl_loaded_at > dateadd(day, -3, current_date)
    tables:
      - name: orders
        identifier: raw_orders        # actual relation name in the warehouse
        description: One row per order
        columns:
          - name: id
            description: Primary key
            data_tests:
              - unique
              - not_null
      - name: customers
        freshness: null               # opts this table OUT of freshness checks

Key resolution rules you must memorize:

PropertyDefault if omittedNotes
databasetarget.databaseCross-database sources are legal on adapters that support it
schemathe source nameThis is why name and schema are so often confused
identifierthe table nameUse it when the warehouse name is ugly: name: orders, identifier: _RAW_ORDERS_V2
loaded_at_fieldnoneRequired for freshness on adapters without metadata-based freshness
quotingproject/adapter defaultSet at source or table level for case-sensitive relations

Reference it in a model as {{ source('jaffle_shop', 'orders') }} — source name first, table name second. It compiles to raw.jaffle_shop_prod.raw_orders given the YAML above.

How a source declaration resolves to a physical relation and flows into the DAG

Testing sources

Source tests are written exactly like model tests, under columns: data_tests: or table-level data_tests:. In dbt 1.8+ the key is data_tests; the legacy key tests still parses but is deprecated. Source tests are the only automated contract you have against a table you do not control.

  • Run them alone: dbt test --select source:jaffle_shop
  • One table: dbt test --select source:jaffle_shop.orders
  • All sources: dbt test --select source:*
  • dbt build does execute source tests when they are in the selection; it does not run freshness.

Freshness

dbt source freshness runs a select max(loaded_at_field) per table and compares it to the current warehouse timestamp.

SettingMeaningResult
warn_afterStaleness threshold for a warningYellow WARN status
error_afterStaleness threshold for failureERROR status, non-zero exit code
filterPredicate injected into the freshness queryUsed to prune huge partitioned tables
freshness: null at table levelDisables inherited freshnessTable reported as skipped

period accepts minute, hour, day only. Results are written to sources.json in the target directory. From dbt 1.9, on supporting adapters (Snowflake, BigQuery, Databricks/Spark, Redshift), loaded_at_field can be omitted and dbt uses warehouse metadata for last-modified time; dbt 1.10 added loaded_at_query for a custom freshness expression. If neither a loaded_at_field, loaded_at_query, nor metadata support exists, the table is skipped.

Selection using freshness state: dbt build --select source_status:fresher+ --state path/to/prod-artifacts rebuilds only models downstream of sources that got new data since the stored sources.json.

Worked scenario

Requirement: raw.finance.gl_entries is loaded hourly on weekdays, but raw.finance.exchange_rates is loaded once a week. Both live in the same source. CI must fail if GL data is more than 6 hours old, but must never fail on exchange rates.

Reasoning: Put freshness with error_after: {count: 6, period: hour} at the source level so it is inherited, then set freshness: null on the exchange_rates table to opt it out. Do not delete loaded_at_field — that would silently skip the table with a warning rather than deliberately disabling it, and it would break the day someone adds source-level freshness back.

Common traps

  • source() takes the source name, not the schema name. If name: jaffle_shop but schema: raw_jaffle, the call is still source('jaffle_shop', 'orders'). Distractors swap these.
  • ref() cannot point at a source, and source() cannot point at a model. A "source" that dbt builds is not a source — it is a model.
  • Freshness is not a test. dbt test does not check freshness; dbt build does not check freshness. Only dbt source freshness does.
  • identifier vs name: name is the dbt-facing alias used in source(); identifier is the physical table.
  • Sources cannot be materialized, snapshotted, or given a +materialized config. Configs available are essentially enabled, meta, tags, freshness, quoting, database/schema/identifier, overrides.
  • Tags set at source level apply to all its tables; table-level tags add to, not replace, them.
  • A package can define sources, and you override them in your project with the overrides: key naming the package — not by redefining the same source name (which raises a duplicate-source error).
  • Freshness warnings do not fail a job; only error_after breaches produce a non-zero exit code.

Unlock with a paid plan

7 more sections (about 3,019 words) continue below. Every paid plan unlocks all dbt Analytics Engineering Certification study guides, the full question bank and mock exams, from $4.97/month.