Airbnb и смещения в экспериментах: почему группы не независимы
Интерференция между вариантами в маркетплейсе: как перетекание спроса, длинный цикл решения и реакция предложения искажают A/B-тест, и какими проверками это видно на своих данных.
Содержание статьи
Новое ранжирование поиска выиграло у старого: у тестовой группы бронирований на 60% больше, p-value меньше 0,01, выборка в сотни тысяч пользователей. А суммарное число бронирований по городу за ту же неделю не изменилось вообще. Оба наблюдения верны, и вместе они означают одно: тест не создал спрос, он перетащил его у контроля. В маркетплейсе такое случается по структурной причине — одно и то же жильё нельзя забронировать дважды, поэтому группы делят один ограниченный ресурс и перестают быть независимыми. Airbnb публично описывал несколько механизмов, из-за которых аккуратно проведённый эксперимент в такой системе даёт неверный ответ. Ниже — разбор этих механизмов и проверки, которыми интерференцию можно обнаружить в своём продукте, даже если у вас двадцать городов, а не двести.
Коротко
Стандартный A/B-тест опирается на допущение, которое редко проговаривают вслух: то, что происходит с одним пользователем, не зависит от того, в какую группу попал другой. В маркетплейсе это допущение нарушается почти всегда.
- Общий ограниченный ресурс — инвентарь, слоты, курьеры, промо-бюджет — превращает сравнение групп в гонку за один пирог.
- Перетекание может и завышать эффект (тест отнял спрос у контроля), и занижать его (выгода утекла в контроль через общую модель или реакцию продавца).
- Больше выборки смещение не лечит: чем больше доля теста, тем сильнее давление на общий ресурс.
- Длинный цикл решения растягивает эффект на недели — короткое окно измеряет недозревший результат.
- Нейтральный средний результат совместим с поломкой в одном сегменте: платформа, версия приложения, браузер, рынок.
- Лечится не статистикой, а дизайном: рандомизация по рынку, переключающиеся периоды, проверка чувствительности к доле теста.
Одно и то же жильё нельзя забронировать дважды — здесь и ломается независимость
Формально допущение называется SUTVA: отклик пользователя зависит только от его собственного назначения в группу. На нём держится вся простая арифметика A/B-теста — и разница средних, и расчёт дисперсии, и доверительный интервал. Пока пользователи ничего друг у друга не отнимают, допущение безобидное. В соцсети оно ломается через социальный граф, в райдхейлинге — через очередь водителей, в жилье — через календарь объекта.
Airbnb — двусторонний маркетплейс с конечным и неоднородным предложением: на конкретные даты в конкретном районе доступно ограниченное число объектов, и они заметно различаются по привлекательности. Когда тестовый вариант поиска лучше подсвечивает удачные варианты, его пользователи быстрее эти варианты занимают. Пользователь контрольной группы, открывший поиск через два часа, видит рынок, из которого уже вынули лучшее — и его конверсия падает не от старого ранжирования, а от чужого успеха.
Получается неприятная вещь: контрольная группа перестаёт быть «продуктом без изменения». Она становится «продуктом без изменения в мире, где половина пользователей получила изменение». Сравнение двух групп отвечает уже не на тот вопрос, который задавали при запуске.
Δ_измеренный = Δ_истинный + (спрос, отнятый у контроля) − (выгода, утёкшая в контроль)Оба слагаемых в скобках не наблюдаются напрямую и не уменьшаются с ростом выборки. Увеличив долю теста с 5% до 50%, вы усиливаете первое слагаемое, а не сокращаете его.
Перетекание спроса: тест выигрывает у контроля, пока рынок стоит на месте
Посчитаем на учебном примере, чтобы увидеть арифметику. Выходные в одном городе: свободны 100 объектов, поиск за эти дни открыли 10 000 человек, половина получила новое ранжирование. До теста город стабильно отдавал около 100 бронирований за такие выходные — по 50 на каждую половину аудитории.
Сценарий первый, предложение полностью выбрано. Новое ранжирование помогает выбрать быстрее, тестовая группа берёт 62 объекта, контролю остаётся 38. Сравнение групп показывает рост на 63%. Рынок при этом получил те же 100 бронирований: эффект изменения для бизнеса равен нулю, а отчёт обещает полтора раза.
Сценарий второй, предложение выбрано не до конца. Допустим, при идеальном подборе город мог бы отдать 120 объектов. Тест берёт 62, контроль проседает до 45 — часть его пользователей не нашла подходящего варианта, потому что удачные уже заняты. Итог по рынку: 107 вместо 100, реальный прирост 7%. Сравнение групп при этом даёт 62/45 − 1, то есть почти 38%. Направление вывода верное, масштаб завышен впятеро.
Разница между двумя сценариями — степень дефицита, и она меняется от сезона к сезону и от района к району. Поэтому один и тот же тест в высокий сезон и в низкий даст разные числа, и ни одно из них не будет эффектом самого ранжирования.
Второй сценарий из текста. Сравнение групп: +37,8%. Прирост по рынку: +7%. Разницу дало перераспределение спроса, а не новое качество поиска.
Положите рядом три линии: объём по тестовой группе, объём по контролю и суммарный объём по рынку за те же даты. Если группы расходятся, а сумма держится на уровне прошлых недель, у вас перераспределение. Вывод «вариант работает» придётся переписать до «вариант перетягивает спрос на себя».
Перетекание умеет прятать эффект и переносить издержки на вторую сторону
Перетекание не всегда работает в пользу теста. Есть минимум три канала, по которым выгода утекает к контролю и измеренный эффект оказывается меньше настоящего.
Первый — общая модель. Поиск и рекомендации обычно учатся на поведении всех пользователей сразу. Если тестовая группа начала активнее открывать определённый тип объектов, модель это запомнит и поднимет их в выдаче всем, включая контроль. Контроль становится лучше без изменения интерфейса, и разница между группами сжимается.
Второй — реакция второй стороны. Хозяин объекта не знает про эксперимент. Он видит, что запросов стало больше, и меняет цену, календарь или скорость ответа. Эти изменения достаются обеим группам: пользователь контрольной группы заходит в тот же улучшенный объект.
Третий — сам пользователь. Поездку часто планируют вдвоём и с нескольких устройств. Если рандомизация идёт по устройству или по cookie, один человек успевает побывать в обеих группах, и разница размывается механически.
Практический вывод неудобный: знак смещения заранее неизвестен. Нельзя сказать «мы знаем, что эффект завышен, поделим пополам». Нужно понимать, какой канал перетекания сильнее в вашем продукте, и закрывать именно его.
Второй канал стоит разобрать отдельно, потому что он самый медленный и потому чаще всего пропускается. Изменение адресовано одной стороне, а меняет условия для другой: подняли в выдаче новых хозяев — старые получили меньше показов и скорректировали цену и календарь; включили части пользователей агрессивные уведомления — у хозяев выросла нагрузка на ответы, и отвечать они стали медленнее всем.
Это не издержка, которую можно списать в примечание. Это прямое нарушение условий эксперимента: окружение контрольной группы изменилось из-за теста. Причём хозяин корректирует цену не в тот же день, так что к концу короткого окна реакция ещё не проявилась — тест заканчивается до того, как появится главный долгосрочный эффект.
Рабочий приём — завести метрики второй стороны в обязательный набор: доля объектов, получивших хотя бы один запрос, медианное время ответа, доля активных хозяев, распределение спроса между объектами. Если изменение улучшило пользовательскую метрику за счёт концентрации спроса на узком верхе предложения, это видно как рост доли бронирований у топа при падении доли объектов с хотя бы одной сделкой.
- Общая обучаемая модель — канал утечки в контроль. Либо обучай на одной группе, либо фиксируйте модель на время теста.
- Реакция продавца или исполнителя — канал утечки в обе стороны. Смотрите на метрики второй стороны как на guardrail.
- Рандомизация по устройству вместо аккаунта — канал утечки внутри одного человека. Проверяйте долю пересечений.
- Медленные каналы требуют окна длиннее, чем цикл решения пользователя: реакция предложения отстаёт от спроса.
Нейтральный средний результат совместим с поломкой в одном сегменте
В публичном разборе экспериментов Airbnb (инженерный блог компании) есть эпизод, который часто пересказывают: редизайн страницы поиска выглядел нейтральным, пока результат не разбили по браузерам. В одном из браузеров новая страница работала заметно хуже — из-за технической ошибки, а не из-за дизайна. После исправления картина изменилась. Я не воспроизвожу числа, потому что в пересказах они расходятся; важен сам механизм.
Механизм такой: итоговая метрика — это средневзвешенное по сегментам. Рост в большом сегменте и провал в маленьком складываются в ноль, и тест честно показывает ноль. Решение «не запускаем, эффекта нет» при этом неверно дважды: эффект есть, и есть техническая поломка, которая будет жить дальше.
Разрезы, которые стоит смотреть до обсуждения итога, выбираются не по интуиции, а по тому, где изменение могло сломаться технически: платформа и ОС, версия приложения, браузер, тип устройства, новый или вернувшийся пользователь, рынок, язык интерфейса. Это не поиск победного сегмента — заранее зафиксированная диагностика. Разница между диагностикой и подгонкой в том, что список разрезов написан до запуска, а вывод по отдельному сегменту требует отдельной проверки.
| Итог по тесту | Что в сегментах | Рабочая версия |
|---|---|---|
| ноль | один сегмент сильно хуже | техническая поломка в этом сегменте |
| ноль | везде около нуля | изменение действительно нейтрально |
| рост | рост только в одном крупном сегменте | эффект узкий, перенос на всех не обоснован |
| рост | рынок целиком не вырос | перераспределение спроса внутри рынка |
| падение | падение у новых пользователей | проверьте обучение модели и онбординг |
Решение о поездке длиннее окна теста, поэтому эффект дозревает неделями
Бронирование жилья — не импульсная покупка. Между первым поиском и подтверждением проходят дни: человек сравнивает варианты, советуется, ждёт отпускных дат. Это значит, что в первые дни теста большая часть его пользователей ещё не доехала до целевого события, и измеренный эффект заведомо ниже итогового. Airbnb отдельно писал об этом как о причине, по которой тест нельзя мерить по первым дням.
Дальше хуже: у разных исходов разная задержка. Бронирование видно через дни, отмена — через недели, отзыв и повторная поездка — через месяцы. Если решение принимается по быстрой метрике, вы фактически выбираете суррогат. Суррогат допустим, но только если заранее проверено, что он связан с нужным исходом в ту же сторону; иначе получится «бронирований больше, отмен ещё больше».
Отсюда два правила для окна теста. Длительность считается не от размера выборки, а от цикла решения: окно должно покрывать хотя бы один полный путь типичного пользователя плюс запас. И в отчёт идёт не только финальная точка, но и кривая накопления эффекта: если она к концу теста всё ещё растёт, вывод о величине эффекта преждевременный.
На третий день видно 2,1%, на двадцать восьмой — 5,2%. Ни одно из этих чисел не является «ранним сигналом» другого: они измеряют разные доли дозревших пользователей.
Окно теста попало в один режим спроса, а вывод распространили на все
Спрос на поездки сезонен, и это не мелкая поправка: в высокий сезон предложение дефицитно, в низкий его избыток. Эффект изменения поиска в этих двух режимах разный по природе. Когда объектов мало, лучший подбор перераспределяет спрос. Когда объектов много, он действительно может увеличить число сделок. Тест, прошедший в августе, не отвечает за февраль.
Сезонность добавляет и техническую ловушку. Если тест длится три недели и захватывает праздник, обе группы переживают его одновременно — и само сравнение от этого не портится. Портится экстраполяция: полученный процент переносят на годовой план, где такого спроса нет большую часть времени. Плюс праздник меняет состав аудитории, а вместе с составом — и средний эффект, потому что он разный у разных сегментов.
Что с этим делать без большой платформы. Во-первых, фиксировать в дизайне, на какой период распространяется вывод, и не считать годовой эффект умножением недельного на 52. Во-вторых, для важных изменений повторять тест в другом режиме спроса — это дешевле, чем годовой откат. В-третьих, сравнивать не «неделя теста против прошлой недели», а группы внутри одного окна: межгрупповое сравнение устойчиво к сезону, а сравнение периодов — нет.
Подглядывание — это не про любопытство, а про правило остановки
Если смотреть на p-value каждый день и останавливать тест в первый день, когда он ушёл ниже 0,05, частота ложных срабатываний перестаёт равняться заявленной. Причина не в самом просмотре, а в том, что момент остановки стал зависеть от данных: у блуждающего показателя рано или поздно случается удачный день, и именно в этот день делается вывод.
Разбор типовых заблуждений в онлайн-экспериментах — известная работа «A/B Testing Intuition Busters» — как раз про такие интуитивные ошибки: они выглядят безобидно и не видны в итоговой таблице. Смотреть промежуточные данные при этом нужно: сломанный checkout или обвал guardrail надо замечать в первый час, а не в отчёте через три недели.
Разделяйте два разных действия. Остановка ради безопасности срабатывает по заранее описанному вреду — падение guardrail ниже порога, рост ошибок, жалобы. Остановка ради результата требует формального правила: либо фиксированный горизонт без промежуточных выводов, либо последовательный дизайн с распределением уровня значимости по проверкам. Третьего варианта «посмотрим и решим» не существует, хотя именно он чаще всего и применяется.
| Зачем смотрю | На что имею право | Что должно быть до запуска |
|---|---|---|
| ищу вред | остановить тест | порог guardrail, владелец, время реакции |
| ищу результат | ничего не решать | фиксированный горизонт или sequential-правило |
| проверяю корректность | починить и перезапустить | SRM-проверка, баланс сегментов |
Признаки интерференции в вашем продукте: ищи общий ресурс
Интерференция — свойство не компании, а механики. Маркетплейс жилья просто делает её очевидной. Чтобы понять, есть ли она у вас, достаточно ответить на один вопрос: может ли действие одного пользователя изменить то, что увидит или получит другой. Если да — группы связаны.
Дальше стоит посмотреть на силу связи. Слабая связь при доле теста 5% и большом рынке может быть несущественной; сильная связь при 50/50 на маленьком рынке ломает вывод полностью. Ориентир простой: чем ближе спрос к пределу предложения, тем дороже игнорировать перетекание.
| Общий ресурс | Как выглядит в продукте | Что перетекает между группами |
|---|---|---|
| Ограниченный инвентарь | жильё, слоты, билеты, товар в одном экземпляре | занятая позиция исчезает у другой группы |
| Очередь исполнителей | курьеры, мастера, операторы поддержки | ускорили одних — остальные ждут дольше |
| Общая модель ранжирования | поиск и рекомендации учатся на всех сразу | поведение теста меняет выдачу контролю |
| Общий бюджет | промокоды, скидочный фонд, рекламные ставки | лимит исчерпан — у контроля его уже нет |
| Рейтинг и лента | отзывы, топы, приглашения, шаринг | контент одной группы видят обе |
Три проверки, которыми перетекание видно на своих данных
Первая проверка — спорный спрос. Возьмите объекты, на которые в окне теста смотрели или которые бронировали пользователи обеих групп, и посчитайте, какую долю всего спроса они собирают. Если большая часть спроса приходится на объекты, за которые конкурируют обе группы, перетекание не гипотеза, а факт. Один запрос ниже даёт это число.
Вторая проверка — рынок целиком. Посчитайте суммарный объём целевого события по рынку за окно теста и сравните с тем же окном прошлых периодов, нормализовав на дни недели. Если группы разошлись, а сумма не изменилась, в отчёте должно стоять слово «перераспределение», а не «прирост».
Третья проверка — чувствительность к доле теста. Запустите изменение с разной долей трафика на сопоставимых рынках: например, 10% в одних городах и 50% в других. Если оценённый эффект зависит от доли, это прямой признак интерференции: при отсутствии связи между пользователями оценка от доли зависеть не должна. Проверка стоит дорого по времени, зато даёт не только знак смещения, но и его примерный масштаб.
with exposed as (
select user_id, variant
from experiment_users
where experiment_id = 'search_rerank_7'
),
demand as (
select e.variant, v.listing_id, count(*) as views
from listing_views v
join exposed e using (user_id)
where v.viewed_at >= date '2026-09-01'
and v.viewed_at < date '2026-09-22'
group by 1, 2
),
per_listing as (
select
listing_id,
count(distinct variant) as variants,
sum(views) as views
from demand
group by 1
)
select
count(*) as listings_total,
count(*) filter (where variants = 2) as listings_contested,
sum(views) filter (where variants = 2)::numeric
/ nullif(sum(views), 0) as contested_demand_share
from per_listing;- Считайте спорный спрос по тому ресурсу, который реально исчерпаем: объект на конкретные даты, а не объект вообще.
- Долю выше примерно половины трудно игнорировать: группы воюют за один и тот же верх предложения.
- Ту же логику применяйте к слотам, курьерам и промокодам — меняется только ключ группировки.
Что делают с интерференцией: рынок, переключающиеся периоды, малая доля
Общая идея всех рабочих дизайнов одна: рандомизировать не отдельного пользователя, а кластер, внутри которого происходит перетекание. Если спрос и предложение локальны, таким кластером становится рынок — город или район вместе с датами. Тогда изменение получают все пользователи рынка сразу, и внутри кластера перетекать некуда.
Цена известна заранее. Единиц наблюдения становится не миллион пользователей, а несколько десятков рынков, и они сильно различаются по размеру. Мощность падает в разы, различимым остаётся только крупный эффект. Часть потерянной мощности возвращают парным подбором рынков и учётом их предтестового уровня, но чудес ждать не стоит: в этом дизайне маленький эффект обычно неизмерим.
Второй подход — переключать вариант во времени: рынок живёт час или день на тесте, потом столько же на контроле, порядок случаен. Сравниваются интервалы внутри одного рынка, поэтому различия между городами перестают мешать. Подход работает, только если эффект проявляется быстро и не тянется за границу интервала. Для длинного цикла бронирования он плохо подходит: решение, начатое в интервале теста, завершится уже в контрольном.
Третий подход — признать, что чистый эффект без давления на рынок измеряется только на маленькой доле трафика. Доля в несколько процентов почти не меняет условия для остальных, поэтому оценка ближе к истине. Взамен тест идёт долго и видит лишь крупные изменения.
| Дизайн | Единица рандомизации | Когда подходит | Чем платите |
|---|---|---|---|
| Обычный A/B | пользователь | нет общего ресурса и очереди | перетекание не контролируется |
| По рынку | город или район × период | спрос и предложение локальны | единиц мало, мощность падает в разы |
| Переключающиеся периоды | рынок × интервал времени | эффект виден за минуты и часы | не годится при длинном цикле решения |
| Двухуровневый | рынок, затем доля внутри него | нужно измерить сам размер перетекания | дороже и требует многих рынков |
| Малая доля трафика | 1–5% пользователей | нужна оценка без давления на рынок | долго, различим только крупный эффект |
Побочные метрики выдают перетекание раньше, чем итоговая таблица
Если менять дизайн дорого, остаётся второй путь: оставить обычный A/B, но заранее завести метрики, по которым перетекание обнаружится. Их удобно держать тремя блоками.
Блок рынка отвечает на вопрос, вырос ли пирог: суммарный объём целевого события, число задействованных единиц предложения, доля предложения, которое получило хотя бы один запрос. Блок контроля отвечает, не стало ли ему хуже: его конверсия и время до целевого события в сравнении с прошлыми периодами, а не только с тестом. Блок второй стороны отвечает, не перенесли ли мы издержки на продавца: время ответа, доля отказов, концентрация спроса на верхних объектах.
Падение абсолютных показателей контроля при стабильном рынке — самый прямой сигнал. Его часто не видят, потому что контроль по привычке смотрят только как базу для сравнения, а не как отдельный объект наблюдения.
- Объём по рынку — обязательная метрика теста, а не фон в BI.
- Конверсия контроля сравнивается с его же прошлыми неделями, иначе деградация невидима.
- Доля предложения с хотя бы одной сделкой ловит концентрацию спроса на узком верхе.
- Время ответа и отказы второй стороны — guardrail, который нарушается медленно и потому чаще всего пропускается.
Масштаб Airbnb не переносится: что делать при двадцати городах
Честная оговорка: почти всё, что перечислено выше, в крупной компании держится на инфраструктуре, которой у локального продукта нет. Рандомизация по рынку требует десятков сопоставимых рынков. Переключающиеся периоды — десятков сделок в час на каждый рынок. Двухуровневый дизайн — и того, и другого одновременно. Если у вас двадцать городов и три тысячи заказов в месяц, кластерный тест даст доверительный интервал шириной больше любого ожидаемого эффекта, и формально корректный результат окажется бесполезным.
Что остаётся в таком масштабе. Первое — считать спорный спрос и объём по рынку: это обычные запросы, они не требуют платформы и сразу говорят, насколько сильна связь. Второе — уменьшать долю теста там, где ресурс дефицитен, сознательно меняя мощность на корректность. Третье — для изменений, которые явно перераспределяют спрос, отказаться от A/B в пользу последовательного запуска по рынкам с фиксацией предтестового уровня, честно назвав метод квазиэкспериментом.
И четвёртое, самое дешёвое: написать ограничение прямо в выводе. «Эффект измерен при доле теста 50% на дефицитном предложении, поэтому он включает перераспределение спроса и является верхней оценкой» — это одна строка, которая защищает решение лучше, чем третий знак после запятой в p-value. Непосчитанное смещение, про которое команда знает, безопаснее посчитанного эффекта, про смещение которого никто не написал.
Чеклист перед запуском теста там, где участники связаны
Этот список короче любого design document и закрывает те ошибки, которые потом невозможно исправить на этапе анализа.
- Назовите общий ресурс: инвентарь, очередь, модель, бюджет, рейтинг. Если не нашёл — объясните, почему его нет.
- Оцените спорный спрос запросом до запуска, а не после.
- Выберите единицу рандомизации под этот ресурс, а не по привычке.
- Посчитайте цикл решения и поставьте окно с запасом, а не по калькулятору размера выборки.
- Зафиксируйте список диагностических разрезов: платформа, версия, рынок, новизна пользователя.
- Заведите метрики рынка, контроля и второй стороны в обязательный набор.
- Опишите правило остановки отдельно для вреда и отдельно для результата.
- Заранее решите, что будет написано в выводе, если эффект окажется перераспределением.
Что здесь факт, а что объяснение
Разделю уровни уверенности, потому что для кейса это часть содержания. Публично известно: Airbnb — двусторонний маркетплейс с конечным предложением; компания описывала в своём инженерном блоге проблему ранней остановки тестов, влияние длинного цикла решения на измеряемый эффект и эпизод с нейтральным результатом, за которым скрывалась техническая поломка в одном браузере. Общеизвестно и то, что для связанных участников индустрия применяет кластерную рандомизацию и переключающиеся периоды.
Объяснение, а не факт: конкретные каналы перетекания в Airbnb именно в той пропорции, в которой я их разобрал. Все числа в статье — учебные, они подобраны так, чтобы арифметика была проверяемой, и не являются результатами чьих-либо тестов. Процентов из публикаций компании я не приводил, потому что в пересказах они расходятся, а проверить первоисточник построчно сейчас не могу.
Для работы это различие важнее, чем кажется. Механизм переносится на ваш продукт, потому что он следует из структуры рынка. Числа не переносятся вообще: они зависят от дефицита предложения, доли теста и длины цикла, а все три величины у вас свои.
Итог и куда дальше
Главная ошибка в экспериментах маркетплейса не статистическая, а смысловая: сравнение групп по умолчанию читают как эффект изменения для бизнеса. Там, где участники делят один ресурс, это два разных числа, и расходятся они в разы. Проверка, которая отличает одно от другого, занимает один запрос: изменился ли объём по рынку целиком, пока группы расходились.
Дальше порядок простой. Найдите общий ресурс. Посчитайте, какая доля спроса за него конкурирует. Выберите единицу рандомизации под этот ресурс, а окно — под цикл решения. Заведите в набор метрики рынка и второй стороны. А в выводе напишите, какую часть эффекта вы измерили, а какую — нет: именно эта строка делает результат переносимым на следующее решение.
Материалы по теме
Эксперименты Booking.com: дисциплина решений вместо значимости
Как устроена культура A/B-тестов Booking.com и какая у масштаба обратная сторона: множественные сравнения, эффект новизны, пересечение тестов и локальная оптимизация одного экрана. Что из этого переносится в команду на 3–10 тестов в квартал.
Spotify: почему хороший эксперимент не обязан выиграть
Spotify публично считает learning rate 64% при win rate 12%: как устроен их критерий «эксперимент с обучением», почему нейтральный тест без мощности не считается знанием и что записать в карточку до старта, чтобы отрицательный результат стал решением.
Платформа экспериментов Spotify: что взять команде с 20 тестами в год
Spotify сделала из A/B-теста элемент инфраструктуры: словарь метрик, exposure вместо назначения, guardrail по умолчанию, разведение тестов, проверка сплита. Разбираем механизм и минимальный контур для небольшой команды.