Автоматизация проектирования. К языку кодирования самовозводящегося интерфейса пользователя
В статье расскажу о способе унификации проектирования интерфейса на примере одного проекта. Вместо того, чтобы рисовать 50 структурно однотипных макетов в Figma, мы спроектировали 5 из них, ввели язык кодирования и разметки дизайна и с его помощью описали остальные макеты. Вы узнаете о границах применимости такого подхода, о его плюсах и минусах.
Что было на входе
Нам поручили спроектировать систему обработки документов для импортных заказов. Для обеспечения доставки заказа от производителя до склада покупателя нужно создать и подготовить много разных документов: финансовых, отгрузочных, товаросопроводительных, таможенных. В этом процессе задействовано много людей разных специализаций, каждый подключается к обработке документов на своём этапе, когда до него доходит очередь.
На старте любого проекта мы погружаемся в предметную область и визуализируем существующий бизнес-процесс. Для визуализации мы используем карту процесса-опыта — метод, который разработал арт-директор Byndyusoft Андрей Шапиро. Метод отлично подходит для описания сложных бизнес-процессов, таких, с которым мы столкнулись в кейсе, о котором я рассказываю в этой статье.
Схематизация процесса показала нам эту сложность. В чём она состояла:
- более 50-и этапов обработки документов для одного заказа;
- более 12-и ролей, участвовавших в обработке документов;
- более 150-и пользователей будущей системы.
Заказчик думал, что система будет состоять из 12 личных кабинетов и потребуется 12 фронтенд-разработчиков для такой работы. На самом деле нет.
![Фрагмент карты процесса-опыта](/images/blogs/design-automation-towards-a-coding-language-for-a-self-erecting-user-interface/1_process_experience_map.png)
Описанный процесс напомнил нам конвейерную обработку объекта. Лента, на которой находится объект, в нашем случае — заказ, движется от одного окна обработки к другому, от одного сотрудника к другому. Исполнитель, каждый в своём окне, со своими задачами ждёт прихода заказа на его пост для обработки. На каждом этапе заказ преобразуется: приходит в одном состоянии, выходит в другом, но сохраняет свои артефакты в течение всего пути, местами приобретает новые.
![Конвейерная обработка объекта](/images/blogs/design-automation-towards-a-coding-language-for-a-self-erecting-user-interface/2_object_pipelining.png)
Макеты в Figma
Мы поняли, что нам нужно спроектировать окно обработки заказа. Сотрудник с любой ролью видит окно типовой структуры, но набор функций у него свой, соответствующий его задачам. Окно мы назвали «задачей». Каждая задача имеет артефакты входа и выхода.
Артефакты входа:
- данные о заказе;
- тип задачи;
- описание задачи;
- время на обработку;
- документы для обработки;
Артефакты выхода:
- принятое решение;
- комментарий;
- обработанные документы;
- отправка заказа дальше, на следующий этап обработки.
Артефакты могут присутствовать или отсутствовать в задаче, зависит от того, что нужно сделать на этапе, когда эта задача появилась. Например, на одном этапе надо сообщить системе и другим участникам процесса, что документ проверен, и он в порядке или не в порядке, либо перенаправить другой роли на уточнение. На другом этапе нужно приложить новые документы. На третьем — указать транспортную компанию, которая повезёт заказ. И так далее. От выходящих данных, которые требуется добавить в задачу, зависит, какие функции мы дадим пользователю интерфейса. От функций зависит, какие элементы он увидит на экране.
Получилась такая структура.
Дано:
- наименование задачи;
- описание задачи;
- документы — опционально.
Решение:
- варианты решения;
- описание задачи;
- документы — опционально;
- комментарий
Ниже примеры экранов с такой структурой:
![](/images/blogs/design-automation-towards-a-coding-language-for-a-self-erecting-user-interface/3_screen_examples.png)
Мы создали несколько экранов в Figma. И стало ясно, что нужно нарисовать ещё около 50-и похожих экранов. А если понадобится добавить новых участников процесса и новые задачи для них, то нужно будет создавать новые макеты. Но нам хотелось потратить меньше усилий, избавить себя от рутины и придумать способ, который упростит масштабирование системы для всех её создателей. Поэтому мы перешли от императивного подхода к декларативному — описанию интерфейса через язык, а не набор макетов.
Переход к кодированию интерфейса
Суть идеи в том, чтобы описывать интерфейс с помощью кода и передавать его разработчикам в готовом виде. Для описания мы выбрали язык YAML, поскольку его разметка наиболее простая и аккуратная, в отличие, например, от формата JSON, да и в целом она приятнее для глаз дизайнера.
![](/images/blogs/design-automation-towards-a-coding-language-for-a-self-erecting-user-interface/4_yaml_via_json.png)
Проектировщик, фронтендер и бэкендеры договорились о правилах наполнения файлов .yaml и создали документ, описывающий эти правила. Правила касались структуры документа, порядка следования элементов, использования терминов предметной области, которые должны отображаться в интерфейсе. В дальнейшем каждый, кто вносил изменения в шаблоны, опирался на этот документ.
![](/images/blogs/design-automation-towards-a-coding-language-for-a-self-erecting-user-interface/5_documentation.png)
Дизайнер описывал все задачи по шаблону, о котором договорилась команда. Тем временем фронтенд-разработчик получил макеты в Figma и создал все необходимые компоненты, из которых должен собираться интерфейс, и запрограммировал внешний вид задачи. После описал механизм визуализации — как из JSON собрать набор компонентов. YAML мы конвертировали в JSON, с которым работали разработчики.
Дизайнер создавал файлы с кодом, отправлял коммит в репозиторий, тестировщик их проверял, после чего бэкендер его апрувил.
![](/images/blogs/design-automation-towards-a-coding-language-for-a-self-erecting-user-interface/6_yaml.png)
Как протестировать шаблоны до внедрения?
Перед тем, как отдать шаблоны разработчикам, нужно убедиться, что они описаны верно, что мы всё учли. А для этого важно увидеть результат своей работы. Проверять текстовый файл сложно, есть вероятность пропустить ошибку. Кроме того, макеты мы обязательно показываем стейкхолдерам, чтобы они их согласовали. Но как это сделать, если у нас файл с кодом? Для этого мы написали визуализатор шаблонов, в котором и смотрели результат своей работы.
![Пример визуализации изменений шаблона задачи](/images/blogs/design-automation-towards-a-coding-language-for-a-self-erecting-user-interface/7_visualization_of_task_template_changes.gif)
Что на бэкенде
В качестве технического решения, для оркестрации бизнес-процесса мы выбрали BPM-движок — Camunda. Она позволяет создавать схемы сложных бизнес-процессов, выполнять описанные процессы, а также визуализировать текущее состояние сущности. В нашем случае сущностью является заказ.
Бэкенд-разработчики описали бизнес-процесс в Camunda, используя BPMN-нотацию. Сам процесс они списывали с карты процесса-опыта, созданной дизайнерами. На схеме Camunda нарисованы кубики. Кубик — это точка процесса, точка процесса — задача пользователя по обработке документа. В каждом кубике указан ID-ключ задачи, и в файле YAML с описанием задачи также указан ID-ключ. По этому ключу бэкенд понимает, какую задачу надо создать на определённом этапе процесса обработки заказа.
![Схема процесса в Camunda](/images/blogs/design-automation-towards-a-coding-language-for-a-self-erecting-user-interface/8_camunda.png)
Задачи должны следовать по заданной логике с момента создания заказа и до его прихода в точку назначения.
Заказ создаётся в сторонней системе, с которой мы интегрированы. Как только создаётся заказ, в нашу систему приходит сигнал о создании заказа. В этот момент запускается процесс в Camunda. Бэкенд обрабатывает сигнал из Camunda и создаёт первую задачу, которая отображена в Camunda. Затем получает ID задачи из Camunda и данные о заказе, берёт шаблон JSON из репозитория и заполняет его данными.
Сохраняет в базе данных созданную задачу c заполненным JSON, задача появляется в списке на фронте. В момент, когда пользователь открывает задачу в интерфейсе фронт запрашивает задачу у бэкенда, получает сгенерированный, заполненный JSON и отрисовывает его. Пользователь выполняет задачу и сохраняет результат, фронт отправляет результат на бэкенд.
Выводы
Подход имеет свои плюсы и минусы.
Из плюсов:
- Подход позволил сократить часы фронтендера и дизайнера, что полезно в продуктовой разработке. Часы фронтендера сократились за счёт того, что он отрисовал все компоненты, запрограммировал внешний вид экрана один раз, и дальше экраны строились без его участия. И таких экранов можно было сделать сколько угодно много. Часы дизайнера сократились не так драматично, но от рутины он точно был избавлен — вместо копирования макетов и их изменения он копировал шаблон Yaml и менял там строчку кода.
- Дальнейшее масштабирование системы возможно без привлечения дизайнера и фронтендера, если не потребуется создания новых элементов интерфейса.
- Благодаря выбранному решению, мы избежали дублирования работы на всех этапах: шаблон делает один человек, другие эту работу не дублируют. В чём дублирование — дизайнер рисует макет, затем разработчик описывает его кодом. В нашем подходе дизайнер сразу описывает макет кодом и передаёт его разработчикам.
- Добавление новой задачи занимает фиксированное время, что позволяет дать точную оценку.
- Обеспечивает высокую скорость разработки и реагирования на замечания стейкхолдеров. Порой правки удавалось довести до прода за 5 минут.
Минусы подхода обусловлены границами его применимости:
- Подходит для кейсов, когда нужно спроектировать множество структурно похожих экранов.
- Требует дисциплины, внимательности и синхронизации команды.
- Если требуется ввести новый элемент интерфейса, то нужно привлекать дизайнера и фронтендера.
- Подход сработал, так как каждый раз работа шла с одним заказом в определённом состоянии, и поэтому участники конвейера последовательно обогащали его или проверяли. Если бы надо было работать кросс-заказно, такой подход не подошёл бы, потребовалось бы в итоге инструментальное решение для работы с коррекцией заказов.