Джейсон Гольдштейн и Джульетта Саболь

Это тонкая проблема, но очень важная. Читая статью, вы пропускаете рекламу, подписку на информационные бюллетени, ссылки на связанные статьи, модули книг, виджеты журналов и всевозможные другие контекстные инструменты, которые связывают наших читателей с остальной частью публикации.

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

В то же время слишком многие сайты переусердствуют до такой степени, что эти элементы вытесняют сам контент, который они предназначены для поддержки.

Мы думаем об этом не как о балансе, а как о форме искусства: если пространство ограничено, нам нужно более разумно использовать его, чтобы мы могли оптимизировать как читательский опыт, так и потребности бизнеса. в то же время.

Финал - это великая теория того, что мы называем Unified Yield: способность учитывать читателя, его отношение к The Atlantic, контекст статьи, которую он читает, и динамически дополнять ее. страницу с правильными элементами, которые имеют наибольший смысл и наиболее эффективны в данный момент.

Для этого нам нужна гибкая, мощная и высокоточная система для вставки компонентов на страницу новой статьи.

Ограничения серверной инъекции

Впервые мы занялись этой проблемой в 2014 году. Мы собирались запустить адаптивный редизайн, и видимость изменила природу рекламной экосистемы: теперь мы могли размещать рекламу по всей истории. Когда вы - издание, известное своей длинной художественной журналистикой, это действительно большое дело.

Мы создали инжектор на Python, который попытался бы разместить их разумно.

При запуске на сервере код не мог знать высоту экрана, и, хотя он мог читать контент как HTML, он ничего не мог знать о макете. Вместо этого, если бы мы хотели показывать одно объявление на экране, нам пришлось бы угадывать.

Это сложно. Вы можете подсчитывать элементы, но некоторые абзацы длинные, некоторые короткие, вам нужно учитывать изображения всех размеров, а цитата - это отдельный элемент, который может работать долгое время.

Вместо этого мы использовали количество символов в качестве прокси для длины. На компьютерах мы пытаемся размещать рекламу каждые 2800 символов или примерно каждые 560 слов. Со временем мы узнали, что лучше всего будет, если мы будем размещать их только между двумя абзацами, полностью избегая изображений и специальных виджетов.

Но мы знали, что это не идеально. В конце концов, это тупой инструмент.

Оценка одного объявления на экран - это не на самом деле одно объявление на экран. Он боролся с плавающими элементами, которые находились бок о бок с другим контентом, застревал на краю, и его было трудно тестировать или настраивать с нужным нам уровнем точности.

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

В то же время мы зациклены на опыте читателя и не хотим загромождать статью мебелью.

Мы хотим разумно распределить все эти вещи, используя стратегии и алгоритмы, которые учитывают фактический размер устройства, длину и характер статьи, а в некоторых случаях и то, что мы знаем о читателе, чтобы сформировать производительную и элегантную систему.

В 2021 году мы решили решить эту проблему более изощренным способом. И для этого, учитывая истинный размер экрана устройства и макет содержания статьи, необходимо запустить код инжектора в браузере. .

В браузер

Это непросто.

Во-первых, у нас есть серьезные опасения по поводу производительности, изменения макета и кумулятивного сдвига макета. Это означало, что это должно быть быстро: все элементы должны быть на своих местах, прежде чем читатель перейдет к ним.

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

Переход на одноколоночную компоновку был радикальным изменением, но с этим все согласились. Команды Design и Story Engineering хотели, чтобы у них была больше гибкости, чтобы вводить в статью новые визуально интересные модули. Реклама была готова отказаться от инвентаря в правом рельсе в обмен на мощность и гибкость, которые предлагают новые форсунки. Но, прежде всего, мы считали, что оптимизированный макет был удобнее при чтении.

Это будет текущая тема на нашей новой странице статьи, и она будет не столько о компромиссах или счастливых случайностях, сколько о тщательном проектировании машины, чтобы каждая деталь встала на место.

На низком уровне это означало, что код должен как можно меньше касаться DOM (HTML документа). Потребуется один проход, чтобы измерить высоту каждого компонента, сохранить это в облегченной структуре данных, а весь остальной процесс будет сравнивать числа. Логические сравнения обходятся браузеру намного дешевле, чем прикосновение к реальным элементам макета - вот почему популярные фреймворки, такие как React, имеют виртуальную DOM. Когда вы выполняете большое количество операций, это может иметь значение.

