Метрики как код: бизнес‑логика в dbt вместо настроек BI
В начале немного контекста. Я — аналитик данных в команде BIGDATAHOUSE, и на всех проектах трансформацию данных реализую через dbt — де‑факто стандарт, в качестве BI в основном использую Superset. Бизнес‑логика в таком стеке оказывается разбита по двум местам: одна её часть оседает в dbt, другая описывается в BI. Со временем эти две части неизбежно расходятся, и отследить такие расхождения становится всё сложнее. Из профессионального интереса изучая альтернативные BI‑платформы, я наткнулся на Lightdash. Он обещает закрыть эту и ряд других проблем.
Теперь представьте ситуацию (возможно некоторым и представлять не нужно, достаточно вспомнить рабочие будни): на созвоне кто‑то задаёт вопрос «а какая у нас выручка за квартал?», и в этот момент одновременно озвучивают три разных цифры. Одна из дашборда в Superset, вторая из выгрузки CSV файла, а третья из SQL запроса к базе данных. И дальше команда обсуждает не бизнес задачу, а то, какая цифра является корректной. Но проблема может быть даже не в ошибке расчётов, а в том, что каждый держит в голове своё определение выручки.
Эту неоднозначность закрывают инструменты семантического слоя: метрика описывается один раз в коде (об этом чуть позже), и везде — от ad‑hoc запросов до дашбордов — используется одно и то же определение.
Одним из таких инструментов и является Lightdash — BI‑платформа с открытым исходным кодом, предоставляющая возможность нетехническим пользователям самостоятельно исследовать показатели, строить визуализации и дашборды (ведь метрики и измерения описывают аналитики и дата‑инженеры, а дальше в BI работают уже все желающие, например, менеджеры или руководители).
Главная особенность Lightdash как раз в том, что он изначально строится вокруг dbt и семантического слоя. Вместо того чтобы реализовывать ещё один слой моделирования в BI‑инструменте, Lightdash берёт dbt‑модели и YAML‑определения метрик как единственный источник истины. Метрики и модели версионируются, любые изменения проходят ревью, их можно легко откатить, покрыть тестами. Благодаря этому Lightdash наследует все ключевые преимущества dbt и переносит их в пласт BI.
Таким образом, платформа ощущается как естественное продолжение современного дата‑стэка, а не обособленная система, где данные нужно проектировать заново.
От вопроса до чарта за несколько кликов
Начну не с теории, а с демонстрации простого сценария: допустим, перед вами стоит задача узнать, сколько было клиентов и какой объём денежных средств они принесли за определённый период времени.
При иных обстоятельствах приходится обращаться к аналитикам и ожидать их ответа в виде графика или выгрузки в другом формате. В Lightdash это происходит следующим образом: вы открываете список таблиц (на самом деле это dbt‑модели с описанием метаданных в формате YAML), выбираете необходимую для исследования и попадаете в раздел «Explore» — слева панель с готовыми измерениями и метриками, справа — пустой холст. Не нужно писать ни строчки SQL‑кода, визуализация строится по принципу drag‑and‑drop, далее настраиваете фильтры, внешний вид графика (кастомизация, кстати, в Lightdash держит достойный уровень и заслуживает отдельного обзора разнообразия визуализаций в целом и их «продвинутых» настроек) и получаете ответ на бизнес‑вопрос, а если запросов несколько — чарты собираются в дашборд.
Анатомия YAML‑файла
models: - name: customers config: meta: primary_key: customer_id joins: - join: orders sql_on: ${customers.customer_id} = ${orders.customer_id} relationship: one-to-many - join: payments sql_on: ${orders.order_id} = ${payments.order_id} relationship: one-to-many columns: - name: customer_id description: This is a unique identifier for a customer tests: - unique - not_null config: meta: dimension: type: number metrics: unique_customer_count: tags: ["period_metric"] type: count_distinct label: Unique customer count description: Total number of customers spotlight: categories: ["core", "revenue_growth"] total_order_amount_deduped: type: sum_distinct sql: ${orders.amount} distinct_keys: [orders.order_id] format: usd round: 2 - name: created description: Timestamp (UTC) when customer was created config: meta: dimension: type: timestamp metrics: date_of_first_created_customer: type: min date_of_most_recent_created_customer: type: max
Буквально пара слов про сам dbt — это инструмент трансформации данных в хранилище: вы пишете SQL‑запросы (модели), а dbt сам понимает порядок их выполнения, материализует результаты, тестирует и генерирует документацию. С SQL‑моделями сосуществуют YAML‑файлы с метаданными.
Прежде чем разбирать, почему график собрался за пару кликов, стоит понять одну неочевидную вещь про файл выше — это файл из dbt‑проекта, но не весь он «про dbt».
dbt из этого YAML знает не все инструкции: models, name, description, columns, tests (unique, not_null), config — это стандартные свойства модели, которые dbt читает, валидирует и использует для генерации документации и тестов. А вот всё, что лежит внутри конфига meta — dimension, metrics, joins — dbt не интерпретирует вообще. Для него meta — это произвольный словарь, который он пробрасывает дальше, не заглядывая внутрь.
Именно здесь и появляется Lightdash. Он читает тот же файл, но смотрит в meta и достаёт оттуда свою семантику: метрики, измерения и связи, YAML остаётся полностью валидным dbt‑файлом, проект собирается и тестируется как обычно, а BI‑логика живёт рядом, ничего не нарушая.
Обратите внимание на блок joins в конфиге — там ответ на вопрос «почему простой drag‑and‑drop корректно обрабатывает композитные метрики». Заметьте две инструкции, обе снимают с конечного пользователя работу, которую иначе пришлось бы реализовывать на SQL:
sql_on— условие соединения (ONизJOIN). Заполняется один раз и в дальнейшем любые чарты, где потребуется соединение таблицыcustomersсordersилиpaymentsбудут объединены именно по этому правилу;relationshipsопределяет мощность связи, например, связьcustomers→ordersобозначена как one‑to‑many, то есть одному клиенту соответствует много заказов.
И тут стоит заметить, что метрика total_order_amount_deduped с distinct_keys нужна именно потому, что связь customers → orders → payments множит строки, но теперь Lightdash об этом осведомлён и считает сумму корректно.
Коротко о блоке metrics:
unique_customer_count—count_distinctпоcustomer_id= кол‑ву уникальных клиентов;total_order_amount_deduped—sum_distinctсdistinct_keys= дедуплицированной сумме заказов.
Т.е. левая панель в UI содержит не сырые колонки, а готовые агрегации с предопределёнными типами. Вы выбираете «что» посчитать, а «как» — определено в YAML файлах.
Измерения — это то, по каким атрибутам вы группируете. Описали колонку в YAML (в примере выше — created — дата создания пользователя), и она доступна в BI, с соответствующим типом данных, от которого зависят доступные фильтры.
Чем ещё выделяется Lightdash?
Drill‑down до сырых записей
В Lightdash любой агрегат можно проверить, к примеру, чтобы понять, откуда на графике резкий выброс значений. Для этого достаточно кликнуть по элементу визуализации (столбцу, точке, ячейке таблицы) и выбрать «View underlying data» — на скриншотах ниже это показано на конкретном примере: круговая диаграмма с распределением суммы заказов по странам, кликаем по US. Видно меню и открывшуюся следом таблицу сырых строк, собранную с учётом тех же фильтров и группировок, что и на графике.


