Data Quality Controls & Observability — DE Interview PrepКонтроль качества данных и мониторинг — Подготовка к DE-интервью

Fast-revision cheatsheet for Data Engineering interviews. Tailored for Senior/Lead DE.

Шпаргалка для быстрого повторения перед Data Engineering интервью. Заточена под Senior/Lead DE.

Core theme: embed quality controls directly into pipelines, validation, monitoring, schema-change handling, exception handling, integrity, alerting, observability, remediation.

Ключевая тема: встраивать контроли качества напрямую в пайплайны, валидация, мониторинг, обработка изменений схем, обработка исключений, целостность, алерты, observability, восстановление.

Amal Imangulov · Senior/Lead DE · 10 yrsAmal Imangulov · Senior/Lead DE · 10 лет Anchor: 6.2B-record entity-resolution fix @ MetaЯкорь: исправление entity-resolution на 6.2B записях @ Meta Anchor: DQ monitors on 3.6B events/dayЯкорь: DQ-мониторы на 3.6B событий/день Anchor: dbt tests @ Career.ioЯкорь: dbt-тесты @ Career.io

📐Dimensions of Data QualityИзмерения качества данных

Quality is multi-dimensional. Name them precisely; each maps to a different test. For economics data (rates, CPI prints, GDP revisions, FX), timeliness + accuracy + consistency dominate because downstream terminals trade on it.

Качество — многомерно. Называй их точно; каждое соответствует отдельному тесту. Для экономических данных (ставки, CPI, GDP-ревизии, FX) доминируют свежесть + точность + согласованность, потому что терминалы торгуют на основе этих данных.

