Эффективный процесс разработки на основе Feature-Sliced Design: наш опыт

в 20:10, , рубрики: feature slice design, FSD, javascript, React, TypeScript, архитектура, методология, паттерны проектирования, процесс разработки

Всем привет! Меня зовут Егор, я – фронтенд-разработчик в Чиббис, один из трёх разработчиков новой версии важного продукта компании – партнерского личного кабинета.

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

Сегодня я хотел бы поделиться с вами нашим процессом разработки, который сложился у нашей команды при работе на этом проекте. Зачем? Мне кажется, что информация о готовых процессах/воркфлоу/циклах/итерациях разработки на Feature-Sliced Design (далее – FSD) встречается значительно реже, чем какие-то базовые рекомендации, правила и сухая теория этого архитектурного паттерна.

В статье будет представлен готовый шаблонный процесс от А до Я, который позволит вашей команде пройти путь "from zero to hero" (от пустой папки проекта до готового бизнес-продукта с большой пользовательской аудиторией). Вам решать, насколько процесс получился оптимальный. С интересом выслушаю критику сообщества. В спорах рождается истина и возможно, что-то мы сможем взять в работу. Но сначала немного о команде и о самом проекте.

О проекте и мотивация выбора FSD

Чиббис - маркетплейс доставок еды в 190+ городах РФ, крупный игрок на рынке российского фудтеха, с 8000+ ресторанами-партнерами и 3+ млн клиентов. Наша киллер-фича – возможность заказывать бесплатное блюдо за накопленные баллы с каждым заказом (даже с первым).

Эффективный процесс разработки на основе Feature-Sliced Design: наш опыт - 1

