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

Для МойОфис как мультипродуктовой экосистемы со сквозными сценариями — все эти вопросы очень актуальны! Поэтому на прошлой неделе JS-еры, дизайнеры и UX-исследователи нашей и других компаний собрались на митапе под названием Frontend&UX Talks, чтобы обсудить вопросы современного веба. Получилось интересно, драйвово и даже сказочно! (И я в прямом смысле этого слова :-))

Под катом расскажем в 7 тезисах, к каким выводам пришли и как именно: поехали!

О фронтенде в 2025-м году мы поняли, что...

В JS

Понятный и простой код — куда важнее, чем кажется

Алексей Золотых, МойОфис

Руководитель отдела группы разработки веб-редакторов

В эпоху ИИ-агентов и других game changer-ов код пишется для человека, а не машины. На чтение кода в целом мы тратим намного больше времени, чем на его написание — поэтому к его синтаксису и понятности надо относиться особенно внимательно.

Тут в первую очередь идет речь об одной метрике:

Cognitive complexity — чем больше различных блоков в методах (if, for и т.д.), тем сложнее становится код. Особенно речь идет о «передозировке» if: с ним код становится чересчур тяжелым для чтения, его сложно разбивать на отдельные модули.

Но есть решение мультиметоды

По сути это специальные функции, которые динамически выбирают конкретную реализацию (метод) на основе переданных аргументов. В отличие от классического полиморфизма, где выбор метода зависит от типа объекта (this), мультиметоды могут учитывать любые аргументы функции.

Полиморфизм — это когда код работает с разными типами сущностей. И мультиметоды как раз один из инструментов для поддержки такого формата.

Если же говорить о тестировании, то в контексте работы с мультиметодами и диспетчингом (функция dispatch), тестирование проходит гладко и практически без нюансов!

Но есть и минусы:

  • Сложности с поддержкой (приходится «тащить» библиотеку в код вручную).

  • В случае с диспетчеризацией — может проседать производительность, поэтому иногда без тех самых If-ов не обойтись:(

  • Сложнее становится и отладка, потому что мультиметоды зачастую пронизывают весь код .

Примитивы это сказка!

Елена Кудина, Контур

фронтенд-разработчик

Если коротко, примитивы – это способ описания UI на основе формата BEM-JSON (структурированный JSON-формат, работающий по методологии БЭМ: блоки, элементы, модификаторы) – инструмент для отображения интерфейсов. 

Раньше в Контур.Фокус вместо них использовались готовые HTML-страницы с бэкенда, но от них пришлось отказаться по трём причинам:

  • ограниченная интерактивность;

  • даже при небольших изменениях приходилось обращаться к бэкенду;

  • отсутствие управления состоянием и маршрутизацией. 

На этом фоне примитивы оказались куда удобнее и эффективнее. Они позволяли быстро создавать единое оформление для блоков с разным содержанием.

Как работают примитивы? 

Чтобы лучше понять их принцип, рассмотрим пример контракта при работе с ними. На интерфейсе списка телефонов видно, что бэкенд, используя примитивы, передаёт фронтенду задачу: «Есть список номеров, команда примитивов – теперь отобрази блок телефонов». Затем фронтенд рендерит нужный компонент.

Вот как это работает: 

  1. Указываем тип блока (первая строка – "phonesList"), чтобы фронтенд понял, какой примитив использовать. 

  2. Передаём массив телефонов с настройками – как в обычном дата-контракте. 

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

Но есть и минусы...

Главные проблемы примитивов: 

  • Обучение работе с примитивами занимает много времени (как для бэкенда, так и для фронтенда). 

  • Примитивы плохо адаптируются под нестандартные дизайны. 

  • Отклонение от контракта требует внесение правок в существующий примитив или создания нового.

  • Низкая скорость добавления новых компонентов (нужны доработки и на бэкенде, и на фронтенде).

  • Обучение работе с примитивами занимает много времени (как для бэкенда, так и для фронтенда). 

  • Примитивы плохо адаптируются под нестандартные дизайны. 

  • Отклонение от контракта требует внесение правок в существующий примитив или создания нового.

  • Низкая скорость добавления новых компонентов (нужны доработки и на бэкенде, и на фронтенде).

Реактивное программирование актуальное и удобное! Но не для всех...

Дмитрий Грузынов, МойОфис

Старший инженер-программист, разработка веб-редакторов

Библиотека RxJS — незаменима для систем со сложным UI/UX взаимодействием. Для примера это касается таких продуктов, как:

  • Инструменты для дизайна, например Figma.

  • Корпоративные чаты (в нашем случае — мессенджер Squadus :)

  • Сложные приложения (из наиболее известных — тот самый Netflix!)

  • Интерактивные визуализации и дашборды.

Почему же RxJS так хорош?

Вот основные причины:

  • Много операторов «из коробки».

  • Хорошая тестируемость благодаря разбиению логики на независимые и переиспользуемые операторы.

  • Широкие возможности по контролю Backpressure.

  • Можно использовать в качестве State Management, совместно с react-rxjs и redux-observable.

Если сравнивать с другими JS решениями, то получается такая картина:

В сравнении с redux-saga, он проигрывает по читаемости и поддерживаемости исходного кода, особенно при наличии сложной бизнес-логики c несколькими потоками данных.

Если к этому добавляется необходимость контроля Backpressure, что неизбежно в подобных системах, то логика на Saga усложняется еще больше.

В сравнении с effector:

Код на Effector будет более императивным, с бóльшим количеством boilerplate. Также для Effector значительно меньше операторов «из коробки» — многое придётся писать с нуля. Однако в Effector лучше обстоят дела с отладкой: есть интеграция с Redux DevTools. И это уже решение вместе с state management.

Для управления состоянием, в RxJS необходимо использовать связки с другими библиотеками. Мы используем для этого связку React+RxJs+react-rxjs.

Библиотека react-rxjs добавляет примитивы createSignal, для создания сигнала, то есть пары observable и функции emit для него, и state, для хранения состояния из сигнала.

Для получения состояния из React компонента, добавляется хук useStateObservable.

В отличие от классических подходов с централизованным хранилищем (как в Redux), здесь используется децентрализованная модель состояния. 

Такой подход очень напоминает архитектуру Effector:

Оба подхода:

  • отказываются от концепции Single Source of Truth, хранилища состоят из децентрализованных реактивных потоков;

  • основаны на реактивной модели;

  • у них похожий набор примитивов.

Но есть и минусы:

  • В React проектах реактивное программирование не всегда оправдано — у RxJS высокая сложность обучения, из-за чего искать релевантных для него специалистов может быть гораздо сложнее. Все-таки эта библиотека больше знакома Angular разработчикам, которые не всегда имеют прямое отношение к React.

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

    Тут может помочь другой способ улучшения работы библиотеки (но его мы спойлерить не будем, смотрите выступление!).

    Что по итогу?

  • RxJs избыточен в React для простых систем без сложного асинхронного кода с несколькими источниками.

  • Он хорош в управлении Backpressure, может и в State management, улучшает читаемость кода.

  • Есть сложности в освоении и в отладке.

Следить за CSS обновлениями каждый квартал – базовый минимум

Павел Востриков, Лаборатория Касперского

Архитектор веб-направления

Кирилл Оленичев, Лаборатория Касперского

Старший разработчик

Ребята уверены: без CSS спек в регулярной разработке никуда.

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

1)<dialog>

