company_banner

Как переписать фронтенд нагруженного проекта и не потерять главного

    Обложка статьи


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


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


    Знакомо? Если да, то вы в непростой ситуации. Она закономерно возникает в большинстве проектов, которые за годы своего существования накопили достаточный объём legacy.


    Возможно, вас ждут и такие опасности:


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

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


    Недавно команда фронтенда Яндекс.Маркета также столкнулась с описанной ситуацией. У нас начало расти время разработки нового функционала, появились участки кода, которые могут сопровождать только «старички», нанимать и адаптировать новых членов команды стало сложнее и дороже. Чтобы справиться с этим, нам пришлось обновить свой технологический стек и переосмыслить часть процессов разработки и поставки нового функционала.


    Как менялся фронтенд Маркета


    Яндекс.Маркет существует уже 19 лет. Подумать только, пройдёт ещё немного времени, и он будет старше некоторых своих разработчиков. За эти годы фронтенд был переписан несколько раз.


    XSLT


    На заре развития сервиса и в последующие десять лет основой фронтенда Маркета была технология XSLT. На клиенте использовались JS-скрипты с рядом вспомогательных библиотек, БЭМ и набор портальных UI-компонентов Яндекса. Cерверная часть фронтенда работала на XScript — внутренней разработке Яндекса, базирующейся на XSL-трансформациях.


    Главная страница Яндекс Маркета на XSLT


    Вот так декларативно делались http-запросы к бэкендам на фронте Маркета в 2015 году:


    <w.resource id="market-touch:models-top">
        <x:http>
            <x:guard type="StateArg">market:models-top.ids</x:guard>
            <x:method>getHttp</x:method>
            <x:param type="StateArg">market:config.some-host</x:param>
            <x:param type="String">GetCards?ids=</x:param>
            <x:param type="StateArg" as="Long">market:models-top.ids</x:param>
            <x:param type="String">&amp;type=micro</x:param>
            <x:param type="String">PopularModels?</x:param>
            <x:param type="String">&amp;region=</x:param>
            <x:param type="String">&amp;n=</x:param>
            <x:param type="StateArg" as="Long">market:models-top.count</x:param>
            <x:param type="StateArg" as="String">market:exp.guru-params</x:param>
        </x:http>
    </w.resource>

    А вот такие сообщения об ошибках получал разработчик:


    <xscript_invoke_failed reason="UNKNOWN"
       object="Yandex/Example/Example :
          exampleRequestAuth /usr/local/www/assessor-ng/test.xml"/>

    Представьте, сколько времени потребовалось бы, чтобы передать все необходимые знания новому члену команды в 2020 году. Неприемлемо много для быстро развивающегося проекта.


    NodeJS


    В 2016 году фронтенд Яндекс.Маркета перешёл на NodeJS. Это было непросто и потребовало много времени. Процесс был поступательным, в нём приняло участие большое количество инженеров Маркета. Использование NodeJS позволило нам работать с одним языком, решая задачу сразу в двух окружениях: на сервере и на клиенте. Появились полиморфные части кода и новая архитектура серверной части фронта. Был придуман и запущен процесс обучения команды. Мы стали эффективнее.


    Изменения произошли и на клиенте. Появился новый JS-шаблонизатор Yate (Yet Another Template Engine). Он был разработан внутри Яндекса и наследовал некоторые идеи из XSLT. Использование этого шаблонизатора позволило заменить лишь инструмент, частично сохранив парадигму в умах разработчиков, которые до этого несколько лет работали с XSLT. Нам удалось ускорить обновление кодовой базы Маркета и сократить время на адаптацию новых сотрудников. Yate когда-то даже был выложен на GitHub и имеет свой playground. Попробуйте, если интересно.


    Главная страница Яндекс Маркет 2015 год


    Актуальный стек


    В 2019 году технологический стек фронтенда Маркета обновился вновь. На сервере появилась статическая типизация (Flow), была доработана архитектура, вынесен общий код. Страницы разделились на новые виджеты и начали загружаться на клиент прогрессивно.


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


    Виджеты на странице категории Яндекс Маркета


    Что касается прогрессивной загрузки, то в классическом подходе html-страница в рамках одного GET-запроса компонуется (шаблонизируется) на стороне сервера, затем отдаётся браузеру. Происходит это по такой схеме:


    Схема классической модели загрузки web-страницы


    В модели, реализованной на фронте Маркета, сервер оперирует виджетами, которые отдаются на клиент чанками, сразу отображаются и инициализируются. То есть пользователь уже может что-то набирать в поисковой строке, пока страница ещё догружается. А в это время на сервере будут запрашиваться данные для других частей страницы. Важно, что это происходит в рамках одного обычного GET-запроса/ответа.
    Такая загрузка страницы напоминает прогрессивную подгрузку JPEG. Отсюда и название.


    В сравнении с классической (слева), прогрессивная загрузка (справа) выглядит так:


    Схема прогрессивной модели загрузки web-страницы


    Больше деталей можно найти в этом докладе.


    Клиент мобильной web-версии Маркета мы перевели на React + Redux. Почему не на Vue.js? Мы устроили соревнование между двумя фреймворками. Две команды энтузиастов портировали страницу поисковой выдачи Маркета на обе технологии. По итогам внутренних бенчмарков и тестов React в нашем проекте показал себя лучше. К тому же он был более распространён в других проектах компании.


    После завершения текущего обновления последуют новые. Что будет меняться — зависит от трендов в индустрии фронтенда. Поживём — увидим.


    Проводить подобные обновления сложно. Hо сложность эта полностью преодолима при правильной подготовке. Важно помнить, что если обновления не планировать и не проводить вовсе, то «сложно» со временем обязательно превратится в «невозможно», и вас могут ждать уже другие опасности. Не стоит до этого доводить.


    Как переписать фронтенд нагруженного проекта


    Оглянувшись на недавнее обновление технологического стека в Маркете, приведу несколько советов.


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


    Изучайте популярные технологии и тренды в индустрии. Стройте прототипы с использованием этих технологий в песочнице. Мы проделывали это с поисковой выдачей Маркета.


    Стремитесь заранее понять, что подойдёт вашему сервису, а что нет. Не поддавайтесь хайпу, принимая решение внедрять что-то в проект. Смотрите на свои прототипы и результаты их тестирования. Оцените готовность команды работать с перспективной технологией. Если технология не подходит вашему сервису или команде, ищите альтернативы.


    Зафиксируйте состояние ключевых метрик. Определите, какие метрики играют ключевую роль для вашего бизнеса, откуда и как сервис получает трафик, какие существуют внешние SLA, как распределяется нагрузка, когда стартуют маркетинговые компании и так далее. Что точно нельзя сломать? Следите за ключевыми метриками по ходу работ.


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


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


    При переводе Маркета на React нам пригодился и альтернативный план. Одну часть сервиса мы портировали постранично, другую — блоками и виджетами.


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


    Обновление Маркета мы начали с самых важных страниц: главной, поисковой выдачи, карточки товара. Это самые конверсионные, самые нагруженные и самые технически сложные страницы сервиса.


    Учитывайте изменения окружающего контекста. Не старайтесь продумать детальный план на год вперёд. Параллельно с вашей работой сервис будет изменяться другими людьми. Будьте готовы обновлять и дополнять ваш план по ходу движения к цели.


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


    Достаньте с высокой полки и изучите старые гайды по дизайну. Если их не было, переносите стили как есть (оптимизировать будете после), проводите дизайн-ревью обновлённой части сервиса. Только не пытайтесь проводить редизайн продукта при переходе на новую технологию. Переходите как есть.


    При переезде на React у нас не было никаких дизайн-гайдов, только исходники. Макеты существующих страниц, которые нам удалось найти, не особо помогли. Было непросто. Чтобы ничего не упустить, мы проводили дополнительные дизайн-ревью на этапе разработки, писали скриншот-тесты, привлекали к проверке тестировщиков из смежных команд, так как они хорошо знают UI сервиса. После релизов мы внимательно следили за обращениями пользователей в службу поддержки Маркета и оперативно исправляли недочёты.


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


    Мы продумали файловую структуру заранее, зафиксировали и описали её в документации. Перестроили и проверили сборку до начала работ по обновлению основного функционала.


    Не игнорируйте тесты. Полезны будут все виды тестов: unit, интеграционные, e2e. Если можете, допишите тесты на ключевой функционал до старта работ по переходу на новую технологию. Если тестов нет и писать некогда — пишите по ходу работ. Рефакторинг без тестов возможен, но больше похож на вождение автомобиля с закрытыми глазами. Может и пронесёт.


    Замеряйте клиентские метрики вашего ресурса: TTI, TTR, TTFB, TTLB и другие. Зафиксируйте показатели до начала работ и следите за ними по ходу продвижения к цели. Это поможет обнаружить деградации вовремя, сэкономит время и силы команды, сбережёт деньги бизнеса.


    В Маркете установлены SLA на ряд клиентских метрик: TTI (time to interaction), TTR (time to render) и другие. При переходе на React мы неоднократно выходили за допустимые границы скорости работы нашего сервиса. Наличие SLA позволяло нам сразу замечать деградации, оптимизировать проблемный код до поставки в продакшн.


    Воспользуйтесь A/B-тестированием при обновлении важных частей сервиса. Проверить технические усовершенствования в эксперименте только на срезе аудитории сервиса — хорошая идея. Если что-то пойдёт не так, расследовать деградации и чинить баги будет проще.


    Во время портирования мобильной web-версии Маркета на React, мы провели почти 200 дней экспериментов. Однажды A/B эксперимент даже помог нам обнаружить и устранить нетривиальную утечку памяти в браузере. В экспериментах мы неоднократно видели, как незначительные, на первый взгляд, изменения могут негативно влиять на ключевые бизнес-показатели Маркета.


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


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


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


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

    Яндекс
    Как мы делаем Яндекс

    Комментарии 17

      +2
      Не пробовали использовать mobx? Работа с redux создает ощущение использования sql и хранимых проуедур). Очень много шаблонного кода
        0
        Mobx на проекте пока не примеряли. Желание попробовать в песочнице есть. Спасибо за предложение.

        P.S. Про Redux согласен — многословно, но проблемой для нас не является.
          0

          После XML/XSLT даже Redux кажется образцом лаконичности :)

            0
            это да :), был опыт на SharePoint
          +1
          Хороший конспект для любого migration campaign, не важно фронтенд или нет (мы планируем переделывать бекенд). Спасибо!
            0
            Какие инструменты посоветуете для A/B тестирования frontend'a?
              0
              Можете подсказать чем рисовали столь красивые диаграмки?
                –1
                Плюсую!
                  0
                  Рисовали в Adobe Illustrator
                  +1

                  Почему Flow в 2019?

                    0
                    Тоже очень интересно. Почему на таком серьёзном проекте такая неинтерпрайзная тулза.
                      0
                      Когда начинали переезд TypeScript проигрывал Flow по некоторым важным моментам.
                      И к тому же требовал более жестких мер для внедрения, тогда как flow можно было внедрять постепенно
                      0
                      Как и большинство советов от крупных компаний — они подходят только для крупных компаний. Для мелких и средних это все не потянуть команде, но зато весьма существенно увеличится тех. долг со временем. Да и по-большому счету многое можно не делать, так как влияние на бизнес — минимальное. Keep it simple.
                        0

                        Всё или не всё потянуть могут мелкие и средние компании под вопросом. Прежде всего в части метрик — крайне редко они встречаются в "дикой природе" на фронте.


                        Но вот если исключить метрики, то из советов мелкие компании могут потянуть вполне почти всё остальное.


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

                          0
                          В мелкой компании 1-2-3 frontend developer'ов, теоретически, они могут поднапрячься и добавить unit/acceptance/blackbox тесты, провести несколько A/B тестов, развернуть подходящую инфраструктуру, но это же все добро надо документировать и поддерживать. Если этого не делать, то уже через полгода это превращается в огромную головную боль и начинает сильно устаревать и рассинхронизироваться с текущей актуальной версией. Даже для средней компании это все требует хорошего бюджета и выделения процентов 20 времени. При этом любой рефакторинг проекта будет стоить ооочень дорого.
                            0

                            Да, A/B тесты это к "потянуть вполне почти всё остальное". А так не раз и не два участвовал в проектах/эпиках типа перехода на React с jQuery-like или добавление стейт-менеджера, если по фронту. И даже без автотестов обходились.

                        0

                        Вот ещё важный пункт: в процессе перехода нужно максимально фокусироваться на нём, только критические бизнес-задачи делать, а niсe-to-have ждут окончания перехода тех частей, которые они затрагивают. Так суммарное время и перехода, и этих задач получается меньше.


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

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

                        Самое читаемое