Личный кабинет партнера – b2b-приложение для партнеров (ресторанов), позволяющее управлять своими заведениями на нашей платформе, а именно просматривать и работать с отзывами, формировать меню и карточки продуктов, управлять заказами и анализировать их историю и статистику, подключать новые филиалы и управлять временем их работы и так далее. Функционала в приложении чуть ли не больше, чем на основном сайте (https://chibbis.ru/).

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

Итак, FSD. Будем честными, про FSD-архитектуру/методологию/подход сегодня не слышал только ленивый и про него точно нельзя сказать, что он обделен вниманием. Как я уже говорил, в этой статье я бы хотел поговорить не про основы FSD, т.к. материалов по данной теме хватает (вроде бесконечно обсуждаемого разбиения по слоям), а про сам процесс разработки на FSD.

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

Стек технологий

Итак, наш начальный стек получился следующим:

  • TypeScript

  • Tramvai — модульный фреймворк от команды Tinkoff для разработки React-приложений, про него отдельно поговорим в следующий раз

  • Tanstack React Query для работы с апи, идет в Tramvai из коробки

  • Jest как тестовый фреймворк, React Testing Library как основная библиотека для тестирования и Playwright для user-path сценариев

  • И, наконец, самое ключевое для разработки, о чем мы сегодня и будем говорить: Feature Slice Design для архитектуры.

Почему FSD?

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

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

  • Четкая структура проекта. Я - большой фанат легко считываемых, логичных, хорошо прописанных и продуманных подходов. Одним словом, люблю и топлю за единообразие всех структур. Легкость чтения приложения, выстроенного на FSD, позволяет быстрее ориентироваться не только нам, авторам приложения, но и помогает новым разработчикам быстрее влиться в проект

  • Изолированность и модульность. FSD позволяет делить приложение на независимые модули (фичи, сущности и т.д.), что тянет за собой целый ряд плюсов:

  • Легкость разработки: каждый модуль можно разрабатывать и тестировать отдельно.

  • Повышение производительности команды: разные разработчики могут параллельно работать над разными фичами/сущностями и т.д.

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

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

Итерации и цифры

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

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

5 основных разделов + авторизация + еще один раздел для быстрой обработки заказа неавторизованным пользователем, перешедшим по ссылке из бота в мессенджере=7 итераций

5 основных разделов + авторизация + еще один раздел для быстрой обработки заказа неавторизованным пользователем, перешедшим по ссылке из бота в мессенджере = 7 итераций

Не буду расписывать весь тернистый путь из успехов и неудач, приведу основные цифры:

  • Процесс разработки 1.0.0 версии приложения с нуля занял 5.5 месяцев. Речь именно про написание кодовой базы фронта (без учета затрат на бэк, написание и согласование ТЗ и дизайн)

  • Мы прошли 7 итераций

  • В среднем три недели на итерацию

  • 3 разработчика + техлид

  • 256 000 строчек кода

  • Итоговый рабочий флоу у нас вырисовался после 2-ой итерации

Первая итерация

Начнём с того, что рассмотрим с вами вступительную итерацию. Она немного отличается по содержимому от последующих. В рамках нее один разработчик писал процесс аутентификации в приложении, а двое создавали UI-kit проекта с прицелом на переиспользование в других проектах компании.

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

Все UI-kit компоненты хранятся у нас по пути shared/ui и абсолютно все представлены в Storybook, т.е. к каждому написаны не только тесты, но и т.н. «стори» Сторибука. Кто не знаком со Сторибуком - это такой инструмент взаимосвязи разработчиков, дизайнеров и тестировщиков, служащий для разработки и визуального тестирования UI-компонентов в изоляции: можно прямо «наживую» менять пропсы компонента в интерфейсе стори, смотреть, как меняется компонент, взаимодействовать с ним и видеть различные его состояния.

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

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

Плюс в сторибуке удобно смотреть глобальные переменные дизайн-системы, например, цвета:

Эффективный процесс разработки на основе Feature-Sliced Design: наш опыт - 4

Или переменные размера текста:

Эффективный процесс разработки на основе Feature-Sliced Design: наш опыт - 5

Флоу разработки

* Маленький дисклеймер:

Вас может удивить название одного из наших слоев – Routes. Это Pages слой в чистом виде, нейминг обусловлен применением file-system роутинга на проекте.

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

Этап 1: Анализ требований

Работа над новой страницей (разделом) для нашей команды начинается с изучения:

  • ТЗ

  • Дизайн-макета

  • Ручек бэка

Этап 2: Создание FSD-макета

Следующий шаг – создаем выделенную страницу в excalidraw, делимся с командой, устраиваем созвон и начинаем создавать FSD-макет раздела.

Вид свысока на макет раздела «Заказы»

Вид свысока на макет раздела «Заказы»

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

А вот так разбиение выглядит укрупненно. Выделяется визуальная область, и соотносится с ее FSD-составляющей в проекте – это feature или entity, виджет или страница, а может быть вообще UI какого-то компонента

А вот так разбиение выглядит укрупненно. Выделяется визуальная область, и соотносится с ее FSD-составляющей в проекте – это feature или entity, виджет или страница, а может быть вообще UI какого-то компонента
Что я имею в виду под UI в описании к скриншоту выше: обратите внимание на компоненты OrderSummary, OrderList и OrderString: они не переиспользуются на других страницах, либо проектов, поэтому просто хранятся в папке UI страницы OrderHistory, нет никакого смысла выносить их в UI-кит и писать стори

Что я имею в виду под UI в описании к скриншоту выше: обратите внимание на компоненты OrderSummary, OrderList и OrderString: они не переиспользуются на других страницах, либо проектов, поэтому просто хранятся в папке UI страницы OrderHistory, нет никакого смысла выносить их в UI-кит и писать стори
А вот так это выглядит в составе страницы. Наблюдаем знакомые нам по скриншоту выше компоненты OrderSummary и OrdersList, расположенные на странице

А вот так это выглядит в составе страницы. Наблюдаем знакомые нам по скриншоту выше компоненты OrderSummary и OrdersList, расположенные на странице

Еще раз обращаю внимание, что разбиение и выделение FSD-компонентов осуществляется ни в коем случае не по визуальной репрезентации или занимаемому на экране пространству, а по смыслу и цели того или иного компонента. Макет в данном случае нужен только для наглядности. Например, фича DeclineOrder со скриншота выше несет за собой гораздо больше, чем кнопка «Отменить заказ», которая вообще может содержаться не в фиче, а храниться, например в виджете OrderStatusPanel. Клик по кнопке, т.е. запуск фичи, поведет за собой изменения разных компонентов, но для наглядности на конкретно этом скриншоте мы обводим только кнопку, понимая, что за ней стоит.

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

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

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

Вот так выглядит сверху макет более сложного раздела – «Ресторан», где в составе раздела есть 4 страницы. В левой части экрана – различные модальные окна и формы
А так выглядела работа с модальными окнами раздела «Меню», здесь, как вы можете видеть, 99% - это фичи, однако, верстка модального окна может храниться и в сущности или виджете

А так выглядела работа с модальными окнами раздела «Меню», здесь, как вы можете видеть, 99% - это фичи, однако, верстка модального окна может храниться и в сущности или виджете

Этап 3: Схема страницы в документации

После того, как FSD-макет готов, мы создаем схему страницы в Confluence.

Мы храним такую схему страницы в документации разработки, в разделе структуры проекта, рядом с папками, дублирующими FSD-слои приложения. Что она из себя представляет:

  • Ссылки на дизайн, макет, URL, используемые методы API

  • Цель раздела – зачем он вообще нужен пользователю

  • Далее указывается каждая страница (если их несколько), и под ней ее схема такого вида:

Эффективный процесс разработки на основе Feature-Sliced Design: наш опыт - 12
  • Далее по нисходящей указываются виджеты-фичи-сущности-shared, с описанием текстом (где это нужно), что делает, например, фича, какие методы бэка использует, какой UI содержит внутри

Пример

Пример
Как я уже говорил, не все фичи имеют визуальную репрезентацию

Как я уже говорил, не все фичи имеют визуальную репрезентацию

Этап 4: Создание задач и их декомпозиция

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

Список задач, блокирующих разработку страницы «История заказов»

Список задач, блокирующих разработку страницы «История заказов»

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

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

Этап 5: Распределение задач

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

Этап 6: Разработка и тестирование

Далее идет обычный процесс разработки и тестирования.

Этап 7: Ретроспектива

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

Итоги

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

  • Скорость разработки: время разработки каждого следующего раздела сокращалось, несмотря на различную их сложность. Я уже упоминал, что в среднем цикл выходил у нас около трех недель, но первые несколько разделов приближались к четырем неделям, а последние мы делали за 2 недели (не считая тестирования), несмотря на то, что именно они являлись самыми сложными по функционалу в приложении.

  • Скорость тестирования: первые разделы «размазывались» во времени из-за недостаточной декомпозиции задач, неравномерно распределенной во времени нагрузки QA со стороны фронта, срок полного прохождения тестов и исправлений раздела мог достигать 2+ недель. Последние итерации – 1 рабочая неделя.

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

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

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

Итого мы получили готовый, легко масштабируемый на другие проекты (нашей компании и не только) шаблон процесса, который можно брать и использовать «как есть». Абсолютно не претендую на новаторство, т.к. спустя время встречал доклады о похожих процессах у коллег (хороший знак, что мы на верном пути), просто делюсь готовой последовательностью, которая именно в такой конфигурации и порядке хорошо сыграла нам на пользу и может быть полезна вам в разработке ваших FSD-проектов. Напомню, что данный флоу подходит для новых проектов, а в следующий раз я поделюсь опытом перевода готового продукта на FSD.

До скорых встреч! Пишите любые интересующие вас вопросы в комментарии.

Автор: Egor_Pestov

Источник


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js