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

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

Немного контекста

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

Мы пишем большое SPA с поддержкой серверного рендеринга, в котором более сотни роутов. Под капотом используем Node.js для SSR, пишем на React-Redux без бойлерплейта. Весь код у нас статически типизирован с помощью Flow и TypeScript, собираем проект Webpack’ом.

Инструменты
Инструменты

Зачем нужны ежедневные релизы и что для этого надо

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

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

Минимальный джентльменский набор, который понадобится:

  • тесты;

  • автоматизация процессов деплоя;

  • онлайн мониторинг продакшена.

Далее про все это более подробно.

Настраиваем три вида тестов

Начнем с тестов нескольких видов: юнит-тестирование, скриншотные тесты и интеграционные тесты.

Юнит-тесты

У нас написано более 1000 юнит-тестов. Используем тестовый фреймворк mocha.  Также есть метрика покрытия кода тестами, но к ней относимся без фанатизма. Достаточно определенного не слишком высокого порога, ниже значений которого тесты упадут.

Скриншотные тесты

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

В рамках скриншотного тестирования у нас тоже более 1000 тестов. Они проходят за 11 минут в CI. Мы даже написали собственное решение, которое занимает порядка 100 строк кода (чуть подробнее про него — ниже). Начать стоит с того, что у нас своя библиотека компонентов на React и есть витрина, в которой эти компоненты представлены. Этой витриной пользуются разработчики, дизайнеры и менеджеры.

Витрина компонентов
Витрина компонентов

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

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

it('components', async ({ browserName, component, version, page }) => {
  const [componentName, index] = component.split('.');
  await page.goto(`${STYLEGUIDE_URL}/#/${componentName}`);
  const elements = await page.$$(STYLEGUIDE_PREVIEW_SELECTOR);
  const screenshot = await elements[index].screenshot();
  expect(screenshot).toMatchSnapshot(
    `${componentName}-${index}-${version}-${browserName}.png`
  );
});

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

Актуальное изображение и диф
Актуальное изображение и диф

Интеграционные тесты

Всего вышеизложенного недостаточно для того, чтобы выявить проблемы в бизнес-логике. Для этого у нас еще есть интеграционные тесты. Их пишет команда тестирования. Подробнее о том, как они устроены, рассказал в блоге наш QA Lead . Я лишь отмечу, что их более 2000.

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

Автоматизация процессов деплоя

Мы работаем по классическому Git flow. Есть ветка master (код из мастера находится на продакшн), и есть ветка dev, где ведется основная разработка, и от нее делаются фиче-ветки.

Также есть релизная ветка. Это ветка, которая создается от ветки dev и потом  вливается в master и dev. Таким образом на продакшн поставляется новый код.

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

Git flow и уведомления
Git flow и уведомления

Затем приходит уведомление, где отмечается дежурный фронтенд-разработчик. Его задача — пройти по ссылке и нажать кнопку «Катим релиз». После этого релизная ветка вливается в master и dev. 

Все, что вливается в master, автоматически начинает собираться и раскатываться на продакшн, о чем также приходит уведомление: «У нас запланирован деплой». После того как все собралось и успешно раскатилось на боевые сервера, нам приходит уведомление, что все прошло успешно.

Онлайн мониторинг

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

Мониторинг сервера
Мониторинг сервера

Среди метрик — количество 200-х, 300-х, 400-х, 500-х ошибок и другие данные: сколько потребляется памяти и так далее. Также на графиках вертикальной зеленой линией отмечается релиз и есть соответствующие значения за предыдущий период. После релиза нас больше всего интересуют ошибки.

Мониторинг ошибок
Мониторинг ошибок

Чтобы видеть детали ошибок, у нас настроен Sentry — клиентский и серверный. Он нужен для мониторинга ошибок в рантайме. Там можно отфильтровать проблемы по релизу и убедиться, что нет всплеска ошибок. Если появляется новая ошибка или какой-то аномальный всплеск, также сразу приходит уведомление в слак.

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

Мониторинг производительности
Мониторинг производительности

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

Lighthouse CI
Lighthouse CI

