# Часть 12. Контракты данных: ODCS и контрактные проверки dbt Контракт данных нужен, чтобы потребитель ниже по цепочке видел обещание продукта, а не только текущую реализацию. В SDD Data контракт — это не декоративный YAML. Это проверяемый слой между продуктовым обещанием и SQL. ## Два уровня контракта ODCS отвечает за технический контракт дата-продукта: схему, качество, SLA и ломающие изменения. Контракт модели dbt отвечает за форму конкретной dbt-модели. В учебном примере мы используем `models/schema.yml`, встроенные dbt-тесты и singular-тесты как минимальные контрактные проверки; `contract.enforced: true` не включён, чтобы не смешивать первый учебный проход с ограничениями конкретного адаптера. В боевом контуре его можно включать там, где адаптер и materialization это поддерживают. ## Дрейф контракта Дрейф контракта возникает, когда модель меняется, а контракт нет. Например, `mart_customer_360` получает новую колонку `product_code`, но grain остаётся описан как «одна строка на `customer_id`». Это опаснее синтаксической ошибки: dbt может пройти, а потребитель получит другой смысл. Дрейф бывает трёх типов: - дрейф схемы: поля добавлены/удалены/переименованы; - семантический дрейф: поле осталось, но его смысл изменился; - процессный дрейф: validation.md или отчёт ревьюера не отражают изменение. ## Связь контракта и тестов Контракт должен быть покрыт доказательствами. Если ODCS говорит, что `risk_event_count_7d` обязателен, то должен быть хотя бы один тест или ручной факт ревьюера, который подтверждает присутствие и поведение `not_null`. Иначе контракт остаётся обещанием без доказательств. ## Qwen-запрос ```text Сравни specs/customer_360_contract.odcs.yaml, specs/models/mart_customer_360.md, models/schema.yml и models/marts/mart_customer_360.sql. Найди дрейф контракта и разрывы в покрытии тестами. Файлы не меняй. ``` ## Минимальный выход Составьте заметку о влиянии на контракт: ```markdown Изменённые поля: нет Ломающее изменение: нет Влияние на PII: прямые PII в mart не раскрыты Влияние на SLA: нет Покрытие проверками: unique/not_null для customer_id, обязательные поля, список запрещённых PII Нужно подтверждение: нет ``` Для будущих PR этот блок должен быть частью описания изменения. ## Разбор для читателя Контракт данных нужен не для красоты YAML. Он нужен, чтобы потребитель мог полагаться на форму и смысл витрины, не читая каждый раз SQL. В обычной разработке публичный API нельзя менять незаметно; в данных ситуация такая же, только API часто выглядит как таблица. Колонка, grain, freshness и политика PII — это части интерфейса. ODCS, контрактные проверки dbt и schema-тесты закрывают разные уровни. ODCS говорит о продукте и его техническом обещании. `schema.yml` и тесты подтверждают часть формы. Singular-тесты ловят правила, которые не укладываются в стандартные проверки. Ни один слой не заменяет остальные. Если оставить только dbt-тесты, можно получить зелёную модель с неправильным смыслом. Если оставить только ODCS, можно получить красивый контракт без доказательств. Дрейф контракта особенно неприятен тем, что часто выглядит полезным улучшением. Добавили колонку. Расширили детализацию. Переименовали показатель «для ясности». Каждый шаг по отдельности кажется небольшим, но потребитель ниже по цепочке может получить другой набор строк или другую бизнес-метрику. Поэтому влияние на контракт нужно описывать даже тогда, когда изменение кажется безопасным. В этой главе важно научиться читать изменение как ревьюер. Вопрос не «стал ли SQL лучше», а «осталось ли обещание тем же самым». Если обещание изменилось, требуется явное подтверждение человеком. Если обещание не изменилось, это тоже нужно доказать: какие поля прежние, какой grain прежний, какие PII не раскрыты, какие тесты прошли. ## Практика Сравните `customer_360_contract.odcs.yaml` и `models/schema.yml`. Выпишите обязательные поля и укажите, каким dbt-тестом, singular-тестом или ручным фактом они покрыты. ## Типичная ошибка Считать добавление колонки всегда безопасным. Даже nullable-колонка может изменить ожидания потребителей, если она выглядит как новая бизнес-метрика или меняет grain. ## Контрольные вопросы 1. Почему dbt-тесты не заменяют ODCS? 2. Что считать ломающим изменением? 3. Как Qwen Code должен вести себя при дрейфе?