
О чём эта статья
Как справляться с огромной сложностью программного обеспечения в условиях крупной компании, где множество отделов и департаментов, множество проектов? Корпоративный фреймворк — один из ответов.
Эта статья не совсем обычная. Мы будем смотреть на тему создания платформенных решений с разных ракурсов: управление, дизайн, проектирование, разработка и так далее. Чтобы передать вам как можно больше мыслей из первых рук, мой рассказ будут разрезать небольшие вставки-интервью от реальных участников нашей команды.
Меня зовут Сергей Шахов, я долгое время занимался развитием корпоративного фреймворка в нашей компании. Наш фреймворк помогает создавать командам пользовательские интерфейсы, иными словами, это фронтенд-фреймворк.
Статья адресована особенно тем, кто неравнодушен к разработке платформенных решений, а также может быть интересна всем, кто имеет отношение к фронтенд-разработке, проектированию интерфейсов, созданию дизайн-систем или комплексных модульных веб-приложений.
Вы узнаете, стоит ли затевать разработку корпоративного фреймворка, а также о том, как неопределённое может быть управляемым, а скучное — прекрасным. В качестве бонуса дадим вам запретный рецепт, как спасти демонстрацию вашим заказчикам за десять минут до её начала, которому ни в коем случае нельзя следовать. Поехали!
Оглавление
Текст получился довольно большим, более 35 тысяч знаков. Я буду в восторге от вашей решимости прочитать статью от начала до конца, но если вас интересуют только выборочные аспекты командного процесса, я подготовил вот такую быструю тематическую навигацию:
Интервью с Денисом: «Если твой потребитель не представляет, что он хочет — это нормально» (#аналитика, #архитектура)
Код-хамелеон: переобуваемся на лету (#разработка, #архитектура)
Многоэтажный API: сегментируем потребителей (#разработка, #архитектура)
Апартаменты для приложений: любой каприз за наши деньги (#разработка, #архитектура)
Интервью с Антоном: «Микрофронтенды — не для простых приложений» (#разработка)
Разворачивайтесь в марше! (#devops)
Интервью с Романом: «В любом проекте стремлюсь всё автоматизировать» (#devops)
Дизайн-система против изоляции (#дизайн)
Интервью с Константином: «У меня всё очень скучно, но классно!» (#дизайн)
Унифицировать нельзя фрагментировать (#дизайн)
Как всем этим управлять (#управление)
Интервью с другим Денисом: «Нужен вид с вертолёта» (#управление, #архитектура)
Позвони мне, позвони: обратная связь и проверка гипотез (#devrel, #аналитика)
Интервью с Мариной: «Это как марафон на выносливость, а не стометровка на скорость» (#devrel)
Как всё началось
— С какой стати React? Вот Angular — как швейцарский нож, там система «всё включено». И, кстати, его Василий уже использовал!
— Зато React гибче и встраивается в любую архитектуру. И самый популярный!
— Эй, про Vue не забывайте!
Примерно так мы начинали. Фреймворка тогда ещё не было. И дизайн-системы не было. Мы положили себе в корзину React, TypeScript и ещё нескольких смежных технологий.
— Нам надо быть лучше всех! Наш интерфейс должен отличаться. Заказчик просит! — такое тоже можно было иногда слышать в командах. Команд было много, десятки. Продуктов с пользовательскими интерфейсами — больше ста. Некоторые интерфейсы были на веб-технологиях, некоторые — desktop. Разные цвета, шрифты, различались базовые технологии, подходы к архитектуре.
— Как же так, ребята? У вас тоже своя реализация колокольчика со списком уведомлений? Это уже шестая реинкарнация колокольчика в компании! А-а-а-а-а!
И действительно: общих компонентов не было. Каждая команда делала необходимые компоненты для себя сама. Дизайн-системы тоже не было.
Предстояло решить несколько задач:
Унифицировать внешний вид веб-приложений
Создать единую архитектуру для веб-приложений
Минимизировать работу команд по вёрстке
Обеспечить такой эффект, чтобы создаваемые разными командами интерфейсы были соединены для пользователя в единую среду — так, чтобы он и не задумывался, что имеет дело с разными приложениями.
Само собой, в цене были и безопасность, кроссбраузерность, наилучшая эргономичность и другие привычные критерии. Но не в масштабе одной команды, а системно, для многих команд сразу!
Создать фреймворк — одна часть решения этого комплекса задач.
Что у нас теперь есть
Теперь давайте быстро пролетим над многочисленными ухабами и перекрёстками, которые мы преодолевали, и перенесёмся в день сегодняшний.
Я проведу вам небольшую экскурсию по нашему лабиринту Минотавра — устройству фреймворка. Нить Ариадны не обещаю, поэтому слишком глубоко не пойдём: останемся в рамках обзорного формата.
Итак, вот ключевые компоненты фреймворка с высоты птичьего полёта:

Давайте разбираться, что есть что на этой картинке.
UI Kit. Это наша базовая библиотека компонентов. В ней можно найти кнопки, флажки, поля ввода и множество других компонентов, которые мы ожидали бы увидеть в хорошем UI Kit. Мы с самого начала поняли, что при создании дизайна придётся учесть массу специфических нюансов, поэтому вёрстку нужно держать под нашим полным контролем — и поэтому вариант кастомизации готовой библиотеки с открытым исходным кодом отпал очень быстро. Было решено делать свою библиотеку!
UI Blocks. UI-блоки — это компоненты более высокого порядка. Например, «карточка сотрудника», «форма ввода адреса», «навигационная панель» и так далее. Каждый UI-блок состоит из компонентов UI Kit. UI Blocks, как и UI Kit, является библиотекой React-компонентов. Мы время от времени публикуем новые релизы библиотек в нашем корпоративном NPM Registry.
Гайды — это наша «Книга о вкусных и здоровых интерфейсах», написанная в формате Markdown. В книге сказано, когда каких UX-паттернов стоит придерживаться, какие формулировки в текстах мы считаем корректными, а какие — неуместными. Гайды также включают в себя полный справочник всех компонентов из UI Kit и UI Blocks с примерами-песочницами, где можно пробовать менять любые свойства прямо в небольших фрагментах кода. Начинали мы когда-то давно с React Styleguidist, потом однажды один наш невероятно быстрый и способный разработчик предложил: «Давайте я сделаю нам собственный styleguide-движок!» Сказано — сделано. Так мы с этим движком с тех пор и живём. Он объединяет в себе лучшие возможности из React Styleguidist, Storybook, плюс пару наших изюминок: например, можно одним нажатием скопировать чистую HTML-разметку, соответствующую каждому компоненту — такая функция была нужна нашему заказчику.
Pixso — это инструмент для дизайнера, где можно с нуля создать макеты и библиотеки компонентов. Проще говоря, замена Figma. Импортозамещение с китайским диалектом! Pixso позволяет нам хранить все наши макеты на собственных серверах. Именно в Pixso живут все компоненты дизайн-системы, отправляющиеся затем на вёрстку.
Конструктор — это конструктор интерфейсов, наш инструмент с веб-интерфейсом, где можно составлять из UI-блоков целые экраны и страницы, перетаскивая блоки мышкой и настраивая их свойства. Конструктор нужен, во-первых, для быстрого прототипирования интерфейсов, чтобы участвовать в оном могли бы не только профессиональные дизайнеры, но и аналитики, и представители заказчика. Во-вторых, он нужен для централизованного проведения дизайн-ревью. В-третьих, для некоторой экономии усилий: из конструктора выгружается не макет или картинка, а сразу готовый React-код, который остаётся привязать к данным.
Крепитесь, половину схемы мы уже прошли! Спускаемся ниже.
Шаблон — стартовый шаблон React-приложений на базе vite. Точнее говоря, это целый набор шаблонов для разных случаев, разных видов приложений.
Генератор кода генерирует заглушки для React-компонентов или других сущностей. С нужным наполнением, в соответствии с нашим представлением о прекрасном, которое непостоянно и коварно, как любовь Дона Гуана из «Каменного гостя». Это CLI-инструмент.
Правила линтера — наши конфигурации для ESLint.
CI-менеджер — это небольшой вспомогательный инструмент для более удобного выполнения отдельных шагов CI/CD. Например, знает, на какие стенды какое ПО выкладывать, как при этом настраивать base URL для маршрутизации и так далее.
Приложение-контейнер… Ему пока позволим затаиться за кулисами. О нём речь пойдёт чуть ниже!
А теперь представьте, что друг напротив друга, в пушистых креслах, расслабленно сидят двое: я и очень важный человек в нашей команде — Денис. Сейчас будет интервью! И мы постараемся что-нибудь рассказать про архитектуру и анализ.

— Денис, какую ты видишь особенность работы с платформенными решениями, фреймворками?
— Наверное, высокая неопределённость. Переход на новую платформу обычно не бывает одномоментным,
и вот в этот переходный период могут возникать абсолютно непредвиденные сложности.
— Не могу не согласиться! А какие методы борьбы с неопределённостью тебе кажутся наилучшими?
— В идеале это заранее установленные и чётко формализованные взаимные требования, то есть как требования потребителей к фреймворку, так и требования фреймворка к будущему потребителю. Но на практике так не всегда получается. Неплохо срабатывает такой компромисс: когда появляется новая команда-потребитель, мы сразу же открываем друг другу пространства в Confluence: мы им, а они — нам. Там можно сразу же найти ответы на многие вопросы, понять особенности стека этой команды, предусмотреть требования, которые могут возникнуть от них в дальнейшем.
— Давай попробуем поделиться с читателями, как мы выполняем проектирование архитектуры, какие артефакты при этом мы готовим.
— Если вкратце, то три важнейших представления для нас — это функциональная архитектура (какие функции предоставляются каким блоком), системная архитектура (из каких логических блоков система состоит и как они взаимосвязаны), архитектура развёртывания (на каких серверах работает система, как сервера соединяются по сети).
— А про используемые нотации что бы ты сказал?
— Очень практичной оказалась UML Sequence Diagram. Применяли и C4, и ArchiMate. Но вообще, в итоге пришли даже к разработке своей собственной нотации — простой, объединяющей элементы из нескольких общеизвестных нотаций проектирования. Для нас более важным на практике оказалось не чёткое следование какому-то индустриальному стандарту, а возможность иметь единую для всех нотацию, которая сразу понятна всем — и платформенным командам, и прикладным.
— Как думаешь, что лично тебе дал опыт работы над фреймворком, чему ты научился?
— Наверное, вот этому и научился: лучше работать в условиях неопределённости. На практике понял: если твой потребитель сам до конца не представляет, что он хочет — это абсолютно нормально, и в этом случае было бы хорошо сократить time-to-market, поскорее выкатить прототип, MVP. Можно сначала реализовать нечто вообще без попытки выделить библиотеки или единые подходы — просто реализовать как обычно, исходя из бизнес-требований, в каждом отдельно взятом приложении. А дальше уже выделить то общее, что можно было бы опубликовать для всех как элемент фреймворка.
Код-хамелеон: переобуваемся на лету
Какие ещё есть способы борьбы с неопределённостью, не связанные со сбором требований и организацией коммуникаций?
Простой и практичный способ — паттерн проектирования Strategy.
В этой статье я не буду приводить длинных примеров кода, чтобы не разрушать жанр обзора. Ограничусь нарочито сокращёнными фрагментами, чтобы передать саму идею.
Например, введя отдельную стратегию, мы можем заранее обеспечить гибкость и вариативность в вопросе, каким образом осуществляется аутентификация:
const authManager = new AuthManager(
new OAuth2Strategy({
authenticateUri: 'http://backend.ru/auth',
refreshUri: 'http://backend.ru/refresh',
})
);
Паттерн Strategy позволяет изменить решение о каком-то аспекте поведения системы, не затрагивая всё остальное: например, для MVP-версии реализовать простую PinCodeStrategy
, дальше добавить более насущную OAuth2Strategy
, после этого пофантазировать на тему BiometricStrategy
и так далее. Передумали — поменяли стратегию.
Многоэтажный API: сегментируем потребителей
Есть и ещё один полезный подход, который не только вносит вклад в нашу неравную борьбу с неопределённостью, но и в целом повышает гибкость, интероперабельность решения.
Представим, что вам нужно реализовать интерактивную редактируемую таблицу с подключением к заданному серверному источнику данных.
Наши действия? Конечно, собираем и раскладываем по полочкам все требования к этой таблице и проектируем свойства React-компонента, исходя из этих требований — и тут же приступаем к реализации. Но можно сделать ещё лучше: сразу предусмотреть несколько уровней абстракции и несколько уровней дальнейшего переиспользования кода. Например:

Каждый уровень в данном случае — это React-компонент. В нашем фреймворке есть все три компонента (здесь изменены и русифицированы их названия для простоты восприятия, чтобы не погружать вас в детали нашей внутренней терминологии).
Таблица. Это React-компонент, содержащий в себе вёрстку таблицы (
table
,thead
,tbody
и так далее), её стили.Грид. Многофункциональный табличный компонент, имеющий функции сортировки, фильтрации, постраничной разбивки (pagination), группировки данных и так далее. Внутри использует компонент
<Таблица/>
;УмныйГрид. Максимально верхнеуровневый и функциональный компонент, умеющий загружать данные с серверного источника. С ним в паре поставляется готовый backend-сервис. Компонент
<УмныйГрид/>
внутри использует компонент<Грид/>
.
Так как высокоуровневые компоненты опираются на низкоуровневые, картинку ещё можно представить вот так:

Теперь пользователи нашей библиотеки, которым нужно «всё и сразу из коробки», могут пользоваться компонентом «УмныйГрид». Если, напротив, нужен режим «сделай сам» — можно взять «Таблицу» и сделать самостоятельно всю необходимую обвязку вокруг неё.
Построение многоуровневой пирамиды абстракций — исключительно распространённый приём в проектировании, его можно встретить повсюду, например:
В модуле
fs
платформы Node.js высокоуровневые функцииreadFile
,writeFile
построены на основе низкоуровневыхopen
,read
,write
,close
Деление Git-операций на высокоуровневые (
commit
,add
,pull
) и низкоуровневые (cat-file
,commit-tree
,read-tree
).
Этот принцип абсолютно универсальный, с его помощью можно не только делить API-функции на слои, но и заранее планировать способы доступа к конкретной функциональности. Например, видим, что в системе нужна функция создания нового пользователя. Понимаем при этом, что конечный её пользователь — системный инженер и что ему будет удобно вызывать её через CLI. Сразу выстраиваем полную цепочку, включающую несколько уровней доступа: CLI вызывает REST API, REST API внутри серверного процесса вызывает TypeScript API (если исходим из того, что наш серверный код написан на Node.js). Итого функцию мы реализовали только один раз, но у нас готовы три способа доступа к ней: CLI, сетевой доступ, внутрипроцессный доступ.
Что нам это даёт:
Расширяется спектр возможных сценариев, где может использоваться реализованная функциональность.
Как следствие, расширяется и круг людей, которые смогут её использовать.
Возможности переиспользования кода возрастают.
А теперь давайте посмотрим, как мы решали такую задачу: с одной стороны, нам нужно было обеспечить технологическую и организационную декомпозицию (то есть разные команды работают над своими интерфейсами в разных репозиториях), а с другой стороны, для конечного пользователя продукт должен выглядеть как единый и слитный.
Апартаменты для приложений: любой каприз за наши деньги
Когда создаётся большая информационная система с массой функций, первым делом встаёт вопрос о декомпозиции — возникают «подсистемы». Когда речь идёт о пользовательских интерфейсах, мы можем говорить о множестве небольших мини-приложений, каждое из которых создаётся своей обособленной командой. В нашем случае нужно было все эти приложения соединить вместе — так, чтобы для пользователя это представлялось единой средой. Конечно, одна часть рецепта — это создать дизайн-систему с последовательным набором UX-паттернов на разные случаи жизни, о ней мы непременно поговорим в следующих разделах.
А вторая часть рецепта — подходящий архитектурный паттерн, который обеспечил бы удобную сборку, развёртывание, эффективные переходы между мини-приложениями, когда пользователь нажимает на навигационные ссылки.
Какую же технологическую основу взять для такого соединения? Вариантов решения этой задачи несколько, но после череды экспериментов мы пошли в сторону микрофронтендов.
Единое приложение-контейнер (или приложение-хост) управляет жизненным циклом мини-приложений, выделяет им «жилплощадь» на экране, при этом также предоставляет ему в готовом виде типовые сервисы — аутентификацию, логирование, страницу профиля пользователя, редактирование настроек, справку, единую навигацию и так далее. «Заселяйся и живи», всё как в хорошем ЖК — таков замысел.
Схематично можно представить это так:

Надо отметить, что технология микрофронтендов даёт даже больше возможностей, чем пока востребовано в нашем кейсе. Например, в экстазе могут слиться React-, Vue- и Angular-приложения, а у нас же всё на React.
Давайте теперь поговорим с автором этого приложения, Антоном!

— Каким командам ты бы советовал использовать микрофронты, а каким, наоборот, лучше за них не браться?
— Микрофронтенды идеально подходят крупным проектам с распределенными командами. Например, если у вас интернет-магазин с отдельными командами для каталога, корзины и личного кабинета — здесь микрофронты помогут изолировать зоны ответственности и ускорить разработку. Также они актуальны для порталов, CMS или SaaS-решений, где нужна гибкость в переиспользовании функциональных блоков. А вот для небольших приложений с одной командой микрофронтенды могут добавить лишней сложности.
— Давай в двух словах расскажем нашим читателям, как устроены наши микрофронтенды?
— Если в двух словах, мы используем vite вместе с Module Federation. Это позволяет каждому микрофронту быть независимым приложением с собственным сборщиком, но при этом динамически подгружаться в хост-приложение.
— А можешь ли вспомнить про какие-то подводные камни и как ты их обходил?
— Самый частый нюанс — шаринг зависимостей. Например, если хост и микрофронты используют разные версии React, это может привести к ошибкам вроде «Invalid hook call». Мы решили это через shareStrategy: установили loaded-first
, которая гарантирует, что все модули используют единую версию библиотеки из хоста.
Еще одна проблема — роутинг. Когда несколько микрофронтов меняют URL, важно синхронизировать состояние между ними. Мы внедрили централизованный менеджер роутинга на основе React Router, чтобы избежать конфликтов.
И не стоит забывать о производительности: слишком много микрофронтов могут замедлить загрузку. Мы используем lazy loading и предзагрузку критических модулей.
— На твой взгляд, в чём преимущества Module Federation перед более традиционными подходами?
— Module Federation сочетает гибкость и простоту интеграции. В отличие от iframe
, микрофронты работают в одном контексте — это позволяет делиться состоянием (например, данными пользователя) и избежать проблем с CSS-изоляцией.
Если сравнивать с Web Components, Module Federation даёт более тесную интеграцию с экосистемой React/Vue: не нужно писать кастомные элементы, а горячая перезагрузка работает «из коробки».
Кроме того, динамическая загрузка модулей снижает нагрузку на клиента: мы подгружаем только то, что нужно пользователю здесь и сейчас.
— А какие ты видишь плюсы у Module Federation по сравнению с другими подходами реализации микрофронтендов?
— Простота! И гибкость: можно по-разному строить архитектуру с ним.
Разворачивайтесь в марше!
— А скиньте ссылку, где можно что-то понажимать! — такую просьбу вы будете слышать наиболее часто, если вы делаете переиспользуемые визуальные решения и показываете их прикладным командам.
Для этого демостенды всегда должны быть наготове — свежие и благоухающие. Поговорим же о развёртывании и DevOps.
Сборка, автотестирование и публикация стендов у нас проводятся автоматически. Сейчас мы используем для этого Drone, в дальнейшем подход может и поменяться.
Как сделать навигацию по демостендам простой и интуитивной? Мы часто использовали такой подход: адрес демостенда «вычисляется» из адреса соответствующего Git-репозитория. Например, представим, что исходный код нашего UI Kit расположен по адресу (адрес сервера вымышленный):
https://git.example.com/framework/ui-kit
где framework — это имя владельца (repo owner), а ui-kit — имя репозитория (repo name).
Тогда последнюю стабильную версию UI Kit, лежащую в ветке master, всегда можно найти по адресу (адрес сервера также условный):
https://demos.example.com/framework/ui-kit/master/
Последняя development-версия, согласно Gitflow, лежит у нас в ветке develop, поэтому найти живой стенд для неё всегда можно тут:
https://demos.example.com/framework/ui-kit/develop/
Для некоторых репозиториев мы включаем публикацию даже для feature branches. Это полезно, если нужно много обсуждать сырую, промежуточную реализацию с командой или заказчиком. Тогда из ветки feature/big-red-button может рождаться стенд:
https://demos.example.com/framework/ui-kit/feature/big-red-button/
Для каждого репозитория мы задаёмся вопросом: как показать его функции наиболее понятным образом? Если речь идёт про запускаемое веб-приложение, ответ очевиден: на стенде расположится само приложение. Если речь про библиотеку компонентов, то на стенд выкладывается стайлгайд — интерактивная витрина. Как я упоминал выше, Storybook мы не используем, так как стайлгайд-движок мы разработали для себя сами.
Не можем не обсудить и нелёгкие будни с нашим коллегой Романом, который, между прочим, умеет ещё и исполнять оперные арии. Почему именно с ним? А потому что он не только драматический тенор, но и наш DevOps!

— В чём, по твоему мнению, особенность проекта-фреймворка по сравнению с проектом-приложением?
— Я больших особенностей не вижу. Как DevOps, я в любом проекте стремлюсь всё автоматизировать — правда, благодаря этому помимо привычных пайплайнов могут родиться и более специфические рецепты.
— Например?
— Сразу вспомнился такой пример. Как ты знаешь, по некоторым причинам нам вместе с каждой версией дистрибутива нужно передавать пачку Word-документов. Редактировать эти доки вручную — бесит. Поскольку Word-документ — это обычная ZIP’ка, в которой лежат XML’ки, я написал Bash-скрипт, который XML’ки за меня sed-ом редактирует, зацепляясь за определённые ключи.
— А какой был самый экзотический случай, если уж про это заговорили?
— Была у нас однажды демонстрация новой версии заказчику… В самый последний момент мы заметили, что на странице неправильный заголовок! Пересобирать версию — уже не вариант, поздно. Зато пришло на помощь классное расширение — Tampermonkey. С помощью него мы перезаписали строку прямо в браузере и — вуаля! Жёсткий хак, но зато демонстрация прошла отлично.
Я это к тому говорю, что хотя проект фреймворка и подразумевает системность во всём, но без импровизаций всё равно никак. Это жизнь!
— Посоветуй читателям твои любимые инструменты!
— Инструменты? Слишком много, сложно выбрать! Ну или вот: в последнее время очень люблю jq и yq. Незаменимые вещи! Без scp, конечно, никуда.
Дизайн-система против изоляции
Перейдём к дизайн-системе — важнейшей составляющей нашего фреймворка.
Первая аксиома, которую сразу мы приняли для себя — делать компоненты, тут же проверяя их в деле, в реальных приложениях. Иначе вместо работающего инструмента может выйти «музейный экспонат»: красиво, впечатляюще, но бесполезно.
Как из небольших компонентов собирать сразу целые страницы, интерактивные сценарии? Какие подводные камни будут обнаруживаться при этом?
Из этого сразу последовало важное практическое решение: в команде не один дизайнер, который на full-time занимается дизайн-системой, а несколько дизайнеров, и каждый из них работает ещё над какими-либо макетами, прикладными проектами. Эти проекты — полигоны для компонентов дизайн-системы.
Об этом и поговорим с нашим дизайн-лидом!

— Костя, что самое сложное в создании большой дизайн-системы, по твоему мнению?
— Организовать работу, всё продумать! Я не претендую на какую-то истину тут, но для меня это не творческая работа. В последнюю очередь творческая, а больше — аналитическая. Про систематизацию, математику и вот это вот всё.
— Давай попробуем предложить читателям наш вариант чек-листа, пусть даже неполного, чтобы организовать такую работу правильно?
— Попробуем. Во-первых, начинается всё с идеи. То есть нужно понять, для чего дизайн-система делается, где будет использоваться — и, следовательно, какой ей быть. Во-вторых, важно выбрать несколько подходящих под задачу референсов. Можно и без них, но это будет сложнее. Просто всё равно, так или иначе, придётся лезть в интернет, уточнять, сравнивать и так далее. В-третьих, нужно определиться с математикой сетки. Например, мы используем 8px
в качестве базового расстояния, это распространённый подход. Далее, конечно, нужно предварительно определиться с составом компонентов, их размерным рядом (large, middle и так далее). Дальше нужно запланировать прототипирование…
— Компонентов?
— Не только. Прототипирование готовых экранов тоже. Чтобы сразу посмотреть, как компоненты будут сочетаться. Тут тоже важен системный подход. Я вот такой лайфхак применяю: смотреть первым делом не на то, как это выглядит, а на то, чтобы по математике всё сложилось. Чтобы чёткие отступы были, чтобы всё было ровно, чтобы один параметр сочетался с другим. Ещё очень цепляюсь к тому, чтобы названия везде совпадали и не было каких-то пустых слоёв или, например, untitled, undefined и так далее. Унылый подход, но тот, который мне очень нравится! У меня всё очень скучно, но классно!
— Кстати, если всё так скучно, в чём радость от такой работы, как бы ты сформулировал?
— Отвечу вопросом на вопрос: ты давно покупал конструктор наподобие LEGO Technic?
— Очень давно, если честно…
— А я в прошлом году осуществил, так сказать, «мечту идиота»: купил и собрал себе машину огромную на радиоуправлении целиком из Lego. И тут вот такая аналогия: когда ты видишь множество разрозненных деталек, потом собираешь из них что-то, всё друг с другом сочетается, попадает в размеры, а потом ещё и работает — вот он, невероятный кайф!
Унифицировать нельзя фрагментировать
Другая тревожная лампочка, которая всё время загорается при работе над дизайн-системой в крупной компании — риск фрагментации дизайн-системы. В идеальном мире хотелось бы щёлкнуть пальцами — и тут же все интерфейсы стали единообразными во всех продуктах всех команд. Мгновенно! Но, увы, так не бывает. А в действительности же часто бывают две истории. История первая: «Сергей, наша подсистема — совершенно особенный проект. Пойми, это другое! Мы же не как все! Наш заказчик хочет, чтобы и шрифт был большим, и чтобы (здесь можно подставить любые гипотетические пожелания)». История вторая: «Сергей, наш интерфейс делался уже давно! Сейчас там даже страшно что-то обновлять — всё сломается, и мы сорвём наши дедлайны!»
В итоге вам придётся смириться с неизбежным фактом, что переход множественных старых и новых подсистем на новый дизайн — это сложный путь, состоящий из большого числа промежуточных шагов. В середине пути появится множество компромиссных решений, «временных состояний», у вашей дизайн-системы могут неожиданно возникать внебрачные дети и троюродные племянники. Которые потом, если всё пойдёт хорошо, однажды снова вольются в корень «родословной».
Кстати, чтобы сделать разнообразие дизайнов более управляемым, мы сразу включили в наш UI Kit гибкую поддержку темизации. Применяя пакет CSS-переменных, можно не только менять светлую тему на тёмную, но и менять произвольные параметры внешнего вида.
Чтобы от этой тревожной мысли нам не стало совсем грустно, давайте поговорим со светлым и жизнерадостным человеком, дизайнером Дианой.

— Диана, что для тебя было самым сложным в создании дизайн-системы?
— Взаимосвязи всего со всем. Поменялось что-то в одном компоненте — должно сразу что-то поменяться и в другом. Это сложно удержать в голове!
— Как же удалось с этим справиться?
— Мы поняли, что важно все тонкие моменты фиксировать, и мы с командой дизайнеров пошарили единый онлайн-документ, где мы всё записываем: вопросы, принятые решения, принципы нейминга и так далее.
— Мы используем Pixso вместо Figma. К чему готовиться тем нашим читателям, которые задумают рассмотреть такую же замену?
— Интерфейс Pixso очень похож на Figma. Но если проект большой, могут быть проблемы с производительностью. Мы даже решили использовать такой трюк: разбили дизайн-систему на несколько файлов, каждый файл содержит группу схожих компонентов: например, TextField
, TextAreaField
, NumberField
и так далее. Ещё отмечу, что не хватает плагинов: нет привычного для меня Delete Hidden Layers и других. Также пока нет токенов, хотя скоро их должны добавить.
— Что можно было бы пожелать командам, которые только начинают работу над дизайн-системой?
— Главное — не усложнять. Добавлять сложность постепенно. Потому что добавить что-то всегда можно, а убрать — уже нельзя, если это кто-то использует. Вот мы как-то раз столкнулись с тем, что не можем удалить компоненты, которые мы поспешили с самого начала добавить: оказалось, их уже использует другая команда.
— Это как обратная совместимость у разработчиков?
— Да, но мы, дизайнеры, это так не называем. Просто живём с этим!
Как всем этим управлять
У нас классические двухнедельные Scrum-спринты. Планирование, daily-стендапы, ретроспективы — всё как обычно.
Но есть и некоторые особенности управления.
Во-первых, нам важно поглядывать в бэклоги соседних продуктов. Для команды фреймворка хорошо налаженные коммуникации с прикладными командами — существенное условие успеха. Нужно знать, кто когда готов переходить на фреймворк, какие у каждой команды могут всплыть необычные потребности. Большую пользу в понимании этих взаимосвязей приносят ежеквартальные планирования — большие встречи, которые проводятся в духе SAFe: собирается множество команд, все работают с единой большой доской.
Во-вторых, измерять прогресс не так-то просто. Ведь, с одной стороны, можно метрикой успеха посчитать число внедрений фреймворка в прикладные команды. Но сразу выясняется, что внедрение внедрению рознь: одно дело, если команда нашла возможным применить наш UI Kit и тем самым обновить внешний вид интерфейса, а совсем другое дело — использование всех наших наработок в комплексе. Это как арбузы с виноградинками складывать. Много и других нюансов относительно измерений.
В-третьих, в платформенных продуктах существует священное словосочетание — «обратная совместимость». Так как нами поставляются не только пользовательские интерфейсы, но и всевозможные API, то нельзя просто взять и включить в релиз любые изменения, какие нам бы хотелось. Мы всё время оглядываемся на прикладные команды. Что у них может поломаться из-за наших изменений? Потребуется ли им что-то изменить у себя, чтобы начать работать с новой версией нашего фреймворка? Это может оказывать сильнейшее влияние на планирование релизов.
Теперь давайте поговорим с нашим техлидом Денисом (да-да, у нас в команде два Дениса), которому тоже не чужда тема управления.

— Денис, можешь сформулировать какой-то ключевой совет, который можно было бы дать команде, которая только приступает к работе над своим фреймворком?
— Не усложнять. Перед тем как всё разнести по разным библиотекам, можно сначала проверить какой-то код в монорепозитории или вообще в едином приложении. Как только всё обкатаем — тогда можно выделить уже отдельный микрофронт, или микросервис, или библиотеку. Была «просто папка» — стала библиотека. Ну и ещё, конечно, не пренебрегать фундаментальными вещами на старте: выбрать стандарты, линтеры и прочие базовые инструменты, определиться с принципами именования. В любом проекте это важно, а для фреймворка это вообще критично, основа основ.
— Какие у тебя лайфхаки устоялись, которые помогают тебе работать?
— Я люблю «технику помидора»: 25 минут работаю, 5 минут отдыхаю. TODO-списки использую. И ещё секретное оружие: контрастный душ в течение дня! А если из более технического — я постоянно убеждаюсь, что нужен хороший helicopter view, как мы это называем. То есть когда как бы смотришь на всё с вертолёта, широким планом.
Позвони мне, позвони: обратная связь и проверка гипотез
Плох тот фреймворк, о котором молчат.
Мы сразу решили для себя, что рассказывать нужно и отдельным командам, и широковещательно — и здесь мы заходим на территорию епархии DevRel, поэтому совсем скоро мы поговорим с нашим DevRel-специалистом.
От себя расскажу историю о том, как коммуникация с сообществом может служить не только целям продвижения, но и помогать получать ценную обратную связи: быть и «зеркалом», и генератором новых идей одновременно. Как-то раз мы должны были провести встречу со студентами, и мы решили придумать для этого мероприятие нового типа — UX-хакатон. На растерзание был дан уже упомянутый выше Конструктор интерфейсов, и в пылу командной борьбы ребята должны были преодолеть с ним определённые препятствия: спроектировать с его помощью новую веб-страницу, предложить улучшенное UX-решение для одной из функций самого Конструктора и так далее. Какие же потоки полезных мыслей полились! И проблемы, и незамеченные возможности — как на ладони. Особенно важно в таком мероприятии то обстоятельство, что участники видят ваш продукт в первый раз, у них не замылен глаз.
Итак, представляю вам заложившую первые элементы DevRel в нашей компании Марину — волшебную лампу с негаснущим огоньком общительности. Не могу также не отметить с благодарностью, что Марина — автор всех иллюстраций к настоящей статье.

— Марина, расскажи, как это — рассказывать другим о платформенном решении? В чём специфика?
— Мне кажется, платформенное решение продвигать сложнее, чем просто приложение. Это требует работы вдолгую, терпения. Это как марафон на выносливость, а не стометровка на скорость. К тому же фреймворк всегда предполагает большую гибкость: отдельные его кусочки-библиотеки можно как использовать по отдельности, так и брать их в разных комбинациях, и число возможных сценариев использования здесь очень велико. А это значит, что много работы, как эти сценарии понятно объяснить.
— Было иногда тяжело?
— О да, ещё как! Но я не сдавалась. Иногда волновалась, как реагируют читатели и зрители, ставят ли огоньки, лайки. Тонко намекнула даже один раз в чате сообщества: «Сам себе огонёк не поставишь — никто не поставит». В общем, использовала все способы разогрева нашей дорогой аудитории, от ласки до дружеского шантажа! Ну и конечно, качество контента — вот что важно. Рассказать что-то сложное простым языком бывает непросто. А для фреймворка нельзя иначе: если что-то трудно воспринимается, то команды сопротивляются сильнее, предпочтут обойти такой фреймворк стороной. Сколько заходов нам иногда приходилось совершить, чтобы сделать текст для рассказа о какой-то библиотеке! Что-то напишешь, а от тебя на это раздавалось многозначительное и томное «хммм» — я сразу понимала, что текст получился запутанный, и мы выходили на новый круг «пиши, сокращай». Потом ты снова такой: «Хммммммм!..»
— Итеративный подход в разработке документации!
— Вот именно!
— Кстати, давай припомним, какие форматы у нас были востребованы, чтобы рассказать о нашей работе по фреймворку?
— Всё было: и небольшие посты в каналах сообщества, и статьи с примерами кода, и минутные видеодемо, и презентации. Как-то раз мы придумали презентацию-диалог — «Знакомство с фреймворком в 12 репликах». Это было что-то вроде FAQ, каждый слайд — один вопрос и один ответ, очень коротко. Всё для того же: чтобы сделать знакомство с фреймворком проще и быстрее. Постоянно работает чат примерно на 250 человек, где любой желающий может задать нам свой вопрос по фреймворку.
Вместе с фреймворком зарождалась у нас когда-то гильдия фронтенд-разработчиков, и появились тогда же первые онлайн-митапы, мы их назвали Фронтенд-пятницами. Они работали хорошо, на них приходили и разработчики из других команд, так они узнавали про фреймворк, особенно про UI Kit. Посмотреть поближе на какую-то библиотеку фреймворка — это часто становилось главной темой фронтенд-митапа. «Флаг вам в руки: близкое знакомство с чекбоксами и переключателями», — так могло быть написано на афише. С другой стороны, не все разработчики хотят выступать, сопротивляются. Помню, как-то раз один разработчик в ответ на просьбу вписаться в график выступлений мне однажды написал: «Убери меня из своей тетради смерти!»
Финал
Вы дочитали до конца — позвольте поэтому от души пожать вам руку. Минотавр никого не съел!
Мы раскинули перед вами кусочки пазла. Соберётся ли общая картина, полезная для вас? Зависит от вашей задачи, хотя я, конечно, очень надеюсь, что да!
Давайте подытожим. Я сформулирую свои выводы, накопившиеся за время работы над проектом.
Зачем же всё-таки вообще делать свой фреймворк, почему не ограничиться только готовыми решениями? Готовый фреймворк накладывает свои архитектурные правила и ограничения, а свой — позволяет установить собственные. Но эта возможность нужна не всегда.
Всё зависит от масштаба. Как от размера компании, так и от длительности проектного пути.
Если у вас монопродуктовая компания, небольшая команда — отмахнитесь от фреймворка, он даст мало пользы.
Если вы делаете MVP или небольшой продукт с активной фазой в полгода — бегите от мысли о фреймворке, безжалостно прихлопните её чем-нибудь тяжёлым!
Если же вам нужно технологически соединить наработки множества команд и горизонт вашего проекта составляет несколько лет — только тогда фреймворк станет не тянущим вас на дно камнем, а прочным фундаментом придумываемой конструкции.
Если вам будет интересно узнать о других интересных проектах, приглашаю вас в мой Telegram-канал и в Telegram-канал компании ГНИВЦ, в которой происходила вся эта сложносочиненная история.
Желаю вам вдохновляющей неопределённости, контрастного душа и возможности ежедневно находить прекрасное под оболочкой скучного. Успехов!