Course outline

dbt / Intermediate: history and controlled workflows / Lesson 7

Contracts, documentation and model interfaces

Publish a stable branch model while separating column-shape contracts, business assertions and access governance.

About 4 min readContent reviewed
On this page

Lesson goals

  • Define an explicit model shape and its supported enforcement scope.
  • Distinguish model versions and access from database user permissions.

A shared model behaves like a data API

A branch dimension consumed by several reports has an interface: column names, types, grain, meaning and freshness expectations. A model contract can check supported column-shape guarantees during a build, but it does not prove that branch_name is correct or that every branch exists. Keep business tests and source reconciliation alongside the contract. Removing a column or changing its type can break consumers even when the query still runs. Document intentional changes and choose a migration window rather than making downstream teams discover them through a failed morning report.

An enforced shape must list the actual columns

The illustrative contract declares two varchar columns for a two-column branch model, assuming the selected adapter and target support that type name. An enforced contract must cover every output column with compatible types; it is not a partial list of fields the author happens to remember. Support varies by materialization and platform: ephemeral and ordinary Python models are not covered by the same SQL-model contract behavior. Constraints are another layer whose definability and enforcement differ by database. A primary_key declaration accepted as metadata is not necessarily an enforced uniqueness rule.

yaml Illustrative technical example; logical results are reasoned, not engine-tested.
version: 2
models:
  - name: dim_branches
    config:
      contract:
        enforced: true
    columns:
      - name: branch_id
        data_type: varchar
      - name: branch_name
        data_type: varchar

A complete two-column shape example only. Target type compatibility and contract support require separate approved verification.

Descriptions should preserve meaning rather than restate names

Describe branch_id as the stable source identity, not simply โ€œthe branch ID.โ€ Explain whether renamed branches retain identity, which branch names are canonical and how missing source rows are handled. A docs block can centralize a longer explanation and doc() can reference it in supported property contexts. Documentation generation and hosted browsing have their own command and platform boundaries; no such command is run here. Descriptions and artifacts can expose private business metadata, so publish only approved content. A polished catalog page cannot compensate for an ambiguous underlying grain.

Pin a model interface when a breaking change needs coexistence

Model versions are different from the engine version, project version and YAML format version. A consumer can explicitly reference version1 while another migrates to version2, provided the project defines those versions and their relations. The fragment requests a known model version rather than automatically following latest_version. This can improve stability but creates maintenance and deprecation responsibilities. A change in business meaning can be breaking even if column names and types stay identical. Versioning does not eliminate the need to communicate grain and measure changes or reconcile both interfaces during migration.

jinja Illustrative technical example; logical results are reasoned, not engine-tested.
select branch_id, branch_name
from {{ ref('dim_branches', v=1) }}

Requires an explicitly defined version1 of dim_branches. It does not create that version or grant database read access.

Model access governs references rather than every user query

Private, protected and public model access govern supported dbt reference boundaries, with group and project context. They are not replacements for warehouse grants, row policies or secret management. In the v1.12 baseline, run-operation macros have documented access behavior different from ordinary model refs, which is another reason not to treat the modifier as a universal security boundary. Review actual runtime permissions separately. This course creates no contract, role, model version or documentation site. Local validation checks the authored shape and interface examples only, without claiming database enforcement or a successful build.

Common errors and fixes

A column contract is treated as a business-truth guarantee

Why it happens
Shape validation was confused with content and completeness assertions.
How to fix it
Add the relevant data tests and source reconciliation.
Prevent it next time
Document each guaranteeโ€™s exact scope.

Private model access is called warehouse access control

Why it happens
Project reference governance was confused with database permissions.
How to fix it
Review the actual runtime principal and platform enforcement.
Prevent it next time
Keep model governance and security tests separate.

A same-type calculation change silently breaks consumers

Why it happens
Semantic compatibility was reduced to column names and types.
How to fix it
Version or coordinate the meaning change and reconcile downstream measures.
Prevent it next time
Review grain, units and definitions in interface changes.

Check your understanding

1. Does a two-column enforced contract prove branch IDs are unique?

Show worked solution

Not by shape alone. It checks the declared output structure under supported behavior. Use a uniqueness assertion and verify any actual database constraint separately. Also reconcile missing branches and source identity; correct column types can contain incorrect business data.

2. Why might a consumer pin ref("dim_branches",v=1)?

Show worked solution

It can preserve the agreed interface while a new version is introduced. The project must actually define that version and maintain its deprecation policy. Pinning is not a database grant and does not freeze the underlying data against every source update.

Key takeaways

  • Shape contracts, data tests and permissions are different guarantees.
  • A model interface includes semantics and migration ownership.

Official references