Мы внедряем Storybook на легаси-проекте и заранее пообещали рассказать, если он не взлетит
Честный рассказ команды Modus , которая только что завела Storybook поверх большой легаси-базы: зачем стартуем, чего ждём и как через полгода проверим, какие получили результаты.
Представьте, что разработчику нужно поправить одну кнопку — отступ или состояние наведения. Работы на минуту, но для этого необходимо поднять всё приложение, залогиниться и прокликать половину интерфейса до нужного экрана. И так — каждый раз. К третьему заходу проще остановиться на «вроде норм», чем повторять этот цикл ради мелочи.
Нам это надоело и мы решили попробовать Storybook. А заодно — поставить над собой небольшой эксперимент.
Интересно, что про Storybook уже есть много статей, но почти все они одинаковы: «внедрили — и все наладилось». Такие кейсы написаны постфактум, поэтому любой старт легко выдать за гениальный план, а сомнительные гипотезы — за «стратегическое видение». Только пользы от этого мало: вывод есть, но он «смазывает» процесс.
Мы в Modus выбрали другой путь — из точки «запустили проект и понятия не имеем, все чем кончится». В статье расскажем почему выбрали Storybook, как начинали, с какими проблемами столкнулись и что будем измерять.
Если вы сейчас решаете, тащить ли Storybook в уже живой проект, и хотите увидеть честный старт, а не сухой результат — этот материал для вас.
Почему мы стартуем не с нуля
Дело в том, что Modus BI — это зрелое веб-приложение: React 16, Webpack 5, MUI, TypeScript, i18next, Redux Toolkit. Под капотом — сотни компонентов, накопленных за годы и написанных разными людьми, с разными привычками и представлениями о том, «как правильно».
И это меняет вообще всё. Одно дело — развернуть красивый инструмент на чистом репозитории, где ты сам решаешь, как всё будет устроено. И совсем другое — втащить его в проект, у которого уже есть своя история, привычки и особенности. Разница огромная: в туториалах показывают идеальную лабораторию, а у нас — «обжитой дом», который нельзя снести и построить заново.
Поэтому и старт у нас сознательно скромный. Цель не «покрыть весь проект историями», а проверить инструмент на ограниченном наборе компонентов и по факту решить, стоит ли двигаться дальше. Мы не нахваливаем Storybook авансом — мы его тестируем.
С чего всё началось
Триггером стало обновление MUI — с древней 0.19 сразу на пятую версию. Так как между этими версиями целая пропасть — другое API, система тем, подход к стилизации — быстро обновиться не получилось.
С простыми компонентами всё прошло безболезненно: мы поправили импорты, подкрутили пропсы — и поехали дальше. Но часть компонентов за годы обросла логикой, стала составной, и переносить их «по месту» было невозможно — приходилось разбирать и переписывать заново.
И здесь мы упёрлись в неудобный вопрос: как переделать один компонент, не поднимая ради этого весь тяжеловесный проект? Схема выглядела так: собрать приложение, дождаться сборки, залогиниться, прокликать до нужного экрана, воспроизвести состояние, в котором эту кнопку видно, — и только после этого посмотреть, что получилось. Затем внести правки и начать заново. Для одной кнопки в паре состояний это терпимо. Для составного компонента, который надо прогнать через десяток вариантов, — уже пытка.
Storybook закрывает эту боль — его можно быстро развернуть и настроить. В нем файл истории лежит рядом с компонентом, тянет за собой нужные зависимости — и компонент открывается изолированно, без запуска всего приложения. А уже где держать эти файлы — прямо у компонента или в отдельной папке — каждая команда решает по-своему, на работу инструмента это никак не влияет.
Мы попробовали и почти сразу почувствовали эффект — необходимость постоянно держать проект запущенным просто отвалилась. Уже одного этого хватило, чтобы отнестись к инструменту всерьёз.
Проблемы, которые мы решаем прямо сейчас
Миграция MUI была поводом, но не корнем. Проблемы, которые мы хотим закрыть шире — среди них:
UI-кит растёт быстрее, чем мы успеваем его осмыслить. Когда компонентов много и они раскиданы по проекту, никто не будет держать их в голове. Поэтому написать новую кнопку проще, чем найти уже существующую. Так в кодовой базе постепенно заводятся дубли — например, два похожих Button, три почти одинаковых Dropdown и четыре реализации одного и того же тултипа.
Ревью интерфейса превращается в квест. Чтобы проверить изменения, ревьюер выкачивает ветку из MR, запускает всё приложение и вручную догоняет его до нужного экрана. А чтобы увидеть компонент в краевом состоянии — пустой список, длинный текст, ошибка, загрузка — приходится ещё и воспроизводить эти условия. Это долго, поэтому часть состояний до ревью банально не доезжает.
Документация в «.md» устаревает быстрее, чем пишется. Классическая история: описали компонент, через два спринта поменяли пропсы — и документация уже врёт. Поддерживать ее в синхроне с кодом вручную не выходит: это отдельная работа, которая всегда проигрывает в приоритете живым задачам.
Нет единой витрины UI-кита. Сильнее всего это бьёт по онбордингу: новичок не может за вечер понять, из каких кирпичиков собран интерфейс. Вместо этого он лезет в код, дёргает коллег, а иногда изобретает то, что давно написано.
Мы постарались расписать эти боли максимально конкретно. Это наша честная точка отсчёта и одновременно каркас будущего среза: через год пройдёмся по каждому пункту и посмотрим, что действительно ушло, а что осталось при нас.
Почему именно Storybook
Полноценного сравнения инструментов мы не проводили. Storybook был на слуху, у него огромное сообщество, куча плагинов, тонны примеров под любые стеки — и, главное, он решает ровно ту боль, с которой мы к нему пришли: изолированная разработка компонента без запуска всего приложения.
Альтернативы — Ladle, Histoire, самописную песочницу — мы отложили без долгих споров. Не потому что они плохи; наоборот, у того же Ladle есть заслуженные фанаты, и на маленьких проектах он выглядит очень бодро. Просто на старте важнее было быстро проверить саму идею, а не выбрать идеальный инструмент. Если Storybook себя не оправдает — это тоже будет ответ, с которым можно предметно выбирать альтернативы.
А вы бы закопались в детальное сравнение — или тоже взяли бы самый очевидный вариант и проверяли его в бою? И если у вас есть опыт с Ladle или Histoire на большом проекте — расскажите в комментариях, стоило ли оно того.
Как мы стартуем
Мы намеренно не стали писать раздутое соглашение на двадцать пунктов, которое всё равно никто не откроет после первого спринта. Взяли минимум договорённостей, которые реально будут соблюдаться:
Единый формат. Истории пишем в CSF, состояния прокидываем через args и controls. Никакого зоопарка стилей — если каждый будет писать по-своему, через полгода в них никто не разберётся.
Что покрываем первым. Начинаем с переиспользуемых общих компонентов, а не со страниц целиком. Кнопки, поля ввода, селекты — то, что встречается по всему проекту и где дублирование бьёт больнее всего.
Базовый набор состояний. Для каждого компонента заводим хотя бы основные варианты — обычное состояние, disabled, состояние с ошибкой, крайние случаи по данным. Именно краевые состояния обычно и выпадают из ревью, так что здесь польза очевиднее всего.
Декораторы под реальный проект. А вот тут начинается специфика легаси. Чтобы старые компоненты вообще завелись в изоляции, в preview пришлось прописать глобальный декоратор с провайдерами темы — наш HsThemeProvider, MUI-шный ThemeProvider и провайдер CSS-переменных темы. Без них компоненты рендерятся голыми, без стилей, и толку от таких историй ноль.
А вот Redux Provider и I18nextProvider мы на старте сознательно не подключали — их пока просто нет в конфиге. Текущему набору компонентов они не нужны, зато это бомба замедленного действия — как только дойдем до историй для компонентов, которые лезут в store или дёргают useTranslation, придётся возвращаться и достраивать окружение. Фиксируем это заранее, чтобы через год было видно, во что именно мы упёрлись — и упёрлись ли вообще.
Про грабли интеграции отдельный разговор. Стек у нас нетривиальный — React 16.14 и Webpack 5 в связке со свежим Storybook 10 (react-webpack5 + SWC) подружились не сразу, поэтому несколько вечеров ушло на подгонку конфигурации сборки, алиасов и обработки SVG. Ничего фатального, но обещанное в туториалах «развернул за пять минут» — это точно не про зрелый проект с историей. Поэтому на интеграцию лучше закладывать время с запасом.
Вот как выглядит одна из наших историй сейчас — компонент HsSelect с описанием, интерактивным превью и таблицей пропсов, где каждое состояние можно переключить прямо в браузере:

