Doc blocks: переиспользуемая документация
В предыдущем уроке мы писали descriptions прямо в YAML через многострочный |. Это удобно для коротких описаний, но плохо масштабируется:
- Если одна и та же колонка
customer_idупомянута в 15 моделях — приходится писать одинаковую description 15 раз. Изменилась бизнес-логика — править в 15 местах. - Длинные descriptions (с примерами кода, нумерованными списками, диаграммами) — неудобно держать в YAML. Markdown теряется в синтаксисе YAML.
Решение — doc blocks. Описания живут в отдельных .md файлах, а YAML на них ссылается через doc('name').
Что такое doc block
Doc block — это именованный кусок markdown в .md файле. Структура:
- Открывающий тэг:
{% docs name_of_block %} - Контент: произвольный markdown
- Закрывающий тэг:
{% enddocs %}
Файл имеет расширение .md. Может содержать несколько doc blocks, каждый со своим именем.
Имя блока — это идентификатор, по которому на него ссылаются. Snake_case, должен быть уникален в проекте.
Где лежат doc blocks
В dbt-проекте .md файлы могут быть в models/, seeds/, snapshots/, macros/ директориях. dbt сканит их так же, как и .yml.
Convention — класть .md рядом с YAML, обычно с тем же базовым именем:
models/staging/jaffle/
_jaffle__sources.yml
_jaffle__models.yml
_jaffle__docs.md
stg_jaffle__customers.sql
stg_jaffle__orders.sql
Или один глобальный файл models/_docs.md для shared concepts:
models/
_docs.md
staging/
...
marts/
...
Оба паттерна работают. В курсе используем первый — .md рядом с YAML.
Базовый пример
В файле models/staging/jaffle/_jaffle__docs.md (показано как plain text):
{% docs customer_id %}
Primary key клиента.
Sequential integer от 1. Никогда не reused, даже если клиент удалён.
В downstream-моделях используется как FK на dim_customers.customer_id.
{% enddocs %}
{% docs order_status %}
Статус заказа. Возможные значения:
- placed — заказ создан, не оплачен.
- paid — оплачен.
- shipped — отправлен.
- delivered — доставлен.
- cancelled — отменён.
- refunded — возвращён.
{% enddocs %}
И в YAML _jaffle__models.yml:
version: 2
models:
- name: stg_jaffle__orders
columns:
- name: customer_id
description: '{{ doc("customer_id") }}'
- name: status
description: '{{ doc("order_status") }}'
Когда вы запустите dbt docs generate, dbt подставит markdown из doc block. В UI отображается красиво, с rendered markdown.
Когда использовать doc blocks
Эвристика: если description переиспользуется больше 3 раз или длиннее 5 строк — doc block. Иначе inline.
Большой пример: глоссарий
В крупных проектах часто есть _glossary.md со всеми бизнес-понятиями. Пример (показано как plain text, чтобы не путать рендерер):
{% docs revenue_usd %}
Revenue в USD — выручка в долларах США.
Формула: SUM(quantity * unit_price * (1 - discount_pct)) для всех позиций.
Не включает: tax, shipping, refund.
Currency conversion: для не-USD заказов берётся курс на дату заказа
из dim_currency_rates.
Edge cases:
- Cancelled orders: 0.
- Refunded orders: оригинальная сумма (не вычитает refund).
См. также: net_revenue_usd (после refund), revenue_eur (в евро).
{% enddocs %}
{% docs active_customer %}
Активный клиент — клиент, сделавший хотя бы 1 заказ за последние 90 дней.
Формула (псевдо-SQL):
SELECT customer_id FROM fct_orders
WHERE order_date больше CURRENT_DATE - INTERVAL 90 days
GROUP BY customer_id
В dbt — модель int_active_customers за последний run.
Edge cases:
- Включает refunded orders (любая активность = active).
- Не включает cancelled orders.
{% enddocs %}
{% docs churn %}
Churn — клиент, который был активен 90 дней назад, но не активен сегодня.
Это rolling window definition. Альтернативные definitions:
- Subscription churn (cancelled subscription).
- Calendar-month churn (no orders in current calendar month).
Стандарт компании: rolling 90-day churn.
{% enddocs %}
И в YAML моделей просто:
models:
- name: fct_orders
columns:
- name: revenue_usd
description: '{{ doc("revenue_usd") }}'
- name: customer_metrics
columns:
- name: is_active
description: '{{ doc("active_customer") }}'
- name: is_churned
description: '{{ doc("churn") }}'
Бизнес-определение пишется один раз. Изменилась формула churn — правишь один doc block, и все 5 мест в проекте обновляются автоматически.
Это DRY для документации.
Doc block для целой модели
Не только колонки. Описание модели тоже может быть в doc block. Структура doc block для fct_orders примерно такая:
- Grain: один заказ (order_id уникален).
- Бизнес-определение: завершённый заказ. Включает paid, shipped, delivered, refunded. Не включает cancelled и pending.
- Обновление: каждые 30 минут через dbt Cloud Job.
- Зависимости: stg_jaffle__orders, int_order_items_pivoted, dim_customers.
- Использование: главная таблица для revenue-аналитики. Используется в дашбордах.
- Caveats: refunded orders включены с original amount; tax не учтён в revenue колонке.
В YAML на этот doc block ссылаются:
models:
- name: fct_orders
description: '{{ doc("fct_orders") }}'
И в Lineage UI dbt при клике на ноду откроется развёрнутое описание модели вместе с lineage-графом.
Совет про названия doc blocks
- Используйте префиксы по доменам:
revenue_*,customer_*,order_*. Помогает группировать. - Версионируйте через имя, не через содержимое:
customer_id_v2если есть существенное изменение semantics. - Один файл — одна тема:
_revenue_glossary.md,_customer_glossary.md. Меньше merge-конфликтов.
Попробуй сам
Создайте файл _jaffle__docs.md рядом с моделями staging. Добавьте 2 doc block: один для customer_id, один для order_status. Опишите оба бизнес-точно. Затем в _jaffle__models.yml сошлитесь на них через doc(). Запустите dbt docs generate && dbt docs serve и проверьте, что descriptions подтянулись с markdown форматированием.