Docs & Code Guide

Keep a data dictionary in sync with the schema: check drift, push docs down

A data dictionary maintained in a spreadsheet or wiki is a hand-written copy of something the warehouse already publishes: INFORMATION_SCHEMA knows every table, column, and type, updated the instant DDL runs. The copy drifts; the catalog view never does. Keeping a dictionary honest therefore means two moves: a drift check that diffs your dictionary against the live columns, and a write path that stores descriptions in the warehouse itself so they travel with the schema.

7 min readFor data teams whose column docs live in a spreadsheet nobody trusts

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

181/20003 credits left
Try:

No account needed · Editable canvas, not a picture

INFORMATION_SCHEMA is the machine-readable truth

Every SQL warehouse and database that matters, Postgres, Snowflake, BigQuery, Redshift, Databricks SQL, exposes an INFORMATION_SCHEMA (or an equivalent catalog) listing tables, columns, and data types as queryable views. It cannot be stale: it is the schema, published as data. Any dictionary you maintain elsewhere is a projection of these views plus one human column, the description.

That framing tells you exactly what can drift and what cannot. Names and types drift the moment someone runs an ALTER the dictionary does not know about. Descriptions cannot drift in the same way, but they can orphan: a description for a column that no longer exists, or a live column with no description at all. Both failure modes are detectable with one query.

The drift check: dictionary vs live columns

If your dictionary is loadable as a table (a seed file, a synced sheet, a catalog export), a FULL OUTER JOIN against the catalog view yields the complete drift report: columns that exist in the schema but not the dictionary, and dictionary rows describing columns that are gone. Run it on a schedule and fail loudly on rows, exactly like a test.

-- Drift report: undocumented columns and stale dictionary rows.
WITH live AS (
  SELECT table_name, column_name
  FROM information_schema.columns
  WHERE table_schema = 'analytics'
),
dict AS (
  SELECT table_name, column_name
  FROM analytics.data_dictionary
)
SELECT
  COALESCE(l.table_name,  d.table_name)  AS table_name,
  COALESCE(l.column_name, d.column_name) AS column_name,
  CASE
    WHEN d.column_name IS NULL
      THEN 'undocumented: live column missing from dictionary'
    WHEN l.column_name IS NULL
      THEN 'stale: dictionary row for a dropped column'
  END AS drift
FROM live l
FULL OUTER JOIN dict d
  ON  l.table_name  = d.table_name
  AND l.column_name = d.column_name
WHERE l.column_name IS NULL OR d.column_name IS NULL;

Store descriptions in the warehouse: persist_docs and COMMENT ON

The stronger fix removes the separate dictionary entirely. dbt already holds model and column descriptions in its YAML; the persist_docs config pushes them into the warehouse on every run, as relation and column comments on Snowflake and as descriptions on BigQuery. The docs then live where the schema lives, versioned with the transformations that create the columns, and every catalog tool that reads warehouse metadata sees them.

Without dbt, the same result comes from plain DDL: COMMENT ON TABLE and COMMENT ON COLUMN statements checked into the same migrations that alter the schema. A migration that adds a column without its comment is visible in review, which is precisely the coupling a spreadsheet can never give you.

# dbt_project.yml: push YAML descriptions into the warehouse
models:
  my_project:
    +persist_docs:
      relation: true
      columns: true

The picture next to the dictionary

A dictionary answers "what is this column"; it cannot answer "how do these tables relate". That is a diagram, and it deserves the same source-coupled treatment. Datadef accepts pasted DDL or dbt-style definitions as a prompt, so the ER view of your marts is generated from the schema rather than drawn beside it, and an agent connected to the Datadef MCP server can update it when models change. Shared public, the diagram embeds by URL in the wiki page next to the dictionary and follows edits within minutes. The Snowflake-specific version of this loop is in living diagram from Snowflake.

What the drift check cannot catch

No tool writes the descriptions. persist_docs moves them, the drift query finds the gaps, but a human still has to know that amount_cents excludes tax and say so. Budget for that; the automation only guarantees the writing happens once, in one place.

On the Datadef side: it diagrams the schema and its relationships, it is not a data catalog and does not manage column-level prose. For catalog features, lineage-aware search and ownership, look at the tools in data catalog best practices. The diagram regeneration is agent- or CI-invoked, never automatic, and the live embed requires a public project.

FAQ

How do I keep a data dictionary up to date?

Treat INFORMATION_SCHEMA as the source of truth for structure and check your dictionary against it on a schedule: a FULL OUTER JOIN between dictionary rows and information_schema.columns reports undocumented live columns and dictionary rows for dropped columns. Then move descriptions into the warehouse itself, with dbt persist_docs or COMMENT ON statements in migrations, so the docs change in the same commit as the schema.

What does dbt persist_docs actually do?

With persist_docs enabled for relations and columns, dbt writes the descriptions from your YAML files into the warehouse on each run, as table and column comments on Snowflake and Postgres, and as descriptions on BigQuery. The documentation then lives in warehouse metadata where catalog tools and INFORMATION_SCHEMA-based queries can read it, instead of only on a docs site.

How do I find undocumented columns in my warehouse?

Query the catalog. Join information_schema.columns against wherever your descriptions live, dictionary table or comment metadata, and select the rows with no match. On Snowflake, unnannotated columns are also visible directly: information_schema.columns has a comment field, and rows where it is null are your undocumented surface.

Is a spreadsheet a reasonable place for a data dictionary?

Only as a temporary export. A spreadsheet is a copy of the catalog with no coupling to DDL, so it starts drifting immediately and silently. If a spreadsheet is required for stakeholders, generate it from INFORMATION_SCHEMA plus the stored comments on a schedule, and treat the generated file as disposable output rather than the place where edits happen.

Can Datadef generate an ER diagram from my warehouse schema?

Yes, from the schema as text: paste DDL or dbt-style model definitions as the prompt and Datadef generates an editable entity relationship diagram. An AI agent connected to the Datadef MCP server can update it when the models change, invoked from CI or on request. With the project shared public, the diagram embeds by URL in your wiki and reflects edits within minutes.