Дает доступ к нативным модальным (всплывающим на экране) окнам, широко используется с с 2022 года.

Может использоваться также и для показа разных сайдбаров без дополнительных проблем и головной боли.

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

  • showModal() — открыть блокирующее окно диалога (всегда поверх);

  • show() — открыть неблокирующее окно диалога;

  • close(smothValue) — закрыть диалог.

backdrop — псевдоэлемент наложения, используется для стилизации

2) positon anchor и anchor name

Новый концепт позиционирования элементов с их свободным расположением в рамках html-кода.

Anchor-name задает элементу имя, позволяет обозначить его в качестве ориентира для позиционирования других элементов. Например, назначить кнопку, которая будет служить якорем для размещения других элементов.

Position-anchor позволяет прикрепить элемент с абсолютной позицией к элементу-якорю, который был установлен с помощью anchor-name.

Важно: В докладе есть и другие примеры спек и фичей с наглядным лайвкодингом. Чтобы ознакомиться с ними — дочитайте статью до конца)

В UX

Темизация это нелегко, но есть лазейки!

Михаил Проскурин, МойОфис

Старший дизайнер-аналитик, Группа продуктового дизайна

Михаил на примере разработки единой дизайн-системы для продуктов «МойОфис» рассказал, как унифицировать цветовую палитру, если у тебя спектр продуктов в рамках одной экосистемы, и какие здесь могут быть нюансы.

Главный нюанс — разнообразие продуктов. Каждый из них должен иметь особенный дизайн, но в то же время быть частью единой айдентики. Тут не обошлось без такой «базы» цвета интерфейса как работа с пастельными цветами, акцент на светлые и темные темы в UI и другое.

Если говорить о самой дизайн-системе, то в случае с кейсом Михаила, основными требованиями стали:

  • генерация оригинальной палитры;

  • автоматизированное переключение тем

и другие.

Но вот вопрос: как все это реализовать в контексте Figma? Там ведь ограничения по количеству тем и по модам. Но это если не знать лазейки :)