Наконец, нам нужно, чтобы статья была доступна нашей кодовой базе в виде структурированных данных. Это позволило нам определить разницу между всеми видами вещей, которые появляются в статье, без выполнения сложной эвристики в DOM: мы бы знали, что не следует размещать рекламу после dropcap - что выглядело бы ужасно - потому что оно помечено как абзац с буквицей.

Одна из наших больших историй успеха как команды - устранение трения при AB-тестировании сложных систем. Если машину легко настраивать и тестировать, мы можем быстро проводить всевозможные оптимизации и эксперименты.

Это было главной задачей при разработке новой системы. В старой системе нам приходилось обновлять основную бизнес-логику, чтобы изменить инжектор, обновить модульные тесты, чтобы они соответствовали ей, и даже небольшие изменения могли быть чреваты трудностями.

Новая система будет отделять основную логику, которая является сложной, от конфигурации, которая будет удобной, доступной и простой в использовании.

На основе набора правил

Цель этой системы - правильно распределить внедренные элементы и избегать размещения их в позициях, которые выглядят не очень хорошо, и конфигурация это отражает. Он состоит из списка правил, каждое из которых указывает, куда внедренный элемент не может попасть.

По состоянию на 1 августа действующие правила:

  • Внедренные элементы должны быть минимум на 150 пикселей ниже и на 100 пикселей выше фигуры (изображение / видео).
  • Введенные элементы не могут находиться рядом с цитатой или цитатой.
  • Введенные элементы не могут быть сразу после заголовка
  • Введенные элементы не могут быть непосредственно перед списком
  • Введенные элементы не могут быть сразу после абзаца «приветствия», заканчивающегося запятой или двоеточием.
  • Внедренная реклама должна быть примерно на 1 высоту браузера после последней введенной рекламы, но не ниже 700 пикселей (во избежание слишком большой плотности на мобильных устройствах).
  • Внедренная собственная реклама (информационные бюллетени, подкасты, прямые трансляции) должна располагаться не менее чем на 1/2 высоты браузера после внедренной рекламы.
  • Любой внедренный элемент должен располагаться на расстоянии не менее 200 пикселей от конца статьи.
  • Внедренные элементы должны быть на 250 пикселей после введенного модуля рециркуляции, который плавает.
  • Любой введенный элемент должен находиться на расстоянии не менее 250 пикселей от не введенных рециркулирующих блоков.
  • Первый введенный элемент не может быть непосредственно перед абзацем с буквицей.
  • Между верхней частью статьи и первым внедренным элементом на мобильном устройстве должно быть не менее 320 пикселей, а на рабочем столе - 200 пикселей.

Синтаксис правила небольшой и его можно сканировать, но он может многое. Каждое правило описывает тип элемента, который мы будем вставлять, тип компонента верхнего уровня, от которого нужно держаться подальше, и то, как далеко он должен быть сверху или снизу.

Вот пример:

{
  item: InjectedTypes.Any,
  after: "150px",
  before: "100px",
  from: ArticleElementTypes.Image,
}

Итак, здесь говорится, что любой внедренный элемент, независимо от его типа, должен быть не менее 150 пикселей после и 100 пикселей перед элементом фигуры.

В этом много силы.

Многие из наших требований заключаются в том, чтобы избегать прямых смежностей. Например, мы не хотим вставлять что-либо между заголовком раздела и его первым абзацем.

Мы можем описать это, сказав инжектору, чтобы он находился на расстоянии не менее 1 пикселя от нижнего края заголовка.

{
  item: InjectedTypes.Any,
  after: "1px",
  from: ArticleElementTypes.Heading,
}

При наличии этого правила инжектор может размещать рекламу прямо над заголовком - это выглядит нормально - но если он проверяет место прямо под заголовком, он увидит, что расстояние равно нулю, и перейдет к следующей позиции.

Наконец, поскольку правила могут содержать любую логику, которая может выполняться в браузере, когда вам нужна более сложная логика, она доступна. Мы хотим, чтобы объявления размещались как минимум на расстоянии одного экрана друг от друга, но мы также не хотим, чтобы контент был перегружен контентом на маленьких телефонах.

Мы можем установить правило, чтобы использовать большее из высоты экрана или 700 пикселей.

{
  item: InjectedTypes.Ad,
  after: `${Math.max(parseToPx(“100vh”), 700)}px`,
  from: InjectedTypes.Ad,
}

