- PVSM.RU - https://www.pvsm.ru -
Бывало, открываешь код-ревью — и чувствуешь себя археологом. Каждый кусок кода — как артефакт из разных времен: тут блестит бронзовая монетка, там торчит бивень мамонта, а чуть дальше — отпечатки времен .NET 4, пережившие три рефакторинга. Все это чудом взаимодействует, но порой страшно тронуть — вдруг вся конструкция рассыплется.
Эта история знакома многим командам. Мы привыкли думать, что хороший фреймворк — это гибкий фреймворк. Что чем больше у него возможностей, тем лучше. И действительно: гибкость помогает выйти на рынок, быстрее выпустить первую версию, подстроиться под новые требования. Но в какой-то момент эта гибкость начинает мешать.
Мы в команде разработки пользовательского интерфейса поняли это, когда наш общий код перестал быть общим: его было слишком много, он жил своей жизнью, и никто уже не знал, что в нем есть и как оно работает. С этого начался Kaspirin [1]— наш внутренний фреймворк, который мы создали не для расширения возможностей, а чтобы навести порядок и убрать лишнюю вариативность. Название придумалось само собой: смесь Kaspersky и aspirin — лекарство от головной боли, вызванной избыточной гибкостью.
На старте проекта гибкость — спасение. Команда пробует подходы, пишет вспомогательный код, торопится выпустить первый релиз. На этом этапе свобода важна: нужно быстро собирать функциональность и реагировать на изменения.
Но по мере взросления продукта гибкость теряет ту ценность, что имела на старте. Появляются библиотеки, выстраиваются процессы, а набор задач постепенно стабилизируется. Теперь от фреймворка ждут не новых возможностей, а стабильности: все нужное уже придумано и работает. И вот тут гибкость превращается из помощницы в источник хаоса.
Когда фреймворк предлагает десятки способов сделать одно и то же, команда рано или поздно начинает пользоваться ими всеми сразу. Так и появляется «зоопарк решений» — каждый пишет по-своему, потому что может.
При разработке UI потребность в троттлерах и таймерах — не редкость, но каждая ситуация требует тонкой настройки. Сами они писались редко — проблема была в другом. В проекте был общий набор таких инструментов, и у каждого разработчика со временем появлялись свои фавориты. Кто-то предпочитал один троттлер, кто-то другой — и тянул за собой именно тот, с которым уже разобрался. Если требовалась доработка, то человек правил именно знакомый вариант, не оглядываясь на остальные. В итоге инструменты развивались вразнобой, и фреймворк, разумеется, не мог это ограничить. На деле требовались инвентаризация и унификация.
На макете к тексту кнопки добавили иконку. В кодовой базе не было стандартного способа сделать это, и каждый реализовывал такую задачу по-своему. Потом появились версии со спиннером, с несколькими строками текста — и кнопки начинали отличаться. Это плохо вдвойне: если «зоопарк решений» в коде видят только разработчики, то разномастные кнопки в продукте замечают и пользователи. От окна к окну одну и ту же кнопку «шатает» по-разному. Теряется единообразие, и портится ощущение надежности продукта — будто что-то делали на коленке.
Так шаг за шагом гибкость перестает помогать. Когда в проекте нет единого способа решить задачу, ревью превращается в формальность. Появляется ощущение «и так сойдет!», код начинает плыть — вместе с уверенностью, что система под контролем.
Иногда последствия проявляются не в архитектуре, а прямо в повседневной работе, например, когда в соседнем отделе решают похожую задачу — но делают это по-своему. В итоге получается несколько разных реализаций одного и того же, и каждая работает по чуть отличным правилам. Потом кто-то уходит в отпуск, приходит новый сотрудник, а в итоге разобраться, что было правильным, уже невозможно.
Можно сказать, что это вопрос процессов и коммуникации. Но если бы фреймворк не позволял так свободно отходить от стандартных решений, таких расхождений могло бы просто не возникнуть. Ограничения не мешали бы — наоборот, помогали бы командам действовать согласованно.
Когда стало ясно, что общий код живет сам по себе и никто не знает, что в нем лежит, мы решили сделать надстройку над WPF — внутренний фреймворк Kaspirin. Его задача — не расширять возмож��ости, а наоборот, ограничивать их, наводя порядок и выравнивая подходы.
Архитектурно Kaspirin — это набор библиотек, каждая из которых предоставляет инструментарий, помогающий в разработке UI: визуальные компоненты, работу с темами, продуктовую палитру, наборы шрифтов и иконок, навигацию, локализацию, механизмы кастомизации, анимации, логирование, работу с асинхронностью и так далее. Идея в том, чтобы все эти части жили по единым правилам.
Ограничения и унификации, которые мы ввели:
Мы зафиксировали сквозной набор базовых сущностей, который одинаково используется и в макетах, и в коде.
Фиксированный набор цветовых токенов (продуктовая палитра), шрифтов, иконок и иллюстраций совпадает с тем, что дизайнеры используют в Figma.
Названия в макетах и названия в продукте идентичны — разработчику не нужно гадать, какой шрифт, цвет или компонент выбрать. Он просто берет нужный элемент из Kaspirin, и он полностью соответствует тому, что есть на макете.
Эта унификация сквозная — от дизайн-системы до фреймворка — и поддерживается автоматически. Благодаря ей дизайнеры и разработчики говорят на одном языке: каждому компоненту в макете соответствует свой компонент в Kaspirin, и визуальная часть компонентов закрыта для изменений — менять можно только содержимое, но не внешний вид. Например, базовый фреймворк позволяет перекрасить кнопку прямо в разметке:
<Button Background="#FF0000" ... />
В нашем случае такое не пройдет — фон нельзя менять точечно. Можно только выбрать тип кнопки:
<visuals:Button Type="Secondary" ... />
То же самое с наследованием и вид��мостью классов. Если раньше все методы были публичными и их можно было переопределять как угодно, то при переносе кода в Kaspirin мы осознанно задаем уровень видимости так, чтобы использовать класс можно было только предусмотренным способом. Формируются внешняя и внутренняя части фреймворка — осознанное ограничение во имя стабильности.
Мы не принуждаем — мы воспитываем. У нас есть и Roslyn-анализаторы, и элементы кодогенерации, и кастомные MSBuild-таски, и, конечно, все эти инструменты помогают нам соблюдать чистоту и порядок в общей кодовой базе, но главное не в этом. Люди бы с радостью не писали кастомные решения, если бы знали, где искать стандартные. Поэтому мы делаем демки для отдела, ведем канал с новостями о коммитах и пишем документацию — чтобы каждый знал, что у него под рукой есть готовый инструмент.
Интеграция Kaspirin шла не одномоментно, а по шагам. После разработки каждого компонента или инструмента создавалась отдельная задача на его внедрение — по одной на каждый продукт. Эти задачи постепенно попадали в скоуп релиза, и так фреймворк внедрялся в живые проекты. Критерий успеха был прост: чем больше старого кода удалось удалить — тем лучше. Удаление зоопарка решений и их замена на унифицированные инструменты из фреймворка означали, что цель достигнута.
Со временем эффект стал ощутимым. Количество косметических багов снизилось, интерфейс стал выглядеть консистентнее. Исчезли постоянные вопросы от коллег — теперь всем понятно, куда идти за нужным инструментом и где искать решение. Замечания по ревью вроде «так делать не надо» почти перестали появляться. Команда сосредоточилась на реализации бизнес-требований и продуктовых сценариев, а не на разборе того, почему «менюшку перекосило» или «в RTL все съехало».
Средняя скорость ревью не уменьшилась — даже наоборот, проверки стали строже, потому что принцип «и так сойдет!» больше не работает. Заодно и ревью стали точнее: на них обсуждается не возникший хаос, а конкретные улучшения.
Наполнение таких библиотек — не тот случай, когда стоит торопиться. Всегда есть соблазн добавить туда что-то новенькое, только что сделанное, но на этом легко обжечься. Каждую доработку стоит рассматривать через простые вопросы:
Насколько она действительно нужна?
Как часто ей будут пользоваться?
Впишется ли она в общий инструментарий?
Если все складывается — добавляем. Если нет, держим в уме: такая наработка теперь есть в продукте и, если похожие решения будут появляться снова, мы вернемся к ней и подумаем, стоит ли ее обобщить и вынести в общий код.
Здесь важно не загонять всех в рамки ради рамок. Ведь потребности выходить за них все ровно возникают, и в таких случаях мы стараемся делить креатив на осознанный (например, инициативы по комплексному редизайну), и случайный (вроде внезапных экспериментов от джунов). Первый поддерживаем, второй стараемся не пускать в общий код.
Мы еще не выложили все, что хотели: на очереди полный набор иконок, обновленная палитра, наши шрифты. Также готовим к публикации наш ToolKit — утилиту для просмотра инструментов фреймворка.
За кулисами Kaspirin есть еще одна часть, о которой уже шла речь ранее, — подсистема кодогенерации стилей компонентов. Она интегрируется с Figma и позволяет переносить стили из веба в продукт. Мы планируем открыть ее, но пока не можем: код еще не в финальном виде.
В остальном мы просто движемся дальше — добавляем новые компоненты, обновляем и дорабатываем уже имеющиеся, дополняем документацию и улучшаем инструменты. Планируем обновлять наш код на GitHub примерно три-четыре раза в год, чтобы держать систему в актуальном состоянии.
Работа над Kaspirin показала, что для эффективной разработки большой кодовой базы важно не столько создавать новые инструменты, сколько контролировать вариативность уже существующих решений. «Зоопарк» подходов к одной и той же задаче всегда оборачивается усталостью — от ревью, от попыток разобраться, кто, как и зачем это сделал.
Когда команда развивает собственный инструментарий, вовремя замечает общие потребности и фиксирует лучшие практики в единых правилах, у людей просто пропадает желание сочинять одноразовые решения. Код становится понятнее, ревью — спокойнее, плюс формируется культура бережного отношения к общему коду.
Иногда действительно нужно не добавлять фреймворку возможностей, а закрывать лишние. Это не ограничение ради контроля, а классный способ освободить команду от хаоса и вернуть фокус на задачу.
Гибкость нужна, чтобы стартовать.
А порядок — чтобы жить дальше.
Автор: ZergInside
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ui/437271
Ссылки в тексте:
[1] Kaspirin : https://github.com/KasperskyLab/Kaspirin
[2] Источник: https://habr.com/ru/companies/kaspersky/articles/968752/?utm_source=habrahabr&utm_medium=rss&utm_campaign=968752
Нажмите здесь для печати.