Тема: Часть 1. Введение: SDD для данных
Уровень сложности: Средний
Расчётное время изучения: 2–3 часа (теория + практика с локальным проектом)
Предварительные требования: Базовое знание SQL (SELECT, JOIN, GROUP BY, агрегатные функции)
Понимание концепции хранилищ данных и витрин (marts)
Знакомство с методологией Spec-Driven Development (SDD) из первого тома
Базовое представление о dbt (модели, sources, тесты)
Уверенное владение командной строкой и Git
Цели обучения: Сформулировать главное отличие SDD Data от традиционного dbt-курса: порядок работы идёт от миссии и спецификации к SQL, а не наоборот.
Объяснить, почему невидимое изменение смысла данных (grain, метрика, freshness, PII) опаснее, чем ошибка в коде веб-приложения.
Определить роль спецификации дата-продукта как единственного источника истины и место dbt как исполнителя зафиксированного обещания.
Перечислить три базовых правила SDD Data: спецификация живёт в репозитории, проверочный факт имеет команду или шаг ревьюера, автор и ревьюер работают в разных режимах.
Запустить локальный smoke-тест учебного DataLakeHouse на DuckDB и зафиксировать собственный режим чтения курса.
Обзор: Первая часть книги вводит методологию Spec-Driven Development применительно к дата-платформам. Если в первом томе SDD защищал фичи веб-приложения от ошибок агента в маршрутах и UI, то здесь он защищает смысл данных: зернистость витрины, корректность метрик, свежесть, политику персональных данных и совместимость с потребителями ниже по цепочке. Главный тезис части: dbt остаётся основным инструментом трансформаций, но не является источником смысла. Смысл живёт в спецификациях, а SQL обязан исполнять зафиксированное обещание, а не создавать его постфактум.
Учебный проект — DataLakeHouse вымышленного банка «Банк Северный Мост». К концу книги читатель построит локальный dbt-проект на DuckDB, который собирает пять финальных витрин: mart_customer_360, mart_card_turnover_daily, mart_payment_risk_signals, mart_credit_portfolio_quality и mart_open_api_consents. Все данные синтетические, а российский банковский контекст служит набором ограничений: PII, платежи, согласия, риск-сигналы, регуляторная отчётность. Это воспроизводимая учебная архитектура, а не юридическая инструкция.
Часть закладывает ритм всего тома: сначала читатель записывает обещание дата-продукта, затем делает его исполнимым через dbt, затем доказывает фактами и отдаёт на независимое ревью. На уровне первой главы от читателя требуется выбрать режим чтения, запустить smoke-тест и зафиксировать в рабочих заметках понимание, что книга строит один воспроизводимый процесс дата-продукта, а не «банк вообще».
Ключевые концепции: Sdd data: Цикл разработки, в котором агент не начинает с реализации. Сначала человек и агент фиксируют намерение, границы, решения и проверочные факты, затем агент пишет код или SQL по утверждённым файлам, после чего отдельное ревью проверяет не только прохождение тестов, но и совпадение результата со спецификацией. Для данных цикл выглядит так: описать дата-продукт → определить контракт → реализовать dbt-модель → выполнить проверочные факты → оценить влияние на контракт → собрать доказательства релиза.
Спецификация дата-продукта: Документ, фиксирующий обещание потребителю: какой grain обещан, какие поля обязательны, какие запрещены, какие проверки допускают изменение, кто подтверждает дрейф или ломающие изменения. Живёт в репозитории, а не в чате, и служит единственным источником истины.
Контракт данных: Формальная договорённость между поставщиком и потребителями витрины о её структуре и семантике. Любое изменение контракта должно быть явно зафиксировано и одобрено, иначе оно считается ломающим.
Grain (зернистость) витрины: Семантическая единица, описывающая, что представляет собой одна строка в таблице (например, «один клиент» или «один клиент-продукт»). Изменение grain — типичный пример невидимого ломающего изменения смысла.
Невидимое изменение смысла: Дефект, при котором SQL компилируется и dbt-тесты проходят, но потребитель ниже по цепочке получает иной смысл строк. В веб-приложении такой дефект часто виден сразу, в данных он может попасть в отчёт, KPI или риск-модель через неделю.
Проверочный факт: Команда, SQL-запрос или ручной шаг ревьюера, которые доказывают, что dbt-модель выполнила спецификацию, а не просто прошла тесты. Без проверочного факта зелёный dbt build не равен корректной витрине.
Dbt как исполнитель обещания: Позиционирование dbt в этой книге: инструмент трансформаций, который делает поведение проверяемым, но не решает, что именно должно быть правдой для бизнеса. Аналог тестового фреймворка в обычном приложении.
Datalakehouse банка «северный мост»: Учебный проект на DuckDB, воспроизводимый локально. Включает пять витрин: mart_customer_360, mart_card_turnover_daily, mart_payment_risk_signals, mart_credit_portfolio_quality, mart_open_api_consents. Все данные синтетические.
Три базовых правила sdd data: (1) Спецификация живёт в репозитории, а не в чате. (2) Проверочный факт должен иметь команду, SQL или ручной шаг ревьюера. (3) Автор и ревьюер должны работать в разных режимах, даже если это один и тот же CLI-агент после /clear.
Pii и политика персональных данных: Класс ограничений, ради которого книга вводит спецификацию до SQL. В DataLakeHouse персональные данные, согласия и риск-сигналы — это области, где «быстрый SQL» принимает решения, на которые у него нет права.
Цепочка потребителей: Метафора, объясняющая расползание ошибки смысла: операционная команда выгружает источник, дата-инженер приводит типы, аналитик строит отчёт, риск-команда смотрит сигнал, руководитель читает агрегат. Локальная ошибка SQL превращается в системную ошибку смысла.
Smoke-тест и локальный трек: Команда bash smoke_all.sh из book3/examples/bank-lakehouse. Без dbt генерирует синтетические данные и проверяет структуру, с dbt выполняет полный dbt build. Это первый практический шаг первой части.
Дрейф контракта и ломающее изменение: Дрейф — постепенное смещение фактического поведения витрины от спецификации. Ломающее изменение — изменение grain, обязательных полей или семантики метрики, нарушающее контракт с потребителями. Оба требуют явного подтверждения владельца.
Важные даты: Этап 0: запуск smoke all.sh: Первый практический шаг: запустить локальный smoke-тест и убедиться, что DataLakeHouse воспроизводится на машине читателя.
Этап 1: фиксация режима чтения: Решение, по какому треку идёт читатель: учебный локальный на DuckDB или сопоставление с боевым контуром как справочным слоем.
Этап 2: рабочие заметки: Фиксация в markdown-файле: «Первый проход: строю локальный DataLakeHouse банка на dbt-duckdb; сопоставление с боевым контуром не внедряю до практического зачёта».
Практические упражнения: Название: Упражнение 1. Запуск локального smoke-теста DataLakeHouse
Проблема: Склонируйте репозиторий книги, перейдите в каталог book3/examples/bank-lakehouse и выполните bash smoke_all.sh. Сначала без установленного dbt — убедитесь, что скрипт генерирует синтетические данные и проверяет структуру. Затем установите зависимости (dbt-core, dbt-duckdb) и выполните полный dbt build. Зафиксируйте в заметках список созданных таблиц и время выполнения.
Решение: Шаг 1: git clone <репозиторий_книги> && cd <каталог_книги>. Шаг 2: cd book3/examples/bank-lakehouse. Шаг 3: bash smoke_all.sh — без dbt скрипт должен сгенерировать синтетические parquet/csv в каталог data/ и напечатать отчёт о структуре. Шаг 4: pip install dbt-core dbt-duckdb (или эквивалент для вашего менеджера пакетов). Шаг 5: повторный запуск bash smoke_all.sh — должен выполнить dbt build и завершиться зелёным. Шаг 6: изучите файл dbt_project.yml и каталог models/, найдите пять итоговых витрин. Запишите в заметки наблюдения о времени сборки и количестве строк в каждой mart-таблице.
Сложность: beginner
Название: Упражнение 2. Сценарий «невидимое изменение grain»
Проблема: Откройте спецификацию (или описание) витрины mart_customer_360 и письменно зафиксируйте её обещание: какой grain, какие обязательные поля, какие запрещённые поля, кто потребитель. Затем мысленно добавьте в SELECT агенту поле product_code и опишите, как именно сломается контракт с потребителем, который считал по этой витрине количество активных клиентов. Подготовьте аргумент: почему даже зелёный dbt build не защитит отчёт.
Решение: Шаг 1: запишите grain — «одна строка на customer_id». Шаг 2: перечислите обязательные поля, например: customer_id, full_name, birth_date, segment, opened_at. Шаг 3: перечислите запрещённые поля, например: card_pan, phone, email, согласия. Шаг 4: опишите потребителя — например, отчёт «Количество активных клиентов по сегментам», который выполняет select segment, count(distinct customer_id) from mart_customer_360 group by segment. Шаг 5: после добавления product_code тот же запрос без distinct начнёт считать пары клиент-продукт, а с distinct — перестанет учитывать, что у клиента несколько продуктов разных типов. Шаг 6: аргумент: dbt-тесты вроде not_null и unique на customer_id продолжат проходить, потому что customer_id остаётся уникальным (если в данных нет дубликатов клиента), но отчёт покажет завышенное число и сменит смысл.
Сложность: intermediate
Название: Упражнение 3. Проектирование проверочного факта
Проблема: Для витрины mart_card_turnover_daily напишите проверочный факт в виде SQL-запроса, который доказывает, что сумма оборотов за день в витрине совпадает с суммой оборотов в raw-источнике за тот же день, с допуском ±0.01. Запрос должен быть записан в файле tests/facts/mart_card_turnover_daily_recon.sql или аналогичном. Сформулируйте текстом, что именно он доказывает и при каком расхождении считается ломающим.
Решение: Шаг 1: определите источник — например, stg_card_transactions. Шаг 2: напишите сверку: with mart_sum as (select transaction_date, sum(amount) as mart_total from {{ ref('mart_card_turnover_daily') }} group by transaction_date), src_sum as (select transaction_date, sum(amount) as src_total from {{ source('core','card_transactions') }} group by transaction_date) select m.transaction_date, m.mart_total, s.src_total, abs(m.mart_total - s.src_total) as diff from mart_sum m join src_sum s using(transaction_date) where abs(m.mart_total - s.src_total) > 0.01. Шаг 3: пустой результат — успех. Шаг 4: текстом зафиксируйте: «Если diff > 0.01 за любой день — факт считается проваленным, релиз блокируется, владелец — риск-команда». Шаг 5: добавьте этот файл в sources.yml как data_tests или подключите в CI.
Сложность: intermediate
Название: Упражнение 4. Идентификация PII в витрине
Проблема: Откройте схему (SQL или описание) витрины mart_open_api_consents. Перечислите поля, которые вы бы классифицировали как PII или чувствительные. Сформулируйте, какие из них допустимо отдавать потребителю, какие требуют маскирования, какие запрещены полностью. Подготовьте короткий список проверок, которые должны сопровождать эту витрину.
Решение: Шаг 1: выпишите все поля. Шаг 2: классифицируйте. Например: client_id — квази-идентификатор, допустим в агрегированных разрезах; consent_id — допустим; consent_type, granted_at, expires_at — допустимы; ip_address, user_agent, device_id — запрещены или маскируются; full_name, phone, email — запрещены; reason_for_withdrawal — допустим в агрегатах. Шаг 3: для каждого класса опишите обработку. Шаг 4: сформулируйте проверки: тест на отсутствие phone/email, тест на наличие обязательных consent_id, проверка свежести витрины (не старше N часов), проверка доли revoked vs granted. Шаг 5: приложите список владельцев: client_id — владелец данных клиентский сервис, consent_id — продукт открытых API.
Сложность: intermediate
Название: Упражнение 5. Ревью в другом режиме
Проблема: Представьте, что вы написали спецификацию и dbt-модель для mart_payment_risk_signals. Теперь смоделируйте ревью «в другом режиме»: начните сессию CLI-агента с /clear, явно запретите себе менять модель и попросите только проверить соответствие спецификации. Запишите, какие вопросы вы бы задали ревьюеру и какие доказательства потребовали бы.
Решение: Шаг 1: опишите ревьюируемый артефакт — путь к спецификации, путь к модели, путь к тестам. Шаг 2: зафиксируйте запрет — «не редактируй model.sql и спецификацию, только анализируй». Шаг 3: список вопросов ревью: совпадает ли grain с обещанным; все ли обязательные поля присутствуют и не null; нет ли в SELECT запрещённых полей; проходит ли проверочный факт; есть ли расхождение с raw-источником по суммам/количеству. Шаг 4: требуемые доказательства — вывод проверочного факта, список тестов и их статус, diff dbt-проекта против main, ссылка на spec-файл с версией. Шаг 5: зафиксируйте вердикт — approved / changes_required / blocked.
Сложность: advanced
Кейсы: Название: Кейс 1. Витрина клиентов «раздулась» после добавления продукта
Сценарий: Средний банк, платформа данных на dbt + Snowflake. Витрина mart_customer_360 уже полгода обслуживает дашборд «Активные клиенты по сегментам» и отчёт регуляторной отчётности. Команда аналитиков попросила дата-инженера добавить в витрину поле product_code, чтобы не джойнить справочник продуктов на стороне BI.
Задача: Дата-инженер с AI-ассистентом добавил product_code в SELECT, прогнал dbt build, тесты на not_null и unique по customer_id прошли (потому что у одного клиента в данных был один продукт на дату снимка). Релиз прошёл в продакшн. Через неделю регуляторный отчёт стал показывать «активных клиентов» на 18% больше реального числа — у клиентов с двумя и более продуктами строки размножились. Дашборд по сегментам продолжал показывать корректные цифры, потому что там был ручной distinct.
Решение: Команда остановила релиз, откатила изменение в dbt-проекте, написала отдельный аналитический слой mart_customer_products с grain «клиент-продукт», а в mart_customer_360 оставила grain «клиент». После инцидента внедрили SDD-практику: для каждой витрины в репозитории хранится YAML-спецификация с grain, обязательными и запрещёнными полями. Каждый PR проверяется ревьюером в отдельной сессии агента, который сверяет diff со спецификацией.
Результат: Стоимость инцидента — три дня ручных пересчётов регуляторной отчётности и объяснительная в комплаенс. После внедрения SDD Data подобные изменения блокируются на этапе ревью: ревьюер замечает, что добавление поля меняет grain, и требует либо вынести в отдельный mart, либо явно описать дрейф и получить подпись владельца дашборда.
Извлечённые уроки: Зелёный dbt build не равен сохранению контракта витрины.
Grain — это часть спецификации, а не свойство SQL.
Ревью в «другом режиме» (отдельная сессия агента) ловит то, что автор пропускает из-за контекста.
Связанные концепции: Grain (зернистость) витрины
Невидимое изменение смысла
Спецификация дата-продукта
Дрейф контракта и ломающее изменение
Название: Кейс 2. Утечка PII через аналитическую витрину
Сценарий: Крупный финтех, dbt + BigQuery. Команда открытых API готовит витрину mart_open_api_consents для внешних партнёров через BI-инструмент. ИИ-ассистент по запросу «собери витрину по согласиям с максимумом полей» включил в SELECT client_email и client_phone, посчитав их «полезными для сегментации».
Задача: Партнёры начали видеть в дашборде e-mail и телефоны клиентов, хотя согласие на передачу этих данных партнёрам не было получено. Юридическая служба зафиксировала инцидент обработки персональных данных, последовал штраф и обязательство уведомить клиентов.
Решение: Витрину пересобрали с белым списком полей, описанным в спецификации. В dbt добавили тест, который падает, если в схеме появляется новое поле из «красного списка». В репозитории появилась YAML-спецификация с разделами allowed_fields и forbidden_fields. Любой PR с расширением SELECT сверяется со списком запрещённых полей в обязательном порядке.
Результат: Стоимость инцидента — регуляторный штраф, репутационные потери, пересборка партнёрского API. После внедрения SDD Data политика PII описывается до SQL, а не выясняется в суде после релиза. Спецификация живёт в репозитории, рядом с моделью, и любой участник команды видит границы.
Извлечённые уроки: «Максимум полей» — опасная инструкция для агента без спецификации.
Запрет в SQL проверяется легче, чем запрет в голове аналитика.
PII-политика — это контракт, а не best practice.
Связанные концепции: PII и политика персональных данных
Контракт данных
Спецификация дата-продукта
Три базовых правила SDD Data
Название: Кейс 3. Незаметный сдвиг freshness на платёжных риск-сигналах
Сценарий: Риск-платформа среднего банка, dbt + ClickHouse. Витрина mart_payment_risk_signals обновляется каждые 15 минут. Агент при рефакторинге DAG изменил расписание, и фактическая свежесть витрины выросла с 15 до 47 минут. dbt-тесты продолжали проходить, потому что данные были свежими в абсолютном смысле (не старше суток).
Задача: Риск-команда обнаружила сдвиг только через две недели, когда модель фрод-скоринга начала пропускать пики ночных атак: витрина просто не успевала подтянуть последние транзакции. Прямого инцидента удалось избежать, но среднее время обнаружения фрод-сигнала выросло.
Решение: В спецификацию mart_payment_risk_signals добавили явный раздел freshness с верхней границей 20 минут. В dbt-проекте появился проверочный факт, который сравнивает max(updated_at) витрины с текущим временем и падает, если разрыв больше 20 минут. Этот факт входит в обязательный набор релизных доказательств.
Результат: Стоимость — две недели «слепой зоны» и десятки часов разбора риск-аналитиками. После внедрения SDD Data сдвиги freshness обнаруживаются в течение одного релиза, а не двух недель эксплуатации. Freshness — это часть контракта, а не операционная деталь.
Извлечённые уроки: Freshness — это семантическое обещание, а не SLA в ITSM.
Тест «не старше суток» не защищает бизнес-критичные витрины.
Проверочный факт freshness должен жить в репозитории рядом со спецификацией.
Связанные концепции: Проверочный факт
Спецификация дата-продукта
Дрейф контракта и ломающее изменение
Цепочка потребителей
Советы по изучению: Не пытайтесь сразу заменить DuckDB на боевой стек. В первом проходе важнее увидеть связь «спецификация → dbt → проверочный факт», а не собрать production-grade инфраструктуру.
При чтении держите открытыми две вещи: текст главы и models/ в локальном проекте. Каждое утверждение из теории проверяйте соответствующим файлом в репозитории.
Перед тем как просить агента написать SQL, напишите сами 5–10 строк спецификации: grain, обязательные поля, запрещённые поля, потребитель, freshness. Это упражнение быстрее всего показывает разницу между SDD Data и обычным dbt-курсом.
Заведите отдельный файл notes/sdd-data-checks.md и складывайте туда контрольные вопросы из каждой части. Перед стартом следующей главы возвращайтесь к нему и пробуйте ответить без подсказок.
При работе с агентом всегда открывайте новую сессию (/clear) для роли ревьюера. Автор и ревьюер должны работать в разных режимах — это правило №3, и оно экономит часы разбора.
Запускайте bash smoke_all.sh после каждого изменения. Если smoke-тест стал жёлтым или красным, не правьте dbt-модель «наугад» — сначала перечитайте спецификацию и проверочный факт.
Перечитывайте раздел «Первый риск: невидимое изменение смысла» перед каждой новой частью. Этот сценарий повторяется в разных формах, и его легко забыть, утонув в деталях marts.
Не смешивайте учебный трек и боевой контур. Если у вас есть рабочий dbt-проект, сопоставляйте с ним как со справочным слоем, но не пытайтесь «применить» SDD Data к продакшну в первом проходе.
Дополнительные ресурсы: Репозиторий книги, каталог book3/examples/bank-lakehouse: Содержит dbt-проект DataLakeHouse банка «Северный Мост», синтетические данные и скрипт smoke_all.sh. Это основной практический артефакт курса.
Документация dbt (dbt labs): Справочник по моделям, тестам, sources, exposures и макросам. Используйте как словарь, а не как учебник — концептуальная рамка даётся в книге.
Dbt-utils и dbt-expectations: Пакеты с готовыми тестами, полезные для построения проверочных фактов (expect_column_values_to_be_between, recency, свежесть).
Первый том серии (sdd для веб-приложений): Источник методологии SDD. Если вы пропустили первый том, прочитайте вводную главу — цикл «описать → специфицировать → реализовать → проверить → ревью» един для обоих томов.
Чек-лист спецификации дата-продукта (шаблон в book3/templates/): YAML/JSON-шаблон с разделами grain, обязательные/запрещённые поля, freshness, владельцы, проверочные факты. Скопируйте и адаптируйте под свой проект.
Стандарты регуляторной отчётности цб рф (152-фз, положения по иб): Контекст, в котором живёт DataLakeHouse банка. Не как юридическая инструкция, а как источник ограничений, которые делают спецификацию обязательной.
Резюме: Часть 1 вводит методологию SDD Data и фиксирует её ключевой тезис: в дата-платформе смысл важнее кода. Агент может незаметно изменить grain, метрику, freshness, политику PII или совместимость с потребителями, и зелёный dbt build этого не поймает. Поэтому порядок работы противоположен обычному dbt-курсу: сначала миссия платформы, затем спецификация дата-продукта и контракт, затем raw/staging, затем marts, затем проверочные факты и независимое ревью. dbt остаётся основным инструментом трансформаций, но источником смысла является спецификация, живущая в репозитории. Три правила нужно помнить с первой главы: спецификация живёт в репозитории, проверочный факт имеет команду или шаг ревьюера, автор и ревьюер работают в разных режимах. Учебный проект — DataLakeHouse банка «Северный Мост» на DuckDB, который собирает пять витрин (mart_customer_360, mart_card_turnover_daily, mart_payment_risk_signals, mart_credit_portfolio_quality, mart_open_api_consents). Минимальный выход части — выбрать режим чтения, запустить локальный smoke-тест и зафиксировать в заметках, что книга строит один воспроизводимый процесс дата-продукта, а не «банк вообще».