MiddleData Analyst > Метрики и эксперименты > A/B-тесты
Короткий ответ Это пример парадокса Симпсона: агрегированный результат может разворачиваться относительно сегментных из-за разной доли и базовой конверсии сегментов в группах — часто это признак нарушения баланса рандомизации (SRM на уровне сегмента) или смешения с ковариатой, которая по-разному распределена между группами. Как это работает подробнее Как это работает: если, например, в группе B доля пользователей с изначально высокой конверсией (скажем, iOS) случайно или систематически выше, чем в группе A, общая взвешенная конверсия B может оказаться выше даже при том, что внутри каждого сегмента B не лучше или хуже A. Синтетический пример: A: iOS 1000 пользователей (CR 10%), Android 9000 (CR 2%) → общая CR_A=2.8%. B: iOS 9000 (CR 9%, то есть хуже чем в A), Android 1000 (CR 1%, хуже чем в A) → общая CR_B = (9000*0.09+1000*0.01)/10000=8.2%. Общая B выше A, хотя в каждом сегменте B хуже — из-за сильно разного состава сегментов между группами (явный признак проблемы с рандомизацией). Что использовать на практик
ab-testingmetrics
MiddlePerformance Marketer > Google Ads > Аудитории и ретаргетинг
Короткий ответ Нужно сопоставить уровень пересечения с динамикой CPM обоих сетов за один период: если CPM растёт у обоих при высоком overlap, они конкурируют друг с другом в одном аукционе, и решением обычно является объединение сетов в один или явное разграничение через взаимные исключения. Как это работает подробнее При высоком пересечении аудиторий система показывает рекламу одному и тому же пользователю от имени двух разных ad set'ов одного рекламодателя, которые формально конкурируют между собой в аукционе платформы, что может завышать эффективную цену показа без прироста уникального охвата. Что использовать на практике На практике. После подтверждения проблемы через инструмент Audience Overlap сравнивают суммарный результат объединённого сета (при объединении через CBO или единый ad set) с суммой раздельных результатов за сопоставимый период, чтобы оценить реальный выигрыш от устранения внутренней конкуренции. Пример. Overlap 65% между «Посетители сайта 7 дней» и «Вовлечённость в Instagram 14 дней», оба
ad-platformsaudience-targeting
SeniorData Analyst > SQL и данные > SQL
Короткий ответ Сформулируйте SLA свежести для каждого решения: оперативному мониторингу могут требоваться минуты, а отчёту руководства — сутки. Обновление каждые пять минут не создаёт ценности, если данные используются один раз утром. Как это работает подробнее В BigQuery стоимость on-demand в основном определяется сканируемыми байтами. Оцените dry run, историю jobs и объём партиций, затем сравните сценарии расписания в месячном бюджете. Разделите тяжёлую подготовку и лёгкий serving-слой: инкрементально обновляйте агрегаты по новым партициям, а дашборд пусть читает небольшую таблицу. Кэширование и materialized views полезны, если подходят ограничениям запроса. Пример кода Что использовать на практике Считайте сценарии. Покажите стоимость обновления раз в сутки, час и пятнадцать минут. Ограничивайте сканирование. Фильтруйте партиции, избегайте SELECT * и пересчитывайте только изменившиеся окна. Пересматривайте SLA. Рост таблиц меняет цену даже при неизменном расписании. Подводные камни Свежесть без потребителя
bigquerycost-optimization
MiddleData Analyst > SQL и данные > SQL
Короткий ответ В BigQuery стоимость запроса обычно зависит от объёма прочитанных данных, поэтому главные рычаги — партиционирование таблицы по дате и фильтрация именно по партиционирующему столбцу, кластеризация по часто используемым столбцам фильтра, а также явный выбор нужных столбцов вместо SELECT *. Как это работает подробнее Как это работает: BigQuery хранит данные колоночно и по умолчанию тарифицирует запрос по объёму прочитанных байт (в модели on-demand); партиционирование по дате позволяет движку физически пропускать партиции, не попадающие под фильтр WHERE event_date BETWEEN ..., а кластеризация внутри партиции ускоряет и дешевит фильтрацию по дополнительным столбцам (например, user_id, event_type). Пример кода Что использовать на практике На практике: перед запуском дорогого запроса стоит использовать режим предварительной оценки (dry run / предпросмотр объёма обрабатываемых байт в интерфейсе), чтобы увидеть стоимость до фактического выполнения. Выбор конкретных столбцов вместо SELECT * в колоночном
bigqueryclustering
MiddleDevOps Engineer > Контейнеры и доставка > Скриптинг и автоматизация
Короткий ответ Нужно сначала разделить причины — сетевые ограничения CI-окружения (другой регион, прокси, фаервол), ограниченные ресурсы раннера или реальная нестабильность самого внешнего API — собрав логи времени ответа и кодов ошибок, а затем добавить явный таймаут, ретраи с экспоненциальной задержкой и чёткое логирование вместо "тихого" падения теста. Как это работает подробнее CI-раннеры часто находятся в другом сетевом окружении (другой дата-центр/регион, общие с другими джобами ресурсы, возможные ограничения на исходящие соединения), из-за чего задержки и процент ошибок выше, чем на машине разработчика; "флаки" тест обычно означает, что система балансирует на грани таймаута, а не что тест написан неверно сам по себе. Пример кода Что использовать на практике Рекомендация. диагностику начинают со сбора данных — логировать время запроса, код ответа и количество попыток при каждом падении в CI, запустить тест несколько десятков раз подряд в CI, чтобы оценить частоту сбоя. После этого добавляют timeout, ret
ciobservability
MiddleDevOps Engineer > Контейнеры и доставка > Безопасность и DevSecOps
Короткий ответ Сначала немедленно ротируют (меняют) скомпрометированный пароль в самой базе данных и во всех местах, где он использовался, затем оценивают, кто мог получить доступ к репозиторию за период утечки, и только после этого очищают историю Git — порядок важен, потому что очистка истории без ротации не устраняет реальную угрозу. Как это работает подробнее удаление файла новым коммитом не удаляет секрет из истории Git — он остаётся доступен через старые коммиты, форки, локальные клоны у разработчиков и кэши CI. Поэтому единственная гарантированная защита — сделать утёкший пароль бесполезным, сменив его в системе, где он применяется. Пример кода Что использовать на практике Практика. порядок действий — 1) сменить пароль/ключ в БД или сервисе; 2) обновить secret-хранилище и перезапустить сервисы, использующие его; 3) проверить логи доступа к БД за период утечки на подозрительную активность; 4) очистить историю репозитория (git filter-repo/BFG) и уведомить всех с локальными клонами о необходимости их пере
credential-rotationgit
MiddleData Analyst > SQL и данные > SQL
Короткий ответ Нужно либо использовать модификатор FINAL в запросе (SELECT ... FROM table FINAL), который принудительно схлопывает дубликаты по ключу сортировки на момент чтения, либо явно дедуплицировать через ROW_NUMBER()-подобную логику (argMax/LIMIT 1 BY в ClickHouse) без FINAL, если производительность важнее. Как это работает подробнее Как это работает: ReplacingMergeTree асинхронно «склеивает» строки с одинаковым ключом сортировки в фоновых слияниях партиций, оставляя версию с наибольшим значением указанного столбца версии; до завершения слияния в таблице физически могут существовать несколько версий одной логической записи, и обычный SELECT без FINAL увидит их все. Пример кода Что использовать на практике На практике: FINAL даёт корректный результат, но может быть существенно медленнее на больших таблицах, так как требует дополнительной работы по слиянию «на лету» во время запроса. Альтернатива — агрегировать с LIMIT 1 BY id ORDER BY version DESC или использовать argMax(column, version) в GROUP BY id,
argmaxclickhouse
MiddleData Analyst > SQL и данные > Качество данных
Короткий ответ Нужно явно решить и задокументировать стратегию для исторических строк — либо оставить payment_method = NULL с пометкой «данные недоступны до 2024-03-01», либо восстановить значение из альтернативного источника, если он есть; «додумывать» значение нельзя. Как это работает подробнее Backfill — это процесс заполнения нового поля или таблицы историческими значениями; для него нужно либо повторно извлечь исторические данные из источника (если там поле действительно существовало, но не экспортировалось), либо честно признать отсутствие данных за период. Пример кода Что использовать на практике На практике. Перед backfill стоит проверить у владельца источника, существовало ли значение payment_method исторически технически (может быть доступно через архивные логи или другую таблицу), и только потом либо восстанавливать, либо помечать период как «неприменимо»; в любом случае дата появления поля должна быть задокументирована в data dictionary. Проверка. SELECT MIN(order_date) FROM fact_orders WHERE paym
backfilldata-quality
JuniorDevOps Engineer > Контейнеры и доставка > CI/CD и управление релизами
Короткий ответ Джоба запустится только тогда, когда пайплайн выполняется для коммита в ветке `main` (например, после прямого push или merge); она НЕ запустится для merge request из feature-ветки, потому что переменная `CI_COMMIT_BRANCH` в контексте MR-пайплайна не равна `main`. Как это работает подробнее `rules` в GitLab CI вычисляются до запуска джобы и решают, добавлять её в пайплайн или нет. `CI_COMMIT_BRANCH` заполняется только для обычных (branch) пайплайнов; в pipeline, запущенном для merge request (`merge_request_event`), используется другая переменная — `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME`, а `CI_COMMIT_BRANCH` может быть пустой. Пример кода Что использовать на практике На практике. это правило типично для джоб деплоя, которые должны срабатывать только после того, как изменения попали в основную ветку, а не на этапе проверки pull request. Стоит явно тестировать поведение в интерфейсе CI/CD → Pipelines, проверяя, появляется ли джоба в разных типах пайплайнов. Пример. Ожидаемое поведение: при `git push
artifactscicd-and-release-management
MiddleDevOps Engineer > Контейнеры и доставка > Безопасность и DevSecOps
Короткий ответ По умолчанию в Kubernetes сетевой трафик между Pod'ами не ограничен, если не применены NetworkPolicy и CNI-плагин, который их поддерживает; нужно проверить, поддерживает ли используемый CNI NetworkPolicy, и явно создать политику, разрешающую входящий трафик к Pod'ам БД только от Pod'ов приложения. Как это работает подробнее NetworkPolicy — это «allow-list» поверх сети, которая без политик полностью открыта (flat network) для трафика между всеми Pod'ами кластера. Если ни одна NetworkPolicy не выбирает Pod, трафик к нему не ограничен независимо от namespace. Важно: сами объекты NetworkPolicy ничего не делают, если установленный CNI-плагин (например, Flannel в базовой конфигурации) не реализует их применение — нужен CNI с поддержкой (Calico, Cilium, большинство managed-решений облаков). Пример кода Что использовать на практике Практика. сначала проверяют, что CNI поддерживает NetworkPolicy (тестовой политикой deny-all и проверкой, действительно ли трафик блокируется), затем вводят политику «defaul
kubernetesnamespaces