Практически каждая продуктовая компания рано или поздно приходит к мысли, что неплохо бы перестать раз за разом разрабатывать визуальные компоненты для каждого экрана в отдельности, что нужно подойти к этому вопросу более системно. В частности – сделать свою дизайн-систему. Мы в hh не стали исключением, и в этой статье хотим поделиться нашим собственным опытом разработки дизайн-системы, которая сейчас является общей для двух платформ – Android и iOS.  Мы поговорим о балансе между хаосом и системой, переиспользовании и дублировании, пройдемся по отношениям между дизайнерами и разработчиками, а также выясним, фронтенд – это только про покраску кнопок или нет? Если вам удобнее не читать, а смотреть или слушать, то вы можете посмотреть выпуск о дизайн-системе в нашем видео-блоге “Охэхэнных историй”.  Продукт берет свое начало из дизайна. Сначала дизайнеры рисуют макеты, а потом разработчики претворяют их желания в жизнь. Мы долго жили без дизайн-системы: дизайнеры делали всё по-своему, разработчики – по-своему, где-то компоненты дублировались, где-то писались с нуля. Из-за этого были несостыковки и в макетах, и в проектах, и в процессах. Так что мы решили систематизировать работу. В нашем случае всё началось с обновления довольно крупного куска приложения – создания резюме. В процессе проработки мы поняли, что этот раздел придётся переписывать с нуля, чтобы обеспечить требуемую гибкость. Этот момент и стал поворотным – мы решили, что помимо изменения кода можно пересмотреть свой подход к созданию и описанию UI-компонентов. Таким образом, главными предпосылками для дизайн-системы стали: переиспользование кода; желание добиться стройности, концептуальной последовательности всех  макетов. Вдохновиться чужим опытом у нас получилось, но переиспользовать чужие наработки целиком мы не могли, потому что у нас свои собственные компоненты и свой собственный дизайн. Важно понять, что дизайн-система – это не про компоненты в Figma / Sketch / Zeplin / etc и не про свёрстанные вьюхи в коде. Это про набор договорённостей и правил, по которым ваши разработчики взаимодействуют с вашими дизайнерами. Они могут договориться между собой как будут верстаться компоненты, на какие моменты все будут обращать внимание, где находится единый источник правды, и это всё равно будет дизайн-система, если все будут придерживаться принятых ограничений.  Наша дизайн-система построена на следующих принципах: каждый компонент в Figma имеет тот же нейминг и логику работы, что и компонент в коде; создание дизайн-системы происходит постепенно: если при отрисовке нового экрана у нас появляется новый компонент – мы его внедряем;  все новые макеты мы рисуем только с использованием компонентов дизайн-системы, не используем что-то кастомное; несколько раз думаем, прежде чем внедрить новый компонент. Все эти принципы делают общение между дизайнерами и разработчиками конструктивным и эффективным, позволяют рисовать универсальные компоненты для обеих платформ.  При появлении нового компонента мы не переделываем старые экраны сразу, потому что это долго и дорого. Если у вашего продукта высокая скорость изменений, то очень вероятно, что через год новый компонент и так проникнет на все экраны.  Около трёх лет назад дизайн на Android и iOS значительно отличался: использовались разные флоу, разная навигация, компоненты тоже выглядели по-разному. Вот так выглядело наше Android-приложение: никакой нижней навигации, NavigationDrawer с кучей разделов: А так выглядело iOS-приложение:  Пришла пора перемен. Сначала мы обновили навигацию – она стала везде одинаковой, потом – основные флоу и так далее. Макеты стали практически идентичными: кнопки, input-ы, ячейки списков – всё универсальное. На состояние июля 2021 года, приложение для поиска работы на обеих платформах выглядит идентично: везде появилась нижняя навигация, карточки вакансий, флоу поиска и создания резюме выглядят одинаково. Так что теперь стало возможным рисовать один макет с минимальными отличиями – где-то отличаются иконки, может быть, шапка экрана на Android-е выглядит иначе, progress bar-ы. Это позволяет сильно экономить время как дизайнеру, так и команде. Благодаря дизайн-системе каждый знает, где какие компоненты используются и как они себя ведут. Ещё один плюс – при обсуждении макетов можно сосредоточиться именно на сценариях использования, а не на визуальных отличиях двух платформ.  Из минусов, бывали случаи, когда компоненты одной платформы неожиданно для разработчиков перекочёвывали на другую. Как-то раз наш дизайнер нарисовал компонент, принёс его в команду iOS, а это оказался input-компонент из Android.  Вот такой компонент для ввода мы сейчас используем в iOS: Выглядит и работает как стандартный Android-ный TextInputLayout. Это нормальная тенденция – платформы сейчас довольно сильно сближаются и постоянно заимствуют друг у друга удачные решения. Для Android-приложений дизайн-система представляет собой один gradle-модуль, который мы подключаем к нужным feature-модулям, которым требуются определённые компоненты дизайн-системы. В модуле есть всё, что нужно для работы с UI: и компоненты, и атомы, и утилиты. В этом модуле лежит множество xml-файлов – в них организована цветовая палитра, наши стили, иконки, вёрстка компонентов. Мы не разбиваем компоненты на отдельные модули, так нам гораздо проще понять, какие из них уже реализованы, а каких ещё не хватает. Плюс – подключать компоненты становится удобнее.  Также мы сделали специальный application-модуль с приложением-галереей, чтобы демонстрировать дизайнерам и разработчикам работу с теми или иными компонентами.  Да-да, дизайн приложения взяли из material-components-android. Такое приложение удобно как для показа функциональности компонентов, так и для экспериментов с новыми технологиями – hilt, navigation component, jetpack compose, как пример. Если говорить непосредственно об устройстве дизайн-системы, то здесь тоже один модуль (один Framework), который по необходимости подключается к другим модулям. В framework-е лежат атомы – цвета, стили текста, типы теней. Там же находятся свёрстанные компоненты, которые можно вкладывать друг в друга с помощью специальных контейнеров. Таким образом их удобно комбинировать, и получается почти декларативный стиль при описании UI.  Аналогично Android-у, в модуле есть всё, что нужно для работы с UI. Также мы используем Fugen – специальный инструмент для генерации части кода для ресурсов из Figma (у Figma есть API, через который можно это делать). Разработка и внедрение этого инструмента была не очень дорогой, больше времени потребовалось на коммуникацию: как разобраться с автоматизацией, как дизайнерам подготовить сверстанные Figma-файлы. Сейчас из Figma мы генерируем цвета текстов стиля и отступы: запускаем скрипт, и получаем готовый для использования код. В Android Fugen пока не используют.  Насколько была оправдана разработка такого инструмента, сложно судить, потому что атомы менялись не так часто, однако есть уверенность, что Fugen нам пригодится, когда мы вплотную займёмся внедрением тёмной темы.  Задумываемся ли мы над внедрением современных декларативных фреймворков? Разумеется. Но на разных платформах ситуация отличается.  В iOS-команде придумали декларативный DSL для компонентов дизайн-системы и описывают весь UI в коде, поэтому никаких препятствий к переходу на Swift UI мы не видим.  Возможно, через полгода или позже команда сможет перевести приложение на новые рельсы.   В Android же весь UI верстается в XML, мы не переходили ни на какой из существующих декларативных фреймворков. Поэтому переход на Jetpack Compose будет сложнее, придется переписывать систему компонентов, верстать их по-новому. Так что Compose принесет огромный пласт работ, и мы к этому постепенно готовимся: читаем статьи, изучаем codelab-ы, пробуем написать компоненты ДС на Compose-е, ставим звёздочки в нужных issues.  Хорошо, мы сформулировали общие принципы работы с компонентами, но что делать в ситуации, когда общий компонент не подходит в конкретной ситуации? Стоит ли тратить силы и время на дошлифовку? Здесь дизайнеры могут сильно помочь разработчикам. Дизайнеры приближены к бизнесу, они знают куда будут развиваться макеты, и знают, какие кусочки и как они собираются переиспользовать. Для разработчиков это означает, что дизайнер может оставить “поле для манёвров”, подготовив нас к будущим изменениям, чтобы мы могли заложить необходимую гибкость при создании компонента. Для разработчиков, в первую очередь, важен диалог с дизайнером. Перед тем, как что-то обновлять, нужно обговорить все нюансы: что за компонент нарисован, должен ли он быть общим или кастомным. Тогда работа станет ещё эффективнее.  Но ничто не даётся просто так. С каждым добавленным компонентом дизайн-система постепенно усложняется. Разумеется, существует и оправданная сложность, когда вы пытаетесь повторить компоненты соседней платформы, сделать API более удобным. Но в погоне за обобщением можно случайно сотворить нерасширяемых монстров с тонной generic-ов. Чтобы уменьшить сложность разработки, на обеих платформах мы придерживаемся важного принципа ограниченности вариантов. Если вариантов использования одного и того же компонента слишком много, то работа над ним станет бесконечным процессом.  В некоторых случаях язык программирования может помочь в уменьшении сложности кода. Например, в iOS-команде, для достижения наибольшей переиспользуемости компонентов, пошли по пути создания вкладываемых друг в друга компонентов. Берутся компоненты-контейнеры, в которые можно вставить другие компоненты, в них – третьи и так далее. За счёт этого generic-и растут в геометрической прогрессии, и пользоваться компонентами напрямую становится неудобно. Но решением стали короткие alias-ы, которые и служат для поддержания ограниченности вариантов. Дизайн-система – это продукт синергии разработчиков и дизайнеров. Если дизайнер со своей стороны сделает всё красиво, а разработчики не реализуют это в коде, то такая работа не будет иметь смысла. И точно так же с другой стороны. Первый шаг к появлению и развитию дизайн-системы может сделать дизайнер. Он закладывает основы, которые позволяют правильно масштабироваться в дальнейшем. Также, чаще именно дизайнер проявляет инициативу в развитии структуры компонентов.  Разработчики со своей стороны проявляют инициативу в улучшении существующих компонентов, их шлифовке. Они знают, как сделать компонент удобнее для использования в коде, и иногда подмечают то, что не заметил дизайнер.  Командная работа в этом процессе очень важна. Без понимания общей картины, без взаимных договорённостей сложно выполнить работу качественно. В нашей команде это понимают, и все готовы на долгие созвоны-обсуждения очередных компонентов для синхронизации их вида на обеих платформах. Что важнее для дизайнера – системный подход или полёт фантазии? Как и везде, здесь важен баланс. С одной стороны, продуктовому дизайнеру, разрабатывающему дизайн-систему, очень важно иметь системное мышление. Но, с другой стороны, слишком увлёкшись систематизацией, можно забыть про обычных людей, для которых и создаётся продукт. Эстетика тоже важна, поэтому хорошо бы иметь в пару к систематизатору ещё и творческую личность. Для разработчиков действуют схожие правила. Важно корректно реализовать компонент, чтобы он правильно выглядел и правильно работал. В то же время необходимо думать о создаваемом компоненте как о продукте для других разработчиков и уделять внимание API компонента, делать его как можно удобнее.  Для дизайнера важно быть открытым разработчикам, понимать их боль и осознавать, что от качества предоставленных макетов зависит итоговый результат и качество работы.  Проблемы в коммуникациях обычно возникают из-за недостаточной прозрачности процессов, как со стороны дизайнера, так и со стороны разработчиков. Дизайнер разработал компонент, но не сказал об этом разработчикам? Проблема. Разработчик реализовал удобное расширение компонента, но дизайнер об этом не знает? Это тоже проблема.  Со временем, конечно, подобных проблем становится меньше и меньше. Дизайн-система как раз призвана решать многие из них. Ещё одна проблема, в которой вам может помочь дизайн-система, – это непопадание в сроки. Благодаря дизайн-системе у дизайнеров и разработчиков появляется понимание, какие макеты можно реализовать быстро и легко, а какие – сложно и дорого. Что касается “дружбы” между платформами: конечно, есть специфические паттерны, которые подходят одной системе и не подходят другой, но есть паттерны, которые удобно использовать в обеих системах. Kotlin, Swift – похожи между собой, поэтому в случае, когда одна из платформ уходит вперёд, всегда можно посмотреть на её реализацию и утащить что-то к себе.  В разных дизайн-системах всё устроено по-разному: где-то лежат только общие цвета, где-то – только UI Kit. В нашей системе мы добавили haptic feedback – это фича, которая углубляет взаимодействие с UI-компонентами. Правда, она больше актуальна для iOS-платформы, на Android с вибрациями не всё так гладко. Дело в том, что на iOS вибрация достаточно мягкая, приятная и привычная пользователю, так как железом занимается один производитель. На Android из-за огромного зоопарка устройств можно такой тактильный отклик пользователю отправить, что у него телефон со стола упадет! Для начала дизайнер описал набор возможных реакций кнопок с вибрацией: А потом начал добавлять эти эффекты при отрисовке макетов: Также мы стараемся думать про accessibility. При проектировании кнопок мы учли режим для слабовидящих, чтобы кнопки работали корректно и чётко проговаривали текст.  Есть две грани разработки фронтенда. Первая – про корректность реализации: ты должен разработать компонент, чтобы он правильно выглядел. Вторая – про удобство этой реализации. Вторая часть – очень сложная: нужно много договариваться, нужно разрабатывать прототипы, нужно проводить демо, считаться с чужим мнением. В процессе приходишь к пониманию. что фронтенд – это не только про покраску кнопок, это ещё и про коммуникации, про умение предчувствовать, насколько будет удобно то, что ты сделаешь. Очень часто возникает ощущение, что в данный момент ты не можешь сделать всё удобно. потому что не до конца понятно, как это всё видят другие разработчики. Иногда компоненту нужно вылежаться в коде, распространиться по кодовой базе, прежде чем поймёшь, как именно надо было его реализовать. Но что делать с “неправильно” реализованными компонентами? Исправлять. Но не разом, а постепенно, встраивая задачи в беклог, чтобы не тормозить разработку. Дизайн-система – это способ из хаоса создать консистентный опыт. У меня плохие новости – не получится прийти к бизнесу и выпросить несколько месяцев для разработки дизайн-системы. Нужно делать её постепенно, начиная с самых простых вещей: попросить дизайнера систематизировать палитру цветов, шрифтов, базовых стилей. Затем договориться о базовых компонентах, которые будут использоваться в разработке новых фич.  Также важно договориться о том, как работать с легаси. Можно использовать наш подход – не трогать старые экраны при добавлении новых компонентов, – а можно переделывать всё сразу (но это дороже). Дизайн-система не останавливает разработку фич, она инкрементально выращивается в общем потоке работы. Важно придерживаться договорённостей, распространять знания в команде, вовремя предупреждать об изменениях.  Мы в hh продолжаем работу над добавлением компонентов, над их синхронизацией между платформами.  Из дополнительных технических задач мы видим расширение автоматизации рутинных действий, адаптацию декларативных фреймворков, конечно же, добавление тёмной темы. Команда постепенно растёт, поэтому нужно думать не только про написание нового кода, но и про сохранение качества старого. А со стороны дизайнеров есть интересная задача по масштабированию дизайн-системы на всю компанию. Android-разработчик