Чего мы ждём от Storybook
Мы фиксируем ожидания заранее, пока результат ещё неизвестен, — чтобы потом не подгонять оценку под факт. Каждый пункт ниже — это гипотеза, которую мы проверим:
-
Живая документация вместо «.md», которые устаревают на следующий день. Storybook сам по себе — витрина, которая обновляется вместе с кодом. Если история пишется одновременно с компонентом (а не отдельным подвигом раз в квартал), то и «документация» не отстаёт от реальности. По крайней мере, так это работает в теории — значит проверим на практике.
-
Ревью UI без локального запуска всего приложения. Идея простая: ревьюер открывает историю по ссылке, смотрит нужное состояние, оставляет комментарий — и не тратит двадцать минут на сборку проекта ради одной кнопки. Насколько это приживётся в команде — отдельный вопрос; привычки менять сложнее, чем инструменты.
-
Быстрый онбординг через витрину. Новичок заходит в Storybook и за вечер видит весь UI-кит в одном месте — целиком и с интерактивными примерами.
-
Меньше дублирования. Прежде чем писать новый компонент, разработчик сможет проверить, нет ли уже готового. Витрина работает как каталог.
-
Площадка для краевых состояний. В Storybook можно осознанно прогнать пустоту, ошибку, перегруз данными — не воспроизводя эти условия руками в живом приложении.
Каждый из этих пунктов — просто ожидания, а не готовый результат. Мы вполне допускаем, что часть из них не сбудется — и заранее готовы об этом написать.
Отдельно порадовало, что в изоляции получается прогонять и сложное поведение. Например, ModalProvider со стеком модальных окон — раньше такое можно было пощупать только в живом приложении, а теперь оно открывается одной историей и ведёт себя ровно так, как в проде:

Как мы будем оценивать результат
Чтобы наш эксперимент не превратился из честного среза в обычную «победную» историю, мы зафиксировали несколько метрик, которые будем измерять. Так у нас не останется места для вольных трактовок: либо показатель сдвинулся, либо нет:
-
Покрытие. Сколько общих компонентов имеют истории и как эта цифра растёт со временем. Метрика не идеальная (количеством историй легко манипулировать), но динамика показательна.
-
Использование в ревью. Реально ли ревьюеры смотрят истории вместо запуска всего проекта. Проверим прямым опросом команды и по практике MR: если в комментариях начнут появляться ссылки на конкретные stories — значит, инструмент прижился.
-
Онбординг. Прямо спросим у новичков, помогла ли витрина въехать в проект быстрее. Это субъективная метрика, но живой отклик новичка не хуже любой цифры.
-
Дублирование. Стало ли меньше «изобретённых заново» компонентов. Тут придётся немного подкопать руками — автоматически такое не считается — но пройтись по PR-ам за период и посчитать «новые компоненты, которые можно было переиспользовать» вполне реально.
-
Здоровье историй. Сколько накопилось «мёртвы»х и «протухших» stories — тех, что перестали соответствовать реальному коду или сломались после рефакторинга. Это цена поддержки, о которой в подобных статьях обычно умалчивают. Если их окажется треть от всего каталога — это не успех, а новая головная боль.
Также зафиксируем стартовые значения на момент публикации, чтобы через полгода было с чем сравнивать. Наша точка отсчёта:
-
Версия: Storybook 10.2.14, сборка react-webpack5 + SWC.
-
Истории: 14 файлов, 94 stories внутри них. Разброс говорящий — от одной истории на компонент до 24 у HsSelect.
-
Покрытие UI-кита: в src/widgets/ живут 78 компонентов, истории есть у 12 — это ≈15%. То есть на старте покрыт примерно каждый седьмой.
-
Организация: истории лежат рядом с компонентами, в тех же папках.
-
Окружение: провайдеры темы настроены, Redux и i18next в preview — ещё нет.
Через полгода мы возьмём ровно эти же метрики и покажем динамику: выросло ли покрытие, ушло ли дублирование, обросли ли мы кладбищем мёртвых историй.
Вместо вывода — приглашение вернуться
Обычно статьи про внедрение заканчиваются выводами. У нас выводов пока нет — Storybook в Modus BI мы завели только что, а честный вывод рождается из практики, а не из первых впечатлений. Что у нас есть на руках — так это точка отсчёта. Мы зафиксировали свои боли, расписали ожидания и оценили риски, и всё это — до того, как узнали, чем всё закончится.
Через полгода мы вернёмся с промежуточными итогами и пройдёмся по каждому пункту — что сбылось, что провалилось, что еще в процессе работы, а где мы уже промахнулись в прогнозах. И даже если итог окажется «неудобным» — например, Storybook не оправдает ожиданий и превратится в обузу — мы напишем и об этом, так же честно, как пишем сейчас.
Если у вас похожий зрелый проект и вы тоже думаете про Storybook — подпишитесь и дождитесь среза. А ещё лучше — расскажите в комментариях о своём старте: с какими граблями столкнулись и оправдал ли инструмент ожидания. Из таких честных историй пользы куда больше, чем из очередного отчёта победителя.
P.S. Присоединяйтесь к нашему BI-сообществу в Telegram и будьте в курсе последних новостей Modus!
Автор: valeryan15