Сравнивая заметки с нашими редакторами, мы обнаружили довольно много случаев, когда реклама появлялась сразу после строки Дорогой терапевт, которая постоянно встречается в популярной серии статей Лори Готтлиб, где она отвечает на письма. Это довольно неприятно и просто ужасно выглядит. Мы добавили правило для описания любого абзаца, заканчивающегося запятой или двоеточием, как Приветствия, и затем могли указать инжектору, что он никогда не должен помещать ничего непосредственно между этой строкой и ее открывающим абзацем.

Правило выглядит так же, как и для заголовков.

{
  item: InjectedTypes.Any,
  after: “1px”,
  from: ArticleElementTypes.Salutations,
}

Поскольку система очень гибкая, мы можем исключить этот крайний случай так быстро, как только сможем его описать.

Алгоритм

Чтобы использовать их, инжектору необходимо преобразовать статью в понятный формат.

Мы разделили нашу статью на список компонентов верхнего уровня и их верхнее и нижнее положение по Y, которые мы можем перебрать, чтобы решить, где безопасно разместить внедренный объект.

Наш алгоритм внедрения проходит через статью один раз сверху вниз. После каждого элемента статьи, каждого абзаца, каждого изображения он спрашивает: «Могу ли я здесь что-то добавить?»

Во-первых, он проверяет, что следует вводить - в большинстве случаев рекламу, но иногда рекомендуемый модуль чтения или рекламную рассылку.

Оттуда он проверяет конфигурацию, чтобы увидеть, разрешен ли этот внедренный элемент в этой позиции. Для каждого применимого правила он смотрит сверху и снизу позиции, чтобы увидеть, достаточно ли она далеко от типа элемента, которого она избегает, сравнивая разницу в позициях Y с требуемым расстоянием. Если это достаточно далеко, правило действует. Если он находит то, что ищет, он терпит неудачу и переходит к следующей позиции.

Если все правила пройдут, он поместит объявление в массив компонентов и перейдет к следующему шагу.

И последняя загвоздка: CSS плавает

Есть еще один осложняющий фактор. Хотя приятно, что наша статья представляет собой простой список элементов, которые не вложены друг в друга, они могут перекрывать друг друга. Например, предположим, что изображение плавает рядом с телом статьи.

Если мы попытаемся разместить объявление во всю ширину между элементами 1 и 2, это приведет к беспорядку на странице.

В начале процесса мы уже записываем вертикальные координаты (позиции Y) каждого компонента верхнего уровня на странице. Мы можем прокручивать эти элементы, сравнивая их верхнее и нижнее положение по оси Y, и определять, перекрывают ли какие-либо элементы друг друга. Они образуют «перекрывающиеся блоки» - разделы статьи, в которые ничего нельзя вводить.

В инжекторе на стороне сервера не было возможности с уверенностью узнать, как далеко будет висеть это изображение. Мы избегали подобных дизайнов, потому что они были источником головной боли. С новым инжектором мы можем измерить его и с точностью отреагировать.

Инжектор оживает

Через два месяца после запуска этот проект можно смело назвать безоговорочным успехом.

Новый инжектор умнее. Будучи решателем ограничений с точностью на уровне пикселей, он нацелен на одно объявление на просмотр страницы с жестким нижним пределом (будет приятным для читателей с маленькими телефонами), создавая удобство работы как для 27-дюймовых мониторов, так и для миниатюрных телефонов.

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

Мы смогли добавить более тонкие правила, чтобы предотвратить неудобное размещение и непреднамеренные побочные эффекты, которые могут повредить дизайну.

Между тем, наш упор на то, чтобы сделать систему доступной и легко модифицируемой, окупился. Мы внесли десяток корректировок в конфигурацию на основе реальных наблюдений. Мы выпустили наш первый крупный AB-тест для измерения поведения модулей подписки на информационные бюллетени. Инженеры из наших историйных команд смогли задействовать фреймворк, чтобы вдумчиво и разумно включить новые модули выпуска книг и журналов.

Благодаря гибкости и точности нового инжектора мы могли внести почти все эти изменения в мгновение ока.

Возможность так быстро изменить систему и увидеть, каковы будут последствия, - это глоток свежего воздуха для разработчиков и ключ к способности действовать быстро, действовать в соответствии с данными, больше экспериментировать и улучшать результаты.

Такой уровень скорости и мощности открывает дверь к совершенно новому диапазону возможностей и реализации великой теории Unified Yield, согласно которой мы можем разумно разместить нужные элементы перед нужными людьми справа. время, оптимизировать для нужд бизнеса и поддерживать потрясающий читательский опыт при этом.