Проблемы в релизе

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

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

Итоги и планы

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

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

Комментарии (26)


  1. mgis
    16.11.2021 12:14
    +1

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


  1. laatoo
    16.11.2021 12:36
    +6

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

    Не нахожу убедительными оба ответа.


    "Бизнес хочет «фичи на бою». Таким образом он оценивает динамику разработки

    Я не понимаю что это значит.


    Бизнес хочет видеть бурную деятельность, и вы всем штатом её предоставляете, "на все деньги"? В моём представлении бизнес интересует динамика прибыли.


    Больше релизов != больше денег


    Если система построена на ежедневных релизах, а значит, на небольших изменениях, намного проще и быстрей находить проблемы

    Больше релизов = больше багов.


    И ведь действительно: в погоне за частотой релизов и скоростью вы неизбежно их создаёте, а потом героически преодолеваете.


    И вроде бы все довольны: бизнесу приятно видеть бурную деятельность "на все деньги", менеджменту приятно набивать KPI (раздробив один нормально оттестированный и продуманный крупный релиз на десятки маленьких), но все равно попахивает.


    Или я чего-то сильно не понимаю, или это какой-то слишком эффективный менеджмент, за гранью здравого смысла


    xxx: Посмотрите на этот чудесный восходящий график! В этом месяце мы закрыли в 10 раз больше тикетов, чем во всем прошлом! Мы эффективны!
    yyy: В прошлом месяце тикетов было в 20 раз меньше


    1. Ilusha
      16.11.2021 13:06
      +1

      Больше релизов = больше багов.
      в погоне за частотой релизов и скоростью вы неизбежно их создаёте

      Больше релизов - это уменьшение размера каждого релиза. Не вижу причин увеличения количества багов от того, что вместо 1 деплоя на 10 фич стало 10 деплоев по одной.

      Но слишком эффективным менеджментом тут за версту пахнет.


      1. laatoo
        16.11.2021 13:31
        +2

        Не вижу причин увеличения количества багов от того, что вместо 1 деплоя на 10 фич стало 10 деплоев по одной

        Обычно погоня за количеством+скоростью заканчивается примерно так:


        feature1Output(){
            return "whatever business wants";
        }
        
        feature1OutputTest(){
            return assertEquals("whatever business wants", feature1Output());
        }

        А потом они уходят (с)


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


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


        Утрирую, конечно. На мой взгляд сама гонка за количеством релизов — ложная.


        1. Ilusha
          16.11.2021 18:19

          Здесь релизят не быстро, а часто. А требование делать это часто вполне себе обосновано как с технической, так и с продуктовой стороны.

          Есть некоторые недостатки, да. Недостатки есть и у больших релизов. На которые тоже может оказываться давление: график поставок определен, список обязательных фич тоже. И нужно успеть :)


    1. Spunreal
      16.11.2021 15:55

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

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


      1. Artcloud1981
        19.11.2021 12:20

        Большие фичи, бьются на несколько частей, и завозятся на прод поэтапно.


    1. petersolopov Автор
      16.11.2021 15:59
      +2

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

      И ведь действительно: в погоне за частотой релизов и скоростью вы неизбежно их создаёте, а потом героически преодолеваете.

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

      Если бизнес оценивает KPI в кол-ве задач это грустно. Но это не наш случай. 


      1. laatoo
        17.11.2021 12:28
        +2

        У нас каждый день есть небольшие улучшения которые выкатываются на продакшн

        можете привести пример такого улучшения, которое вы делаете за день?


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


        пользователям точно нужно, чтобы каждый день их UX улучшали?


        пользователи точно считают эти ежедневные изменения улучшениями?


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


        1. intet
          17.11.2021 13:16

          'можете привести пример такого улучшения, которое вы делаете за день?'

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


          1. laatoo
            17.11.2021 13:41

            так понятно, спасибо


      1. HellMaster_HaiL
        17.11.2021 12:41
        +1

        Подскажите пожалуйста, как организовано управление проектом? Если два релиза в день, то как вы их планируете? Как происходит оценка фич/задач? Как вы разбиваете фики на таски так, чтобы каждый день релизиться?


        1. Spunreal
          18.11.2021 14:55

          Как вы разбиваете фики на таски так, чтобы каждый день релизиться?

          Чтобы релизиться каждый день, не обязательно всё делить на маленькие фичи. Вот на скрине иллюстрация того, как каждый день есть что релизить, но что-то делается за 30 минут, а что-то 4 дня.


  1. Ilusha
    16.11.2021 12:57

    Как вы измеряете продуктовые метрики как зависимость от фичей?


    1. petersolopov Автор
      16.11.2021 15:10

      Добрый день!  Есть дашборды с продуктовыми метриками, гипотезы проверяются а/б тестами.


      1. Ilusha
        16.11.2021 18:12

        Вы не поняли вопрос.
        У вас есть фича, вы хотите проверить изменения показателей (например: воронки регистраций и продаж, которые могут состоять из нескольких этапов).
        Для этого вам нужно делать А/Б-тестирование. Причем оно должно идти достаточно долго, чтобы график стабилизировался. Если фича не особо популярна, то ветку должны запускать на большой аудитории (и такой же объем в контрольную группу). Плюс проблема плохих результатов.


        1. sky2high0
          17.11.2021 16:52

          АБТ эксперимент из ветки — это исключение. Обычно новую фичу закрывают флагом и открывают во время АБТ.


          1. Ilusha
            17.11.2021 23:30
            +1

            Суть моего вопроса от этого не меняется. Да и ответ я знаю: там так никто не оценивает влияние фичей на финансовые показатели. Но зато «измеряют» эффективность линейного персонала.


            1. sky2high0
              18.11.2021 00:26

              Финансовые показатели — это супер нечувствительная метрика. Есть, конечно, исключения типа изменений, связанных с рекламой или если сайт занимается продажами. Но в массе свой, например, более удобный способ написания комментов на Хабре скорей всего не прокрасит деньги, но может прокрасить time spent.

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


              1. Ilusha
                18.11.2021 01:01
                +1

                Мой вопрос все же относится к контексту проекта.

                Согласен: чувствительность - показатель производный от времени эксперимента и количества пользователей. Отсюда и другие метрики.

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

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

                Но как разработчик, я против них :) Меня они демотивируют.


  1. chemtech
    16.11.2021 13:00

    Кстати, по Sentry есть русскоязычный чат https://t.me/sentry_ru


  1. Doman
    16.11.2021 16:39
    +2

    А можете, пожалуйста, немного подробнее рассказать про скриншот тестирование? Какой сервис используйте для сравнения скриншотов? Насколько удобно апрувить и коммитить ожидаемые ченжи? Как это встроено в CI? Как это отображается в мерж-реквестах?


    1. skeevy
      17.11.2021 09:34

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

      По поводу удобства не знаю, можете предварительно оценить по этому докладу (где-то в середине будет демонстрация Loki)


    1. petersolopov Автор
      17.11.2021 22:52

      Да, конечно! Большинство скриншот тестов, делаем с помощью playwright и @playwright/test.

      Для скриншот тестов есть билд в CI (teamcity). Билд автоматически запускается на каждой ветке. В нем сначала поднимается витрина компонентов (react-styleguidist), а потом playwright заходит на страницы компонентов и фоткает все примеры. Внутри @playwright/test есть хелпер toMatchSnapshot который сравнивает скриншоты и в случае если они не совпадают создает картинку с дифом. В playwright v1.16.0 завезли html репортер, мы еще не успели его прикрутить и смотрим дифы картинок в артефактах в CI если что-то пошло не так.

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

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

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


  1. marvelsrp
    17.11.2021 11:26

    А как вы решаете такую проблему?:

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


    1. Ilusha
      17.11.2021 23:36

      Я не ТС, но могу ответить как было сделано у меня в разных проектах:

      • принудительная перезагрузка после суток работы (не было времени чинить утечки памяти, каюсь)

      • По хедерам респонсов. Пока юзер не получает реквестов на старую страницу - она остаётся такой, какой есть