DimensionQuestion it answersConcrete checkEcon-data example
AccuracyDoes the value match reality / source of truth?Reconcile vs authoritative source; checksumCPI YoY matches official release
CompletenessAre all expected rows/fields present?Row-count vs expected, NULL ratio, missing partitionsAll 50 states present in employment feed
Timeliness / FreshnessDid data arrive within the SLA window?max(event_time) vs now; partition arrival lagFX tick < 2s old; daily print by 08:31 ET
ConsistencyDo related sources/copies agree?Cross-table reconciliation; sum-to-totalSum of components = headline GDP
ValidityDoes it conform to format / range / type?Schema + regex + range/domain constraintsrate ∈ [-5%, 30%]; ISO-4217 currency code
UniquenessAre there duplicates / broken keys?PK dedup, distinct-count, no dup grainOne row per (series_id, date)
ИзмерениеНа какой вопрос отвечаетКонкретная проверкаПример (экон. данные)
Точность (Accuracy)Совпадает ли значение с реальностью / источником истины?Сверка с авторитетным источником; чексуммыCPI YoY совпадает с официальным релизом
Полнота (Completeness)Все ли ожидаемые строки/поля присутствуют?Число строк vs ожидаемое, доля NULL, отсутствующие партицииВсе 50 штатов в фиде по занятости
Своевременность / Свежесть (Timeliness / Freshness)Данные прибыли в пределах SLA-окна?max(event_time) vs now; задержка партицииFX-тик < 2s; дневная публикация к 08:31 ET
Согласованность (Consistency)Согласованы ли связанные источники/копии?Кросс-табличная сверка; сумма по компонентамСумма компонентов = headline GDP
Корректность (Validity)Соответствует ли формату / диапазону / типу?Схема + regex + ограничения по диапазону/доменуrate ∈ [-5%, 30%]; код валюты ISO-4217
Уникальность (Uniqueness)Есть ли дубликаты / сломанные ключи?Дедуп PK, distinct-count, отсутствие дублей по grainОдна строка на (series_id, date)
Freshness sits at the intersection of quality (a stale value is "wrong") and observability (it's a monitored signal). Expect interviewers to care a lot: market data has hard SLAs.
Свежесть находится на пересечении качества (устаревшее значение — «неправильное») и мониторинга (это отслеживаемый сигнал). Ожидай, что интервьюеры будут заботиться об этом: рыночные данные имеют жёсткие SLA.
"Your CPI value is correct but arrives 4 hours late — is that a data quality problem?" Answer: yes, it fails timeliness; quality is fitness-for-use, and late market data is unusable. Tie freshness SLAs to business consequence.
«Ваш CPI-показатель правильный, но прибыл на 4 часа позже — это проблема качества данных?» Ответ: да, это провал по своевременности; качество — это пригодность для использования, а запоздавшие рыночные данные непригодны. Привязывай freshness-SLA к бизнес-последствиям.

🔭Data Quality vs ObservabilityКачество данных vs мониторинг (Observability)

Data Quality (DQ)Качество данных (DQ)

  • Known-unknowns. You assert rules you already expect to hold.
  • Deterministic, declarative tests: "rate must be in range", "PK is unique".
  • Catches violations of defined expectations. Pass/fail.
  • Tooling: Great Expectations, dbt tests, Soda, custom assertions.
  • Известные-неизвестные. Ты формулируешь правила, которые, как ожидаешь, должны выполняться.
  • Детерминированные, декларативные тесты: «ставка должна быть в диапазоне», «PK уникальный».
  • Ловит нарушения определённых ожиданий. Прошёл/провалился.
  • Инструменты: Great Expectations, dbt tests, Soda, кастомные утверждения.

ObservabilityМониторинг (Observability)

  • Unknown-unknowns. Surfaces issues you didn't think to test.
  • Statistical/ML baselines + metadata over time (trends, anomalies).
  • Catches drift, silent breakage, upstream surprises. Detect + diagnose + alert.
  • Tooling: Monte Carlo, Anomalo, custom metric stores + ODS/dashboards.
  • Неизвестные-неизвестные. Выявляет проблемы, о которых ты не подумал протестировать.
  • Статистические/ML-базовые линии + метаданные во времени (тренды, аномалии).
  • Ловит дрифт, тихие поломки, сюрпризы апстрима. Обнаружить + диагностировать + алерт.
  • Инструменты: Monte Carlo, Anomalo, кастомные метрик-сторы + ODS/дашборды.
One-liner for the room: "Quality tells me whether the data meets rules I wrote. Observability tells me when something changed that I never wrote a rule for. You need both: tests for the cliffs you know about, observability for the ones you don't."
Фраза для комнаты: «Качество говорит мне, соответствует ли данные правилам, которые я написал. Мониторинг говорит мне, когда что-то изменилось, на что у меня нет правила. Нужны оба: тесты для обрывов, о которых знаешь, мониторинг для тех, о которых не знаешь».
Don't conflate them. A pipeline can be 100% "green" on dbt tests and still be silently broken (e.g., an upstream filter dropped 30% of rows but every surviving row is valid). Volume/freshness observability catches that; row-level tests don't.
Не путай их. Пайплайн может быть 100% «зелёным» по dbt-тестам и всё равно тихо сломан (например, апстрим-фильтр выкинул 30% строк, но каждая выжившая строка валидна). Volume/freshness мониторинг это поймает; построчные тесты нет.
My 6.2B-record entity-resolution defect was an observability catch first, then a quality fix: the row-level data looked valid, but a distribution shift (cluster-size histogram) and a uniqueness reconciliation revealed mis-merged entities. I then added explicit assertions so it became a known-unknown forever after.
Мой дефект entity-resolution на 6.2B записей был сначала мониторинг-обнаружением, затем quality-исправлением: построчные данные выглядели валидными, но сдвиг распределения (гистограмма размеров кластеров) и сверка уникальности выявили неправильно слитые сущности. Затем я добавил явные утверждения, чтобы это навсегда стало известным-неизвестным.

🏛️The 5 Pillars of Data Observability5 столпов мониторинга данных (Data Observability)

Memorize these five — interviewers love the framework (popularized by Monte Carlo). Each is a class of monitored signal on metadata, not on individual values.

Запомни эти пять — интервьюеры любят этот фреймворк (популяризован Monte Carlo). Каждый — это класс отслеживаемого сигнала на метаданных, а не на отдельных значениях.

1. Freshness1. Свежесть (Freshness)

How recent is the data? Is the table updating on schedule?

Насколько свежие данные? Обновляется ли таблица по расписанию?

Signal: now() - max(updated_at), partition arrival time vs SLA.

Сигнал: now() - max(updated_at), время прибытия партиции vs SLA.

2. Volume2. Объём (Volume)

Did the expected number of rows land?

Прибыло ли ожидаемое число строк?

Signal: row counts vs historical baseline / day-of-week seasonality. Catches drops & floods.

Сигнал: число строк vs исторический базис / сезонность по дню недели. Ловит падения и всплески.

3. Schema3. Схема (Schema)

Did the structure change? Columns added/removed/retyped?

Изменилась ли структура? Колонки добавлены/удалены/переопределены по типу?

Signal: schema diff vs registered contract; type/order/nullability changes.

Сигнал: schema diff vs зарегистрированный контракт; изменения типа/порядка/nullability.

4. Distribution4. Распределение (Distribution)

Are the values still in the expected statistical shape?

Значения всё ещё в ожидаемой статистической форме?

Signal: NULL rate, min/max, mean, cardinality, % in enum, drift (PSI/KS).

Сигнал: доля NULL, min/max, mean, кардинальность, % в enum, дрифт (PSI/KS).

5. Lineage5. Линейдж (Lineage)

What's upstream/downstream? When something breaks, what's the blast radius and root cause?

Что апстрим/даунстрим? Когда что-то ломается, каков радиус поражения и первопричина?

Signal: column- and table-level dependency graph; map an alert to impacted dashboards/consumers and to the upstream source that changed.

Сигнал: граф зависимостей на уровне колонок и таблиц; сопоставить алерт с затронутыми дашбордами/консьюмерами и с изменившимся апстрим-источником.

"You get a freshness alert. Walk me through triage." → Use lineage to find the upstream that stalled, check its own freshness/volume, confirm whether it's a source delay or a job failure, assess downstream blast radius, decide circuit-break vs. serve-stale-with-flag, then notify owners with severity.
«Ты получил freshness-алерт. Проведи меня по триажу». → Используй lineage, чтобы найти апстрим, который застопорился, проверь его собственный freshness/volume, подтверди, задержка ли это источника или провал задачи, оцени даунстрим-радиус поражения, реши circuit-break vs. serve-stale-with-flag, затем уведоми владельцев с указанием severity.

Validation PatternsПаттерны валидации

Schema validationВалидация схемы

Enforce column presence, types, nullability, order before processing. First line of defense; cheap and catches the loudest breakages.

Проверяй наличие колонок, типы, nullability, порядок перед обработкой. Первая линия защиты; дёшево и ловит самые громкие поломки.

# Great Expectations style
expect_table_columns_to_match_ordered_list(["series_id","date","value"])
expect_column_values_to_be_of_type("value", "DOUBLE")
expect_column_values_to_not_be_null("series_id")
# Great Expectations стиль
expect_table_columns_to_match_ordered_list(["series_id","date","value"])
expect_column_values_to_be_of_type("value", "DOUBLE")
expect_column_values_to_not_be_null("series_id")
Constraint / business-rule checksПроверка ограничений / бизнес-правил

Domain logic: ranges, enums, conditional rules, cross-field invariants.

Доменная логика: диапазоны, enum, условные правила, кросс-полевые инварианты.

-- dbt-style singular test: rate must be sane and components consistent
SELECT series_id, date
FROM {{ ref('fct_econ_series') }}
WHERE value NOT BETWEEN -5.0 AND 30.0
   OR currency_code NOT IN ('USD','EUR','GBP','JPY')
-- dbt-style единичный тест: ставка должна быть вменяемой, компоненты согласованы
SELECT series_id, date
FROM {{ ref('fct_econ_series') }}
WHERE value NOT BETWEEN -5.0 AND 30.0
   OR currency_code NOT IN ('USD','EUR','GBP','JPY')
Referential integrityСсылочная целостность

Every FK must resolve to a parent; no orphans. Critical when joining series metadata to facts.

Каждый FK должен разрешаться в родителя; никаких сирот. Критично при джойне метаданных серий к фактам.

-- orphan fact rows = broken integrity
SELECT fact.series_id
FROM fct_econ_series AS fact
LEFT JOIN dim_series AS dim ON fact.series_id = dim.series_id
WHERE dim.series_id IS NULL
-- сиротские факт-строки = сломанная целостность
SELECT fact.series_id
FROM fct_econ_series AS fact
LEFT JOIN dim_series AS dim ON fact.series_id = dim.series_id
WHERE dim.series_id IS NULL
Anomaly / outlier detectionОбнаружение аномалий / выбросов

Statistical bounds on metrics over time. Robust methods (MAD, IQR) beat mean±σ for fat-tailed financial data.

Статистические границы на метрики во времени. Робастные методы (MAD, IQR) бьют mean±σ для финансовых данных с «тяжёлыми хвостами».

# robust z-score; flag if today's row count deviates hard
median = np.median(history); mad = np.median(np.abs(history - median))
robust_z = 0.6745 * (today - median) / mad
if abs(robust_z) > 3.5: alert("volume anomaly")
# робастный z-score; флагнуть, если сегодняшнее число строк сильно отклонилось
median = np.median(history); mad = np.median(np.abs(history - median))
robust_z = 0.6745 * (today - median) / mad
if abs(robust_z) > 3.5: alert("volume anomaly")
Statistical / distribution checks (drift)Статистические / распределенческие проверки (дрифт)

Compare today's distribution to a baseline. PSI / KS for numeric, chi-square for categorical. Catches silent semantic drift.

Сравнить сегодняшнее распределение с базовой линией. PSI / KS для числовых, хи-квадрат для категориальных. Ловит тихий семантический дрифт.

# Population Stability Index > 0.25 = major shift
psi = Σ (actual%_i - expected%_i) * ln(actual%_i / expected%_i)
# Population Stability Index > 0.25 = крупный сдвиг
psi = Σ (actual%_i - expected%_i) * ln(actual%_i / expected%_i)
Reconciliation against source (the accuracy gold standard)Сверка с источником (золотой стандарт точности)

Compare your output back to the source-of-truth: row counts, control totals, checksums, sampled value-by-value. The only check that truly proves accuracy.

Сравнить твой вывод обратно с источником истины: число строк, контрольные суммы, чексуммы, выборочная сверка значение-по-значению. Единственная проверка, которая действительно доказывает точность.

-- control-total reconciliation
SELECT ABS(SUM(loaded.value) - source.expected_total) AS drift
FROM loaded, source_control_totals AS source
WHERE drift > 0.01  -- penny tolerance
-- сверка контрольных сумм
SELECT ABS(SUM(loaded.value) - source.expected_total) AS drift
FROM loaded, source_control_totals AS source
WHERE drift > 0.01  -- допуск на копейки
At Career.io I codified these as dbt tests (generic + singular) in CI so every model PR ran schema, not-null, unique, relationships, and accepted-values checks before merge — quality shifted left into the dev loop.
В Career.io я кодифицировал эти как dbt-тесты (generic + singular) в CI, чтобы каждый PR модели прогонял проверки schema, not-null, unique, relationships и accepted-values перед мерджем — качество сдвинулось влево в dev-loop.

📍Where to Put ControlsГде размещать контроли

The strong answer is "at three layers, each catching different failures, with cost increasing left-to-right."

Сильный ответ: «на трёх слоях, каждый ловит разные сбои, со стоимостью, растущей слева-направо».

LayerWhat it doesCatchesTrade-off
1. Ingestion gates
fail fast
Validate at the door — schema/contract, type, basic ranges before data enters the warehouseMalformed feeds, contract breaks, garbage sourceCheapest fix; but limited context (can't check cross-table consistency yet)
2. In-pipeline assertions
block bad loads
Checks between transform stages; stop/quarantine if a stage produces bad outputTransform bugs, join fan-out, business-rule violations, referential breaksAdds runtime + complexity; must not over-block on flaky thresholds
3. Post-load audits
verify & observe
Reconciliation, distribution/freshness/volume monitors on the served tableSilent drift, completeness gaps, accuracy vs source, SLA missesBad data may already be visible to consumers; pair with rollback/flagging
СлойЧто делаетЛовитКомпромисс
1. Ворота ингеста
fail fast
Валидация на входе — схема/контракт, тип, базовые диапазоны до того, как данные попадут в хранилищеКривые фиды, поломки контракта, мусор из источникаДешевле всего исправить; но ограниченный контекст (пока нельзя проверить кросс-табличную согласованность)
2. Утверждения в пайплайне
блок плохих загрузок
Проверки между этапами трансформации; стоп/карантин, если этап выдаёт плохой выводБаги трансформации, join fan-out, нарушения бизнес-правил, поломка ссылочной целостностиДобавляет время выполнения + сложность; нельзя пере-блокировать на зыбких порогах
3. Пост-лоад аудиты
проверка & мониторинг
Сверка, мониторы distribution/freshness/volume на выставленной таблицеТихий дрифт, пробелы в полноте, точность vs источник, пропуски SLAПлохие данные могут уже быть видны консьюмерам; паровать с rollback/флагированием
Rule of thumb: validate as early and as close to the source as possible (cheapest to fix), but you still need post-load observability because some failures (drift, completeness vs the real world) are only visible after the fact. Shift-left for the known, monitor-right for the unknown.
Правило: валидировать как можно раньше и ближе к источнику (дешевле всего исправлять), но всё равно нужен пост-лоад мониторинг, потому что некоторые сбои (дрифт, полнота vs реальный мир) видны только постфактум. Shift-left для известного, monitor-right для неизвестного.
Putting all controls only post-load means bad data already reached the terminal/consumer before you alert. Putting all controls only at ingestion misses transform bugs and drift. Interviewers test whether you understand the layering, not a single magic location.
Размещение всех контролей только пост-лоад означает, что плохие данные уже добрались до терминала/консьюмера до твоего алерта. Размещение всех контролей только на ингесте пропускает баги трансформации и дрифт. Интервьюеры проверяют, понимаешь ли ты слоение, а не одну магическую локацию.

🚦Circuit Breakers · Quarantine · Dead-LetterCircuit Breakers · Карантин · Dead-Letter

Circuit BreakerCircuit Breaker

When a critical assertion fails, halt the pipeline / block publish so bad data never reaches consumers. Best for accuracy-critical outputs (market data, control totals).

Когда критическое утверждение проваливается, остановить пайплайн / заблокировать публикацию, чтобы плохие данные никогда не добрались до консьюмеров. Лучше для критичных по точности выводов (рыночные данные, контрольные суммы).

"Better no data than wrong data" for financial feeds.

«Лучше вообще нет данных, чем неправильные данные» для финансовых фидов.

QuarantineКарантин

Route failing rows to a side table; let valid rows flow. Keeps the pipeline running while preserving bad records for inspection & backfill.

Направить провалившиеся строки в боковую таблицу; пустить валидные строки дальше. Держит пайплайн работающим, сохраняя плохие записи для инспекции и backfill.

Good when partial delivery is acceptable.

Хорошо, когда частичная доставка приемлема.

Dead-Letter QueueDead-Letter Queue

Streaming pattern: messages that fail parse/validation go to a DLQ topic instead of crashing the consumer. Reprocess after fix.

Стриминг-паттерн: сообщения, которые провалили parse/валидацию, идут в DLQ-топик вместо краша консьюмера. Переобработать после фикса.

Standard for Kafka/Kinesis ingestion.

Стандарт для Kafka/Kinesis ingestion.

def process_partition(rows):
    valid, bad = [], []
    for r in rows:
        (valid if passes_validation(r) else bad).append(r)
    bad_ratio = len(bad) / max(len(rows), 1)
    if bad_ratio > 0.02:                # circuit breaker: too much rot
        raise PipelineHalt("bad-row ratio > 2%, blocking publish")
    write_quarantine(bad)                  # quarantine the rest
    return valid
def process_partition(rows):
    valid, bad = [], []
    for r in rows:
        (valid if passes_validation(r) else bad).append(r)
    bad_ratio = len(bad) / max(len(rows), 1)
    if bad_ratio > 0.02:                # circuit breaker: слишком много гнили
        raise PipelineHalt("bad-row ratio > 2%, блокируем публикацию")
    write_quarantine(bad)                  # остальное в карантин
    return valid
"When do you block the whole pipeline vs quarantine bad rows?" → Block when the output is accuracy-critical and partial data is misleading (a control-total mismatch on a published index). Quarantine when valid rows are independently useful and you can backfill the rest. State the threshold and who gets paged.
«Когда блокировать весь пайплайн vs карантинировать плохие строки?» → Блокировать, когда вывод критичен по точности и частичные данные вводят в заблуждение (несоответствие контрольной суммы на публикуемом индексе). Карантин, когда валидные строки независимо полезны и можно backfill остальное. Назови порог и кого пейджить.

🧬Schema Change, Evolution & ContractsИзменения схем, эволюция и контракты

Detect → Classify → ReactОбнаружить → Классифицировать → Отреагировать

Change typeExamplesCompatibilityDefault reaction
Backward-compatibleAdd nullable column, widen type, add enum valuesafeAuto-evolve + log; notify
BreakingDrop/rename column, narrow type, change PK, NOT-NULL on existingbreaks consumersCircuit-break / require contract version bump
Semantic (sneaky)Same schema, units change (% → bps), meaning shiftssilentOnly distribution observability catches it
Тип измененияПримерыСовместимостьРеакция по умолчанию
Обратно совместимоеДобавить nullable колонку, расширить тип, добавить enum-значениебезопасноАвто-эволюция + лог; уведомить
ЛомающееУдалить/переименовать колонку, сузить тип, поменять PK, NOT-NULL на существующейломает консьюмеровCircuit-break / требовать бамп версии контракта
Семантическое (скрытное)Та же схема, единицы изменились (% → bps), смысл сдвинулсятихоеТолько мониторинг распределения его ловит

Schema evolutionЭволюция схемы

Let schemas change gracefully over time without breaking readers. Columnar formats (Parquet/Avro/Iceberg/Delta) support additive evolution. Always make new columns nullable with defaults; never reuse a column name with new meaning.

Позволить схемам изменяться плавно во времени без поломки читателей. Колоночные форматы (Parquet/Avro/Iceberg/Delta) поддерживают аддитивную эволюцию. Всегда делать новые колонки nullable со значениями по умолчанию; никогда не переиспользовать имя колонки с новым смыслом.

Data contractsКонтракты данных (Data contracts)

Strong framing: "I treat schema as an API. Producers can add safely; removing or retyping requires a version bump and a deprecation window. CI enforces the contract so breaking changes can't merge silently."
Сильное обрамление: «Я отношусь к схеме как к API. Продюсеры могут добавлять безопасно; удаление или переопределение типа требует бампа версии и окна устаревания. CI исполняет контракт, чтобы ломающие изменения не могли мёрджиться тихо».
The dangerous one is the semantic change: schema validation passes, types pass, but the units or population changed. This is why distribution monitoring (pillar 4) is non-negotiable, and why a contract should pin semantics, not just types.
Опасное — это семантическое изменение: валидация схемы прошла, типы прошли, но единицы или популяция изменились. Поэтому мониторинг распределения (столп 4) неотменяем, и поэтому контракт должен фиксировать семантику, а не только типы.

🛠️Exception Handling, Remediation & Self-HealingОбработка исключений, восстановление и самовосстановление

Exception handling principlesПринципы обработки исключений

  • Idempotent tasks: safe to re-run after failure (overwrite-by-partition, MERGE).
  • Retries with backoff for transient errors; cap attempts; distinguish transient vs systematic.
  • Fail loud, fail isolated: one bad partition shouldn't poison the rest.
  • Capture context: which rule, which rows, which run, which upstream version.
  • Идемпотентные таски: безопасно перезапускать после сбоя (перезапись по партиции, MERGE).
  • Ретраи с backoff для транзиентных ошибок; ограничить попытки; различать транзиентное vs систематическое.
  • Провал громко, провал изолированно: одна плохая партиция не должна отравить остальные.
  • Захватывать контекст: какое правило, какие строки, какой ран, какая версия апстрима.

Remediation ladderЛестница восстановления

  1. Auto-heal: retry, re-fetch from source, fill from last-good snapshot.
  2. Quarantine + backfill: hold bad rows, reprocess after fix.
  3. Serve-stale-with-flag: expose last good data marked is_stale=true rather than nothing.
  4. Rollback: revert to prior partition/version snapshot.
  5. Page a human: when none of the above is safe.
  1. Авто-хил: ретрай, перезагрузка из источника, заполнение из последнего хорошего снэпшота.
  2. Карантин + backfill: удержать плохие строки, переобработать после фикса.
  3. Serve-stale-with-flag: выдать последние хорошие данные с пометкой is_stale=true вместо ничего.
  4. Rollback: откатить к предыдущему снэпшоту партиции/версии.
  5. Пейджить человека: когда ничто из вышеперечисленного не безопасно.
For the 6.2B-record entity-resolution defect, remediation was: (1) freeze the bad merge logic, (2) reprocess affected entity clusters via an idempotent backfill keyed by cluster id, (3) add reconciliation + cluster-size distribution assertions so the defect class can never silently recur. That last step — turning an incident into a permanent test — is the part interviewers reward.
Для дефекта entity-resolution на 6.2B записях восстановление было: (1) заморозить плохую merge-логику, (2) переобработать затронутые кластеры сущностей через идемпотентный backfill по ключу cluster id, (3) добавить сверку + утверждения на распределение размеров кластеров, чтобы класс дефекта никогда не мог тихо повториться. Этот последний шаг — превращение инцидента в постоянный тест — это часть, которую интервьюеры награждают.
"What's true self-healing vs just retries?" → Retries handle transient faults. Self-healing handles predictable failure classes deterministically (re-derive from source, swap to last-good snapshot, auto-quarantine) without a human. Be honest: don't auto-heal accuracy-critical financial outputs silently; alert even when you recover.
«Что такое настоящее самовосстановление vs просто ретраи?» → Ретраи обрабатывают транзиентные сбои. Самовосстановление обрабатывает предсказуемые классы сбоев детерминированно (перевывести из источника, переключиться на последний хороший снэпшот, авто-карантин) без человека. Будь честен: не авто-хили критичные по точности финансовые выводы тихо; алерть даже когда восстанавливаешь.

🔔Alerting Design · SLAs / SLOsДизайн алертов · SLA / SLO

Severity & routingSeverity и маршрутизация

SeverityTriggerRouteAction
P0 / CriticalPublished market data wrong/missing; SLA breach on a traded seriesPage on-call nowCircuit-break; incident channel
P1 / HighFreshness/volume anomaly, contract break upstreamPage during hours / ticketTriage via lineage; remediate
P2 / WarnSoft drift, rising NULL rate, slow trendDashboard + digestInvestigate proactively
SeverityТриггерМаршрутизацияДействие
P0 / CriticalПубликуемые рыночные данные неправильные/отсутствуют; нарушение SLA на торгуемом рядеПейджить on-call сейчасCircuit-break; канал инцидента
P1 / HighАномалия freshness/volume, поломка контракта апстримаПейджить в часы работы / тикетТриаж через lineage; восстановить
P2 / WarnМягкий дрифт, растущая доля NULL, медленный трендДашборд + дайджестРасследовать проактивно

SLAs vs SLOs vs SLIs for dataSLA vs SLO vs SLI для данных

Beating alert fatigueБорьба с усталостью от алертов

The #1 observability failure mode in practice is alert fatigue: too many low-value pages and people mute the channel — then the real P0 gets missed. Always pair "more coverage" with "ruthless noise control."
№1 режим сбоя observability на практике — это усталость от алертов: слишком много малоценных пейджов и люди мьютят канал — потом реальный P0 пропускается. Всегда парь «больше покрытия» с «безжалостным контролем шума».

🧰Tools LandscapeЛандшафт инструментов

ToolCategoryStrengthsWatch-outs
Great ExpectationsDQ assertionsRich expectation library, data docs, profilingConfig-heavy; can be slow at very large scale
dbt testsDQ in transform layerGeneric + singular tests in CI, version-controlled, freeWarehouse-only; runs at build, not continuous monitoring
SodaDQ checks (SodaCL)Simple YAML checks, in-pipeline + scheduledLess ecosystem than GE
Monte CarloObservability (ML)Auto 5-pillar monitors, lineage, low setupCommercial; can be a black box; cost
AnomaloObservability (ML)Unsupervised anomaly detection on tablesCommercial; tuning false positives
Custom frameworkBothFits warehouse/scale exactly; metrics → ODS/Scuba dashboardsYou own maintenance; reinvention risk
ИнструментКатегорияСильные стороныЧто учитывать
Great ExpectationsDQ-утвержденияБогатая библиотека ожиданий, data docs, профилированиеТяжёлая конфигурация; может быть медленным на очень больших масштабах
dbt testsDQ в слое трансформацииGeneric + singular тесты в CI, версионируемые, бесплатныеТолько хранилище; запуск на билде, а не непрерывный мониторинг
SodaDQ-проверки (SodaCL)Простые YAML-проверки, в пайплайне + по расписаниюМеньше экосистемы, чем GE
Monte CarloObservability (ML)Авто-мониторы 5 столпов, lineage, мало настройкиКоммерческий; может быть чёрным ящиком; стоимость
AnomaloObservability (ML)Без-учительное обнаружение аномалий на таблицахКоммерческий; настройка ложных срабатываний
Кастомный фреймворкОбаПодходит под хранилище/масштаб точно; метрики → ODS/Scuba дашбордыТы владелец обслуживания; риск переизобретения
Decision logic: dbt tests / Great Expectations for declarative known-unknowns shifted left into CI; Monte Carlo / Anomalo (or a custom metric pipeline) for unknown-unknowns + drift across many tables; custom when you operate at billions-of-rows scale where off-the-shelf full-scan tools are too slow or costly.
Логика выбора: dbt tests / Great Expectations для декларативных известных-неизвестных, сдвинутых влево в CI; Monte Carlo / Anomalo (или кастомный метрик-пайплайн) для неизвестных-неизвестных + дрифта по многим таблицам; кастомный, когда оперируешь на масштабах миллиардов строк, где off-the-shelf полно-сканные инструменты слишком медленные или дорогие.
At Meta I leaned on a custom framework emitting DQ metrics to internal dashboards/alerts (the model Great Expectations/Soda formalize); at Career.io I used dbt tests in CI. Showing I've used both managed and hand-rolled approaches signals I'll pick the right one for the stack rather than dogmatically pushing one tool.
В Meta я опирался на кастомный фреймворк, эмитящий DQ-метрики во внутренние дашборды/алерты (модель, которую формализуют Great Expectations/Soda); в Career.io я использовал dbt tests в CI. Показывая, что я использовал оба подхода (управляемый и hand-rolled), сигналю, что я выберу правильный для стека, а не догматично толкаю один инструмент.

📈Designing Controls That Scale to Billions of RowsДизайн контролей, масштабирующихся на миллиарды строк

This is where the 3.6B-events/day and 6.2B-record anchors earn their keep. Full-table validation on every run is infeasible at that scale; you trade exactness for cost.

Здесь якоря «3.6B событий/день» и «6.2B записей» окупаются. Полная валидация таблицы на каждый ран невозможна на таком масштабе; ты обмениваешь точность на стоимость.

Make checks cheapДелай проверки дешёвыми

  • Aggregate-first: validate counts, sums, NULL ratios, min/max — O(scan) once, not row-by-row in app code.
  • Push down checks into SQL / the engine; let the warehouse parallelize.
  • Incremental / partition-scoped: validate only the new partition, not history.
  • Sampling for expensive value-level checks; full scan only on aggregates.
  • Сначала агрегаты: валидировать counts, sums, доли NULL, min/max — O(scan) один раз, а не построчно в коде приложения.
  • Спустить вниз (Push down) проверки в SQL / движок; пусть хранилище распараллеливает.
  • Инкрементально / партиция-скоупед: валидировать только новую партицию, а не историю.
  • Выборка (Sampling) для дорогих построчных проверок; полный скан только на агрегатах.

Make checks smartДелай проверки умными

  • Approximate algorithms: HyperLogLog for distinct counts, sketches for quantiles.
  • Metric store + baselines: persist DQ metrics over time so anomaly detection is a cheap compare, not a recompute.
  • Tier checks by criticality: heavy reconciliation on key series, light monitors elsewhere.
  • Async observability off the hot path; synchronous gates only for circuit-break-critical rules.
  • Приблизительные алгоритмы: HyperLogLog для distinct counts, скетчи для квантилей.
  • Метрик-стор + базовые линии: сохранять DQ-метрики во времени, чтобы обнаружение аномалий было дешёвым сравнением, а не перевычислением.
  • Тирировать проверки по критичности: тяжёлая сверка на ключевых рядах, лёгкие мониторы где-то ещё.
  • Асинхронный observability вне горячего пути; синхронные ворота только для circuit-break-критичных правил.
My DQ monitors on the 3.6B-events/day pipeline worked exactly this way: partition-scoped aggregate metrics (volume, freshness, NULL/distribution) emitted to a time-series store with seasonality-aware thresholds, plus a few synchronous circuit-breaker assertions on the must-be-correct fields. Full row-level validation only on sampled slices. That kept DQ cost a tiny fraction of pipeline cost while still catching real defects.
Мои DQ-мониторы на пайплайне 3.6B событий/день работали именно так: партиция-скоупед агрегатные метрики (volume, freshness, NULL/distribution) эмитились в time-series стор с сезонно-осведомлёнными порогами, плюс несколько синхронных circuit-breaker-утверждений на must-be-correct поля. Полная построчная валидация только на выборочных слайсах. Это держало стоимость DQ крошечной долей стоимости пайплайна, всё ещё ловя реальные дефекты.
"How do you validate uniqueness on 6 billion rows without it dominating runtime?" → Don't COUNT(DISTINCT) the world each run: validate uniqueness incrementally per partition + an approximate distinct (HLL) tripwire on the full key space + a periodic exact reconciliation job. Explain the cost/exactness trade-off explicitly.
«Как валидировать уникальность на 6 миллиардах строк без доминирования рантайма?» → Не COUNT(DISTINCT) весь мир на каждый ран: валидировать уникальность инкрементально на партицию + приблизительный distinct (HLL) триггер на полном ключевом пространстве + периодический точный reconciliation job. Объясни компромисс стоимость/точность явно.

🗺️Pipeline With Controls (Reference Diagram)Пайплайн с контролями (референсная диаграмма)

  SOURCE FEEDS                INGEST                  TRANSFORM                 SERVE / CONSUME
  (BLS, FX, GDP,          ┌──────────────┐       ┌──────────────────┐       ┌──────────────────┐
   vendor APIs) ─────────▶│ [1] INGEST   │──────▶│ [2] IN-PIPELINE  │──────▶│ [3] POST-LOAD    │──▶ Terminal /
                          │     GATES    │       │    ASSERTIONS    │       │    AUDITS + OBS  │    dashboards /
                          └──────┬───────┘       └────────┬─────────┘       └────────┬─────────┘    downstream
                                 │ fail                   │ fail                     │ drift/SLA
                          schema/contract        biz-rules / ref-integ        reconcile vs source
                          type / range           join fan-out / dedup         freshness · volume
                                 │                        │                   schema · dist · lineage
                                 ▼                        ▼                          ▼
                          ┌────────────┐  bad rows  ┌──────────┐            ┌──────────────────┐
                          │   DLQ /    │◀───────────│QUARANTINE│            │  METRIC STORE    │
                          │  REJECT    │            │  TABLE   │──backfill─▶│  (time-series)   │
                          └────────────┘            └──────────┘            └────────┬─────────┘
                                 ▲                                                   │ anomaly?
                          CIRCUIT BREAKER: bad-ratio > threshold  ──── HALT ─────────┤
                                                                                     ▼
                                                                       ┌──────────────────────┐
                                                                       │ ALERTING (P0/P1/P2)  │
                                                                       │ severity · SLO/SLA   │
                                                                       │ lineage-grouped      │
                                                                       └──────────┬───────────┘
                                                                                  ▼
                                                                       REMEDIATION LADDER:
                                                                       retry → re-fetch → last-good
                                                                       → rollback → page human
  ФИДЫ-ИСТОЧНИКИ            INGEST                  TRANSFORM                 SERVE / CONSUME
  (BLS, FX, GDP,          ┌──────────────┐       ┌──────────────────┐       ┌──────────────────┐
   vendor API)  ─────────▶│ [1] ВОРОТА   │──────▶│ [2] УТВЕРЖДЕНИЯ  │──────▶│ [3] ПОСТ-ЛОАД    │──▶ Терминал /
                          │   ИНГЕСТА    │       │   В ПАЙПЛАЙНЕ    │       │  АУДИТ + МОНИТ.  │    дашборды /
                          └──────┬───────┘       └────────┬─────────┘       └────────┬─────────┘    даунстрим
                                 │ провал                 │ провал                   │ дрифт/SLA
                          схема/контракт         бизн-правила / реф-целост.  сверка с источником
                          тип / диапазон         join fan-out / дедуп         freshness · volume
                                 │                        │                   schema · dist · lineage
                                 ▼                        ▼                          ▼
                          ┌────────────┐  плохие    ┌──────────┐            ┌──────────────────┐
                          │   DLQ /    │   строки   │ КАРАНТИН │            │  МЕТРИК-СТОР     │
                          │  REJECT    │◀───────────│  ТАБЛИЦА │──backfill─▶│  (time-series)   │
                          └────────────┘            └──────────┘            └────────┬─────────┘
                                 ▲                                                   │ аномалия?
                          CIRCUIT BREAKER: bad-ratio > порог  ────── HALT ───────────┤
                                                                                     ▼
                                                                       ┌──────────────────────┐
                                                                       │ АЛЕРТЫ (P0/P1/P2)    │
                                                                       │ severity · SLO/SLA   │
                                                                       │ группировка lineage  │
                                                                       └──────────┬───────────┘
                                                                                  ▼
                                                                       ЛЕСТНИЦА ВОССТАНОВЛЕНИЯ:
                                                                       retry → перезагрузка → last-good
                                                                       → rollback → пейджить человека

Three control layers (cheapest fix on the left), DLQ/quarantine for bad records, a metric store powering observability + anomaly detection, a circuit breaker that can halt publish, and a severity-tiered alerting path into a remediation ladder.

Три контрольных слоя (дешевле всего чинить слева), DLQ/карантин для плохих записей, метрик-стор, питающий мониторинг + обнаружение аномалий, circuit breaker, способный остановить публикацию, и severity-тирированный путь алертов в лестницу восстановления.

🃏Self-Quiz — Flip to RevealСамопроверка — кликни чтобы открыть

Tap a card to flip. Say the answer out loud first.

Кликни на карту, чтобы перевернуть. Сначала произнеси ответ вслух.

Q1. What's the difference between data quality and data observability?Q1. Разница между качеством данных и observability?
known vs unknown…известное vs неизвестное…
DQ = declarative tests on known-unknowns (rules you wrote: ranges, PK, not-null). Observability = statistical baselines + metadata over time to surface unknown-unknowns (drift, silent breakage, upstream surprises). A pipeline can pass every DQ test and still be silently broken — that's why you need both.
DQ = декларативные тесты на известные-неизвестные (правила, которые ты написал: диапазоны, PK, not-null). Observability = статистические базовые линии + метаданные во времени, чтобы выявить неизвестные-неизвестные (дрифт, тихие поломки, сюрпризы апстрима). Пайплайн может проходить каждый DQ-тест и всё ещё быть тихо сломанным — поэтому нужны оба.
Q2. Name the 5 pillars of observability and one signal each.Q2. Назови 5 столпов observability и по одному сигналу.
F V S D L
Freshness (now − max updated_at), Volume (row count vs baseline), Schema (structure diff vs contract), Distribution (NULL%/mean/cardinality/drift), Lineage (upstream-downstream graph for root cause + blast radius).
Freshness (now − max updated_at), Volume (число строк vs базис), Schema (структурный diff vs контракт), Distribution (NULL%/mean/кардинальность/дрифт), Lineage (граф апстрим-даунстрим для первопричины + радиус поражения).
Q3. The schema didn't change and all dbt tests pass, but the numbers look off. What kind of failure, and what catches it?Q3. Схема не изменилась и все dbt-тесты прошли, но числа выглядят не так. Какой тип сбоя и что его ловит?
units…единицы…
A semantic change (e.g., units shifted % → bps, or population changed) — same schema, same types, different meaning. Type/schema validation can't see it; only distribution observability (pillar 4) and reconciliation vs source catch it. Pin semantics in the data contract, not just types.
Семантическое изменение (например, единицы сдвинулись % → bps, или популяция изменилась) — та же схема, те же типы, другой смысл. Валидация типа/схемы этого не видит; только мониторинг распределения (столп 4) и сверка с источником ловят. Фиксировать семантику в контракте данных, а не только типы.
Q4. When do you circuit-break the pipeline vs quarantine bad rows?Q4. Когда делать circuit-break пайплайна vs карантин плохих строк?
accuracy-critical?критично по точности?
Circuit-break when output is accuracy-critical and partial/wrong data is misleading (e.g., published index fails a control-total check) — better no data than wrong data. Quarantine when valid rows are independently useful and bad ones can be backfilled. Define the threshold (e.g., bad-ratio > 2%) and who gets paged.
Circuit-break, когда вывод критичен по точности и частичные/неправильные данные вводят в заблуждение (например, публикуемый индекс проваливает проверку контрольной суммы) — лучше нет данных, чем неправильные. Карантин, когда валидные строки независимо полезны и плохие можно backfill. Определить порог (например, bad-ratio > 2%) и кого пейджить.
Q5. How do you validate uniqueness/quality on billions of rows affordably?Q5. Как валидировать уникальность/качество на миллиардах строк доступно?
incremental + approxинкрементально + приблизительно
Validate incrementally per partition, push checks down into SQL, validate aggregates (counts/sums/NULL%) instead of row-by-row, use approximate structures (HLL for distinct, sketches for quantiles) as tripwires, persist metrics to a store for cheap baseline comparison, and run exact reconciliation periodically — not every run. Tier heavy checks to critical fields.
Валидировать инкрементально на партицию, спустить проверки в SQL, валидировать агрегаты (counts/sums/NULL%) вместо построчно, использовать приблизительные структуры (HLL для distinct, скетчи для квантилей) как триггеры, сохранять метрики в стор для дешёвого сравнения с базисом, и запускать точную сверку периодически — не на каждый ран. Тирировать тяжёлые проверки на критичные поля.
Q6. SLI vs SLO vs SLA, and how do you fight alert fatigue?Q6. SLI vs SLO vs SLA, и как бороться с усталостью от алертов?
indicator/objective/agreementиндикатор/цель/соглашение
SLI = measured signal (freshness lag). SLO = internal target (99% land by 08:31). SLA = external promise with consequences. Fight fatigue: tier severity (only P0/P1 page), group alerts by root cause via lineage, dynamic seasonality-aware thresholds, make every alert actionable (table+rule+owner+runbook), and prune low-precision monitors.
SLI = измеряемый сигнал (задержка freshness). SLO = внутренний таргет (99% приземляются к 08:31). SLA = внешнее обещание с последствиями. Борьба с усталостью: тирировать severity (пейджить только P0/P1), группировать алерты по первопричине через lineage, динамические сезонно-осведомлённые пороги, делать каждый алерт действенным (таблица+правило+владелец+runbook), и подрезать мало-точные мониторы.

🎤30-Second Closing Pitch30-секундный closing pitch

"I think about quality as fitness-for-use across accuracy, completeness, timeliness, consistency, validity and uniqueness, enforced at three layers: ingestion gates, in-pipeline assertions, and post-load audits. On top of that I run observability — freshness, volume, schema, distribution, lineage — to catch the failures I never wrote a rule for. I've done this at scale: I fixed an entity-resolution defect across 6.2 billion records by combining distribution observability with reconciliation, then locked it in with assertions; and I built DQ monitors on a 3.6B-events/day pipeline using partition-scoped aggregate metrics and seasonality-aware alerting so the cost stayed tiny. For economics data, where timeliness and accuracy are non-negotiable, I'd embed circuit breakers on the must-be-correct series, contracts on the schema, and a severity-tiered alerting path into a remediation ladder — so bad data never silently reaches the terminal."

«Я думаю о качестве как о пригодности для использования по точности, полноте, своевременности, согласованности, корректности и уникальности, исполняемой на трёх слоях: ворота ингеста, утверждения в пайплайне и пост-лоад аудиты. Поверх этого я запускаю observability — freshness, volume, schema, distribution, lineage — чтобы ловить сбои, на которые я никогда не писал правила. Я делал это на масштабе: я исправил дефект entity-resolution на 6.2 миллиардах записей комбинируя мониторинг распределения со сверкой, затем зафиксировал это утверждениями; и я построил DQ-мониторы на пайплайне 3.6B событий/день используя партиция-скоупед агрегатные метрики и сезонно-осведомлённые алерты, чтобы стоимость оставалась крошечной. Для экономических данных, где своевременность и точность неотменяемы, я бы встроил circuit breakers на must-be-correct ряды, контракты на схему, и severity-тирированный путь алертов в лестницу восстановления — чтобы плохие данные никогда тихо не добирались до терминала».