На деле мы нашли способ, благодаря которому добавили 12 тем и 4 мода в рамках одного проекта! А если добавить ещё несколько категорий, то количество тем можно увеличить и до 20.

Как это работает?

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

Редизайны продуктов – это не страшно! (ну почти...)

Кнарик Джавадян, МойОфис

Руководитель группы, отдел дизайна редакторов

Светлана Самойленко, МойОфис

Дизайнер-аналитик

Ребята показали кейс редизайна настольных редакторов МойОфис практически с нуля!

Тут пришлось столкнуться с множеством вызовов, например:

1) Нелинейные сценарии

90% процентов пользовательских сценариев редакторов — нелинейны. Проще говоря: может использовать наши функции и инструменты в разные моменты вне зависимости от конкретной цели, из-за чего потенциальный CJM строить становится все сложнее.

2) Работа с иконками

Они долгое время были на ПНГ — а это то еще страдание: при любом увеличении страдает качество, контрастность и т.д., поэтому пришлось глобально рефакторить все подобные элементы.

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

  • Интерфейс должен быть контекстным. Большинство людей кликают правой кнопкой мыши на контекстное меню, даже если оно уже автоматически всплыло для пользователя сбоку.

  • Цвет имеет значение. Респонденты обращали внимание на цвет аналогичных решений по сравнению с нашим, поэтому свежее визуальное решение может сильно разнообразить пользовательский опыт (как бы банально это не казалось). Особенно им понравились акцент на пастельные тона.

  • Оборудование ОЧЕНЬ сильно влияет на исследования. Возможно, клавиатура для исследования с нестандартной раскладкой? Или мышь имеет нетипичные функции, о которых пользователь даже не подозревает? У авторов доклада произошли обе ситуации, поэтому с инструментами для исследования стоит быть начеку.

За сложными интерфейсами – глаз да глаз...

Антон Бессонов, Alpha Research Center

Исследователь пользовательского опыта и клиентских сценариев

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

А почему Delivery?

Производственный процесс в UX состоит из двух ключевых этапов:

  • Discovery — поиск и проработка пользовательской проблемы, сбор гипотез;

  • Delivery — разработка решения, тестирование и сбор обратной связи.

Одни из ключевых технологий, связанных с delivery-частью, — айтрекинг и замер эмоционального отклика. Через реакцию глаз выделяются потенциально слабые места интерфейса и нюансы, на которые стоит обратить внимание дизайнерам.

Эмоциональный отклик и в целом реакция респондента на интерфейс считываются ИИ. Например, нейросети сервиса Sense Machine могут замечать и трактовать микровыражения лица, определять эмоции человека по видео и выдавать детальный анализ по 7 параметрам: счастье, грусть, удивление, злость, страх и даже отвращение — все-таки пользовательский опыт бывает разным :)

Этот метод помогает находить такие потенциальные «косяки» дизайна, как:

  • сложно найти определенную кнопку;

  • некоторые важные элементы плохо заметны;

  • пользователь в целом не понимает какие-либо процессы в приложении/сервисе.

Вывод: айтрекинг и измерение эмоционального отклика — must have к классическим модерируемым UX-тестам для продукта в 2025 году. Особенно вкупе с дневниковыми исследованиями, которые помогут проверить вещи, выходящие за рамки интерфейсов, и провести глубокий доскональный анализ всего CJM пользователя.

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

