Эта статья о небольшом инструменте, который позволяет экономить время при разработке и тестировании фронтенд-проектов. А ещё его можно использовать для сложных A/B тестов и (для самых смелых) прятания встроенных админок. Материал может быть полезен фронтенд-программистам, тестировщикам, тимлидам и продактам.

Два варианта разработки большой фичи

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

Поэтому, вариант второй — частые мержи в мастер. Новый код для этого нужно спрятать под так называемым feature toggle, он же фича-флаг. Это обычный if/else, проверяющий значение, полученное откуда-то извне. Если true — выполняем один вариант кода, если false — другой. Значение флага требуется как-то передать на фронт, например получить его запросом или зашить в страницу при рендере. Неактуальные флаги нужно не забывать удалять, чтобы не превратить проект в мусорное ведро. 

Используя такие техники, всегда можно порадовать заказчика демонстрацией наполовину сделанной фичи :)

По запросам feature flags или feature management можно нагуглить несколько готовых решений разной степени серьёзности, открытости и платности. У нас в ATI.SU исторически используется самописное решение с БД, апи и админкой.

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

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

Реализация

Представьте себе вот такой код в вашем приложении:

import Feature from ‘@ati/morpheus’;

...

<Feature expire={20211410} 
				 name=”Experiment alpha” 
				 on={() => <NiceNewComponent />} 
				 off={() => <UglyOldComponent />} />

И аддон в браузере. Если я в аддоне нажму чекбокс — увижу новый компонент. Отожму — старый.

Лучше один раз увидеть.

Вот как выглядит тестовая страница компонента:

Чем пять раз увидеть лучше один раз попробовать. Для этого — поставьте аддон и перейдите на тестовую страницу.

Пару слов про параметр expire. Он необходим исключительно в гигиенических целях. Если текущая дата больше даты указанной в expire — получаем console.warn о том, что в релизной сборке есть фича с истёкшим сроком годности. При использовании инструментов вроде Sentry можем настроить алертинг и узнать о «протухании» забытых веток.

Какие возможности открывает решение

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

Например. А если наш фича-флаг будет показывать скрытые кнопки для тестировщиков, которые позволят получить сериализованный стейт прямо из компонента или быстро приведут приложение в заданное сложное состояние? Получаем инструмент тестировщика, позволяющий обогатить баг-репорты и упростить ручное тестирование. Для такого мы сделали обёртки <Invisible />  без свойства expire и <Tool /> для инструментов, которые ничего не рендерят, но что-то выполняют.

Если всё-таки захотим начальное состояние фича-флагов пробрасывать с сервера? Хоп, и теперь можем выкатывать большие фронтенд-фичи на группы пользователей или делать почти мгновенный роллбэк. Сегодня выкатили редизайн, а завтра: «верни стену», — откатили обратно.

А если управлять начальным состоянием фича-флагов на основании группы пользователя из Google Optimize или любого другого инструмента A/B-тестирования? Получаем возможность проводить тестирования сложных вариаций. Праздник для продакта (если он сможет найти свободного программиста).

Где скачать

Изначально не было планов делать инструмент публичным, поэтому целиком на гитхабе его (пока) нет, код завязан на наши внутренние инструменты.

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

https://gist.github.com/kucheruk/8369ae5009d737f63d46886febf00b28

Необходимые пояснения

В качестве источника данных о фичах на странице используется Map features в contentscript.js (этот скрипт аддон внедряет на каждую страницу).

Данные для contentscript пересылает компонент Feature.

Popup.js, формирующий выпадашку аддона, запрашивает данные из contentscript.js.

Для общения используется стандартный для браузерных расширений механизм.

Надеюсь, кому-то кроме нас это тоже принесёт пользу.

В дополнение рекомендую почитать, какие ещё есть техники для избавления от merge hell и долгоживущих веток.

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


  1. Devroman
    15.10.2021 14:18
    +1

    Я так понял, что расширение само собирает на странице информацию о АБ вариантах, верно? Как предлагается отслеживать такие сплит тесты чтобы они не терялись? Может имеет смысл expire сделать обязательным, чтобы точно как минимум через Sentry собирать подтухающие куски?

    PS: Идея и реализация огонь


    1. kucheruk Автор
      15.10.2021 14:24

      Пока ещё не собирает. Это из заделов на будущее. Но если поставишь задачу - начнёт собирать :)


  1. Ustas4
    16.10.2021 08:20

    Я , наверное, зануда. Вроде в примере кода нет баланса {}. А в самом реакте я ничего не понимаю. Идея интересная.


    1. kucheruk Автор
      16.10.2021 08:20

      Всё так, добавил. Спасибо :)


  1. Catarina
    16.10.2021 15:23
    +1

    Звучит очень круто! И да, для А/В тестов часто проблема - все-таки найти свободного разработчика.


  1. Ustas4
    16.10.2021 19:42

    Допустим у вас backend имеет 100500 версий схем базы данных( изменения касаются именований таблиц/колонок, поведения объектов) такой метод приведет со временем к росту кода, трудностям проверок на backward compatibility. Много if-ов замедлит процесс.

    Или я ошибаюсь?


    1. kucheruk Автор
      16.10.2021 21:21

      С лёгкостью можно с помощью этой техники отстрелить себе ногу.

      Помогут хорошие практики: делить работу на мелкие части, гигиена (удаление неактуального)

      И речь тут про сложность и чистоту кода.

      Скорость проекта работающего с базой будет упираться в походы в базу, не в if.