Table Calculation
Основные метрики живут в YAML и они могут быть сколь угодно сложными. Но заводить новый показатель в коде ради одной цифры в отчёте или проверки гипотезы может быть избыточно.
Для таких разовых запросов и существуют вычисляемые поля, которые создаются прямо в чарте поверх уже выбранных метрик: доли, накопительные итоги, ранги, изменения к прошлому периоду и так далее.
Наглядный пример — средний чек в разрезе стран. В чарте по country_orders уже выбраны total_order_amount и order_count, и этого достаточно: добавляете table calculation @{total_order_amount} / @{order_count} — и рядом с суммой и количеством заказов появляется новая колонка со средним чеком по каждой стране.


Версионирование чартов
Lightdash хранит историю версий каждого чарта: можно посмотреть, что и когда менялось, и откатиться к любой предыдущей версии — отличная страховка для командной работы.

RLS и CLS с помощью пользовательских атрибутов
Доступ в Lightdash можно ограничивать не только на уровне проекта, но и на уровне отдельных полей и метрик. В YAML для этого прописывается условие доступа — блок required_attributes. Например, так чувствительное поле скрывается от всех, кроме администраторов:
columns: - name: age meta: dimension: required_attributes: is_admin: "true"
Пользователь без указанного значения (is_admin: "true") это поле просто не увидит — вместе со всеми метриками, которые от него зависят. Если условий несколько, required_attributes работает по логике 'И', а конструкция any_attributes — по логике 'ИЛИ'.
Сами пользовательские атрибуты (user attributes) при этом создаёт админ через интерфейс и назначает значения конкретным пользователям или группам. В YAML вы лишь ссылаетесь на нужное условие — кому и какое значение присвоено, решается в UI.
А если ограничивать нужно не колонки, а строки, есть конструкция sql_filter, которая добавляет условие прямо в WHERE запроса к хранилищу.
Получается row/column‑level security, описанная в коде, рядом с самими данными.
Развитый Developer Experience
Lightdash умеет поднимать превью проекты и валидировать контент через CI/CD, и для команды разработки это удобный паттерн, заметно отличающий Lightdash от других BI‑инструментов.
Превью проект — это временная копия продакшн проекта: чарты и дашборды копируются из продакшна и можно безопасно экспериментировать с метриками, измерениями и контентом. Настроив GitHub Actions, вы получаете такой проект автоматически на каждый пулл‑реквест, при каждом новом коммите превью обновляется, а после мержа или закрытия PR удаляется.
Теперь про валидацию контента: команда lightdash validate проверяет, не нарушают ли внесённые изменения существующие чарты и дашборды. В CI процессе эту команду используют как триггер перед принятием изменений: при обнаружении ошибок правки не попадут в основную ветку, пока проверка не завершится успешно. Результат валидации доступен и в интерфейсе: в настройках проекта есть вкладка «Validator» со списком повреждённых чартов и дашбордов и причинами ошибок. Стоит учитывать ограничение: валидатор проверяет ссылки и определения метрик и измерений, но не исполняемый SQL, поэтому ошибку в синтаксисе запроса он не обнаружит.
Помимо этого, Lightdash делает контент управляемым через код наравне с моделями: чарты и дашборды выгружаются в YAML, правятся и загружаются обратно через командную строку — в результате визуализации версионируются и проходят ревью так же, как dbt‑модели. dbt write‑back работает в обратную сторону: новое измерение создаётся прямо в интерфейсе Lightdash, а его YAML‑описание отправляется запросом на слияние в репозиторий dbt.
Заключение
В этой части я постарался показать Lightdash с двух сторон. Сначала — как идею: бизнес‑логика живёт в dbt, а BI лишь читает её из YAML, не заводя собственного слоя моделирования. Затем — как инструмент: разобрал устройство YAML‑файла, drill‑down до сырых строк, вычисляемые поля, версионирование чартов, управление доступом через пользовательские атрибуты, а также превью‑проекты и валидацию контента.
Уместить всё в одну статью не получилось бы: только библиотека визуализаций и их настройки тянут на отдельный разбор. Поэтому материал выходит порционно.
Во второй части я планирую:
собрать полноценный дашборд и разобрать его фильтры и оформление;
пройтись по библиотеке визуализаций и их кастомизации, включая кастомные чарты на Vega‑Lite;
объективно разобрать недостатки;
сравнить облачную версию и self‑hosted;
подвести итог: кому инструмент подойдёт, а кому разумнее остаться на привычном стеке.
А пока можно самостоятельно пощупать Lightdash: есть публичное демо, где без установки и собственного dbt‑проекта доступны примеры визуализаций и запросов. Полноценного опыта «метрик как кода» оно не даст, но для первого знакомства этого достаточно.