Комментарии (18)


  1. artptr86
    04.07.2025 13:17

    В React проектах реактивное программирование не всегда оправдано

    Это пять!


    1. zolotyh
      04.07.2025 13:17

      React не реактивен. Так что все вроде логично


      1. artptr86
        04.07.2025 13:17

        Всё-таки в нём есть модель реактивности, хоть и реализованная довольно криво из-за легаси. Но считать реактивным программированием исключительно RxJS, в котором не меньше проблем, точно нельзя.


        1. Trilemma
          04.07.2025 13:17

          На самом деле тут немного про разную реактивность.

          Реактивное программирование — это парадигма, основанная на потоках данных (data streams) и распространении изменений

          В React реактивность в реагировании и перестройке интерфейса при изменении состояния.

          Так что если рассматривать React как сильно урезанную версию реактивного программирования, без явных потоков, императивно и с завязкой на компоненты, то можно притянуть. Но кажется этот спор немного бессмысленен.

          Команда React сама официально декларирует что не хотела бы чтобы React удовлетворял принципам реактивного программирования https://ru.legacy.reactjs.org/docs/design-principles.html#scheduling


          1. artptr86
            04.07.2025 13:17

            Это одна и та же реактивность, только на неё смотрят с разных позиций. Должна существовать возможность выражать потоки данных и автоматически распространять изменения. В React поток данных может быть выражен хуками useState, useEffect, useMemo и так далее, а распространение изменений производится в рендер-циклах на основе списков зависимостей. Не самая красивая и удобная реализация, но как есть. Реактивное программирование в общем случае не требует, чтобы данные были явно завёрнуты в некоторые объекты потока и чтобы ими можно было оперировать явно, это всё может быть скрыто под капотом как в Svelte (или даже Excel).


            1. Trilemma
              04.07.2025 13:17

              Все-таки useEffect вызывается после перерисовки компонента, а не непосредственно в ответ на изменение данных. Это связывает реактивность в React с жизненным циклом компонентов, а не с потоками данных, как в классическом реактивном программировании.

              useEffect может банально не вызваться, если при перерисовке компонента произошла ошибка.


              1. artptr86
                04.07.2025 13:17

                Да, потому что цикл обработки данных исторически был привязан к рендер-циклу. Поэтому сама реализация довольно убогая. С другой стороны, реактивность в React работает по pull-модели, когда данные втягиваются из потоков данных при необходимости, а не push-модели RxJS, когда они выталкиваются из потока. Но это не означает ущербность модели, просто в ней больше ленивости: просто нет смысла забирать и обрабатывать данные, если они не будут в конечном счёте отрендерены.

                С этой позиции комментарий разработчиков React («Нам будет сложнее контролировать планирование, если мы позволим пользователям использовать подход «выталкивание при наличии данных», распространённый в функциональном реактивном программировании. Мы хотим, чтобы наш код был «связующим». В команде есть внутренняя шутка, что React должен был называться «Планировщик», потому что React не хочет быть полностью «реактивным».») следует понимать именно как следование pull-модели вычислений, а не когда события самостоятельно форсируют рендер.

                Конечно, в более продвинутых реактивных реализациях, как у MobX, Vue, Solid, перевычисление зависимостей отвязано от рендер-цикла, и, соответственно, нет накладных расходов на непосредственно рендер виртуального DOM, но и они следуют pull-модели реактивности как более эффективной и даже более последовательной по духу функционального программирования.


                1. Trilemma
                  04.07.2025 13:17

                  Нет смысла забирать/обрабатывать данные, если в конечном итоге все данные идут в отрисовку. Но это не всегда так, не со всеми данными.

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

                  React is not a generic data processing library. It is a library for building user interfaces.

                  If something is offscreen, we can delay any logic related to it. If data is arriving faster than the frame rate, we can coalesce and batch updates.

                  Думаю, именно поэтому ее и предложили в шутку назвать Schedule, а не из-за pull-based подхода.

                  Думаю, что дальше бессмысленно продолжать дискуссию. Пусть каждый останется при своем.


                  1. artptr86
                    04.07.2025 13:17

                    Извините, но я всё же не понимаю, как это свидетельствует об отсутствии реактивности в React


                    1. Trilemma
                      04.07.2025 13:17

                      Я не писал Вам по поводу отсутствия реактивности в React. Только о том что он не выполняет все принципы реактивного программирования, которое оперирует потоками данных.


                      1. artptr86
                        04.07.2025 13:17

                        А где, собственно, найти более-менее правдивый и полный список этих принципов? Разные сайты совершенно разные вещи пишут, а в документации Реакта принципы не перечислены.


        1. izibrizi2
          04.07.2025 13:17

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


          1. artptr86
            04.07.2025 13:17

            Нет, почему же: реактивность интерфейса — это способность интерфейса автоматически обновляться при изменении данных, на которые он опирается. Что же касается точечного изменения DOM, то он точечно меняется и в React, но через промежуточную отрисовку Virtual DOM. Но к реактивности это всё равно относится сильно косвенно.


            1. izibrizi2
              04.07.2025 13:17

              Точечную отрисовку можно делать и без вдом, как например в preact. Вдом нужен был во времена экплорера, в котором дом работал медленно. Мы говорим про точечные изменения.

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


  1. JerryI
    04.07.2025 13:17

    • Сложные приложения (из наиболее известных — тот самый Netflix!)

    Почему стримелка видеопотоков + поиск - это сложное приложение?


    1. Trilemma
      04.07.2025 13:17

      Ну как будто назвать Netflix простым, даже с точки зрения GUI, приложением - сильное упрощение.

      Youtube же Вы наверное не считаете простым с точки зрения UI/UX взаимодействия приложением? Чем Netflix кардинально отличается?

      Netflix - один из главных евангелистов RxJs. Начал использовать RxJs еще до Angular 2, как раз для решения проблем в реализации сложной бизнес логики.


      1. artptr86
        04.07.2025 13:17

        Вообще это не так удивительно, учитывая java-бэкграунд Нетфликса, и то, что в те годы для JS практически никаких адекватных решений для реактивности не было.


  1. DmitryOlkhovoi
    04.07.2025 13:17

    Избавиться от реакта, должно быть первым)))