1. Фон
Разработка Devlime была организована таким образом, что несколько разработчиков одновременно работают над своими функциями и областями. Поскольку она разрабатывается на основе платформы Vizend, структура проекта и основные методы разработки были в какой-то степени унифицированы, а общая архитектура также была хорошо установлена.
Однако по мере увеличения масштаба продукта и количества функций стало всё больше ситуаций, когда необходимо было модифицировать существующие функции или анализировать код, написанный другими разработчиками. В этом процессе была часто встречаемая ситуация, когда одинаковые или очень схожие функции реализовывались каждым разработчиком совершенно по-разному.
Например, логика запроса определённых данных использовалась одинаково на нескольких экранах, но была реализована отдельно для каждого сервиса, а также UI, отображающий состояние на экране, имел разные стили и структуры, несмотря на похожие функции.
На начальном этапе разработки такой подход не являлся большой проблемой. Однако с увеличением количества функций количество дублирующего кода быстро возросло, и повторяющиеся ситуации, когда необходимо было искать и исправлять несколько мест для модификации одной и той же функции, происходили регулярно.
Кроме того, различия в способах отображения на экранах также приводили к проблемам с последовательностью в UX/UI опыте пользователей.
В связи с этим было решено не просто сосредоточиться на реализации функций, а улучшить процесс с акцентом на обобщение повторяющейся логики и UI, чтобы повысить повторное использование и снизить расходы на обслуживание.
2. Проблемы существующего подхода
2.1 Бэкенд
На бэкенде, несмотря на то, что функции одинаковые, способы их реализации различались у различных разработчиков, что приводило к следующим проблемам:
Во-первых, снизилась читаемость. Код, выполняющий одинаковые функции, был реализован в различных формах, что заняло много времени у тех, кто читал код, чтобы понять функцию.
Во-вторых, не хватало повторного использования. Определённые API вызовы библиотек или логика общего запроса данных были дублированы в нескольких службах. Например, логика запроса информации о сотрудниках была написана непосредственно в нескольких службах.
Member member = memberClient.findMember(citizenId);
Такой подход заставлял разрабатывать одинаковый код каждый раз при создании новой функции.
В-третьих, возросли расходы на обслуживание. Поскольку одинаковые функции присутствовали в разных местах, в случае необходимости исправления приходилось искать и изменять все места.
В-четвёртых, увеличилась вероятность возникновения ошибок. Хотя логика была схожей, в некоторых службах могли отсутствовать обработка исключений или логика проверки, что могло привести к различиям в результатах, несмотря на одинаковую функцию.
2.2 Фронтенд
В фронтенде также существовала аналогичная проблема.
Во-первых, экраны, отображающие одну и ту же информацию, работали по-разному. Например, такие данные, как статус (Status), приоритет (Priority) и тип (Type), отображались по-разному на каждом экране.
Во-вторых, UI/UX не были последовательными. Несмотря на то, что компоненты показывали одну и ту же информацию, на каждом экране применялись разные цвета, стили и макеты.
В-третьих, существовала повторяющаяся логика обработки многоязычности. Каждый раз, когда нужно было отобразить Display Name объекта Member, приходилось писать отдельную логику обработки языка для каждого экрана.
В-четвертых, общие функции не использовались повторно. Такие функции, как профиль участника, отображение времени, обработка ошибок и обработка паттернов, повторно реализовывались на нескольких экранах.
3. Направления улучшения и проектирование
Чтобы решить проблему, мы сначала проанализировали функции, которые используются повторно. В этом процессе объекты для унификации были разделены на две большие группы: бэкенд и фронтенд.
3.1 Цели унификации на стороне бэкенда
На стороне бэкенда были приоритетно выбраны следующие пункты.
-
Класс Enum
-
Логика использования клиента библиотеки
-
Общая логика запросов
-
Повторно используемые доменные функции
-
Общая логика задач
3.2 Общие темы фронтенда
В фронтенде общение было сосредоточено на следующих элементах.
-
Способ представления Enum
-
UI для отображения состояния и приоритета
-
UI профиля участника
-
Функция обработки многоязычия
-
Функция обработки часового пояса
-
Общие настройки Grid
-
Обработка ошибок
Мы не просто унифицировали функции, но и проводили проектирование в направлении поддержания согласованности пользовательского опыта.
4. Процесс применения
4.1 Разделение общих модулей бэкенда
Сначала мы унифицировали логику вызова библиотек, которые используются повторно. Ранее каждая служба напрямую вызывала библиотеки. Вот простой пример кода.
Member member = memberClient.findMember(citizenId);
Мы разделили это на уровень Extern.
@Service
@RequiredArgsConstructor
public class MemberExtern {
private final MemberClient memberClient;
public Member getMember(String citizenId) {
return memberClient.findMember(citizenId);
}
}
Это позволило управлять зависимостями библиотек в одном месте, а также унифицировать логику обработки исключений и последующей обработки.
4.2 Улучшение способа представления Enum
Ранее значение Enum напрямую отображалось на экране.
Late
LeftEarly
HolidayWork
Но было трудно использовать понятные для пользователя выражения, и изменения в пользовательском интерфейсе также влияли на домен. Чтобы решить эту проблему, мы разделили значения домена и значения пользовательского интерфейса.
LeftEarly("Early Leave")
Также на стороне фронтенда было улучшено управление отображением через DisplayName Map. Это позволяет реагировать на изменения экрана без необходимости изменения доменной модели.
4.3 Упрощение обработки мультиязычности
Мы улучшили способ обработки Display Name объекта Member для каждого экрана индивидуально. Ранее на каждом экране язык определялся вручную.
После улучшения мы изменили обработку с помощью общей функции.
getMemberDisplayName(member)
Это позволило управлять логикой обработки многоязычия в одном месте и минимизировать объем изменений при изменении языковой политики.
4.4 Упрощение компонентов UI
Повторно используемый интерфейс профиля участника был выделен в общий компонент.
Ранее для каждого экрана создавались аватары, рассчитывались инициалы и настраивались всплывающие подсказки отдельно. После улучшений мы изменили это на использование только одного компонента. Мы объединили определенные элементы UI/UX в компонент и повысили гибкость внутри компонента за счет параметров.
<MemberProfileGroup
members={members}
maxVisible={10}
/>
Таким образом, удалось обеспечить единообразие дизайна экранов и предоставить одинаковый пользовательский опыт на новом экране.
5. Результаты применения
После применения проектирования кода, пригодного для повторного использования, были отмечены следующие эффекты.
Во-первых, производительность разработки улучшилась. Ранее для разработки новых функций приходилось повторно реализовывать похожую логику каждую раз, но с использованием общих модулей и компонентов время разработки значительно сократилось.
Во-вторых, улучшилась поддержка. Общая логика была собрана в одном месте, что позволяет легко определить область воздействия при изменениях, и снизилась количество повторных исправлений.
В-третьих, качество кода улучшилось. Использование одного способа реализации для одной и той же функции позволило последовательно применять обработку исключений и логику проверки.
В-четвертых, улучшилась согласованность UI/UX. Мы смогли обеспечить единство пользовательского опыта, предоставляя общие функции, такие как отображение состояния, информация о членах, обработка многоязычности, одинаковым образом.
Наконец, мы поделились сведениями, организованными в процессе общих изменений, через документ Notion, чтобы члены команды могли разрабатывать по одинаковым стандартам. Это дало возможность заложить основу для повышения производительности разработки и поддерживаемости всей команды, выходящей за рамки простой повторной использования кода.
Bignow