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

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

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

Тем не менее, вот демонстрация чат-бота LeadBooster для всех, кому интересно.

Позвольте мне добавить сюда несколько важных технических аспектов. Поскольку эта служба должна быть доступна для всех по всему миру, мы официально поддерживаем браузеры вплоть до Internet Explorer 11. Чат-бот внедряется на веб-сайт с помощью фрагмента кода JavaScript, как и любые другие службы или аналитики, к которым вы привыкли.

<script>
  window.pipedriveLeadboosterConfig = {
    base: 'leadbooster-chat.pipedrive.com',
    companyId: ...,
    playbookUuid: '...',
    version: 2
  };
</script>
<script src="https://leadbooster-chat.pipedrive.com/assets/loader.js" async></script>

Укладка, укладка и снова укладка

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

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

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

Первые проблемы

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

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

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

body button {
    color: red;
}

Конечно, многие читатели сталкивались с этой проблемой раньше, независимо от обстоятельств, поэтому я не буду углубляться в тему специфики CSS. Ведь эту тему много раз обсуждали и есть даже калькулятор специфичности CSS.

К счастью, эту проблему можно быстро определить с помощью той же методики «попытка → неудача → обучение». Внедрение чат-бота в часто используемые веб-сайты, такие как популярные шаблоны WordPress, показало большинство проблем, большинство из которых можно решить, увеличив специфичность селекторов чат-ботов.

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

html body #pipedrive-chat-holder { ... }

Опасности важного

Следующая волна проблем не заставила себя долго ждать, и, как следует из заголовка, проблема заключалась в следующем:

button {
    color: red !important;
}

Печально известные и опасные !important. Любой, кто читает эту статью, вероятно, знает, что эта функция CSS в основном предназначена для перезаписи значений, установленных во встроенных элементах, неконтролируемым источником, и не должна использоваться для простого увеличения специфичности селекторов. Конечно, есть много мест, где использование !important является действенным, хорошо продуманным решением, но давайте посмотрим правде в глаза, мы все использовали его один или два раза таким «плохим способом». Я знаю, что есть.

Проблема с !important проста: как только она будет введена, уже не будет пути ее решения. Вы не можете бороться !imporant с помощью обычного набора инструментов CSS. Это похоже на волшебное и бессмертное существо - его нельзя убить. Единственное, что может победить этого монстра, - это еще один !imporant.

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

Проблемы с !important были менее распространены, но позже начали появляться у пользователей с сообщениями о «сломанном чат-боте».

Поскольку !important обычно применялся к некоторым более конкретным тегам HTML, таким как button, возникали и неприятные побочные эффекты. Эта проблема практически заблокировала прогресс «хороших парней» фронтенд-разработчиков в нашей команде, которые хотели сделать чат-бота более доступным, введя семантический синтаксис HTML5.

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

Коллизии JavaScript

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

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

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

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

Вывод был сделан за считанные секунды - пользователь заходит на сайт в IE 11 или более ранней версии, а полифил отсутствует.

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

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

Поскольку fetch не был функцией, это должно было быть что-то еще. Вы могли подумать, что найдется null, но на этот раз этого не произошло. Быстрая проверка того, что такое глобальная переменная fetch на странице, показала нам следующее:

window.fetch  // window.fetch = 4

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

Каркасы

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

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

Как и любой другой человек в нашем положении, мы серьезно отнеслись к такой жалобе и немедленно приступили к исследованию проблемы. Мы установили чистую установку Angular, проложили несколько маршрутов и приступили к тестированию. Одно привело к другому, и знаете что? Сайт сломался. Он не был сломан таким образом, чтобы проблема была сразу заметна, но все же существовала действительная проблема.

Видите ли, как упоминалось ранее, LeadBooster должен работать везде, включая менее удачные браузеры, такие как Internet Explorer 11. Это означает, что наш код должен включать несколько полифиллов.

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

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

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

Окончательное решение

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

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

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

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

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

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

Заключение

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

Напротив, IFrame, будучи в некотором смысле устаревшим инструментом, может быть невероятно полезным и, по сути, единственным решением определенных проблем, существующих в Интернете сегодня. Конечно, есть список недостатков IFrame, но дело в том, что все они решаемы. IFrame предлагает четкое разделение кода, и в настоящее время это невозможно по-другому. Никогда бы не подумал, что это скажет, но IFrame по-прежнему занимает полезное место в 2020 году.

Если кому-то интересно, вот демонстрация чат-бота LeadBooster.