company_banner

Как организовать работу над библиотекой общих компонентов

    Если ваша компания делает несколько продуктов в едином стиле, однажды вам в голову придет идея сделать библиотеку с общим кодом. Например, с UI-компонентами, сервисом авторизации или для работы со сторонними API. Возможно, вы зададитесь вопросами: кто должен поддерживать этот код? Как доносить изменения до пользователей? В конце концов, как вообще заставить их пользоваться вашей библиотекой?

    С 2015 года я работаю в Тинькофф в отделе сервисов для бизнеса. За это время наша команда выросла с 3 до 60+ разработчиков, а экосистема Тинькофф Бизнес — с 3 до 50 веб-приложений. На разных этапах нашего развития мы подходили к работе с общим кодом по-разному, об этом я и хочу рассказать в этой статье.

    image

    Foundation: дешево и сердито


    Итак, перенесемся в 2015 год. У нас всего три веб-приложения: расчетно-кассовое обслуживание, зарплатный проект и панель управления. И столько же разработчиков.

    Интерфейсы приложений выполнены в едином стиле, а общий код вынесен в библиотеку Foundation в отдельном репозитории. Библиотека не компилируется — да и, строго говоря, компилировать там нечего, весь код на ES5 — и не публикуется в npm, а подключается по названию ветки в package.json. Для релизов продуктов создавались отдельные релизные ветки Foundation, чтобы зафиксировать состояние. Если же мы забывали зафиксировать ветку Foundation, при хотфиксе оказывалось, что Foundation изменился и хотфиксная версия просто не собирается.

    Так выглядело подключение Foundation в package.json:

    "dependencies": {
        ...
        "sme-foundation": "git+https://stash_url/sme-foundation.git#develop"
        ...
    },
    

    Главный принцип библиотеки в это время — общее владение кодом.

    Если для продуктовой фичи требовалась доработка Foundation — разработчик фичи делал это сам. А если они были несовместимы с прошлой версией, то он же правил использование этого кода во всех проектах. То же самое касалось и масштабных рефакторингов: хочешь отрефакторить компонент и изменить его API — пожалуйста, но заодно пройдись и по всем использованиям.

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

    К счастью, кода было не так много. Этот подход отлично работал для трех, четырех, пяти проектов… и обладал рядом плюсов:

    • Общий код был в одном месте и переиспользовался в проектах.
    • Проекты разрабатывались быстрее.
    • Библиотека росла.
    • У всех были знания и об общем коде, и о проектах, что делало ревью и принятие архитектурных решений более эффективными.
    • Необходимость доработки общего кода не блокировала разработку фич.

    В этот момент у нас был минимум документации: немного JSDoc и юнит-тесты. Для UI-компонентов не хватало визуальной витрины, и мы запилили дешевое и быстрое решение — Demo UI. По сути это было ангуляр-приложение, на страницы которого вставлялись сами компоненты и соответствующая разметка.

    demo ui

    В 2019 году вы вряд ли будете делать так же. Но вот что можно вынести из этой истории:

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

    Время шло, экосистема Тинькофф Бизнес и команда разработки росли. Когда проектов стало больше десятка, этот подход перестал работать.

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

    У новых разработчиков не было полной картины, что для чего сделано и как используется. Менять API компонента было страшно, из-за этого появлялись компоненты-франкенштейны с большим числом входных параметров.

    Иногда это приводило к неконсистентности в UX. Такие детали, как работа с клавиатуры, работа с фокусом, реализовывались по-разному в разных компонентах. А где-то и вовсе не реализовывались, ведь разработчики были сосредоточены на бизнес-фичах.

    В компании появились другие Angular-проекты, и мы предложили им тоже использовать нашу библиотеку. Поначалу они даже согласились… Но как только понадобились доработки, мы попали в сложную ситуацию: все наши разработчики заняты своими проектами, а у коллег нет мотивации разбираться с чужой библиотекой. За Foundation отвечали все и никто конкретно, и это не устраивало коллег.

    UI Kit и новый подход к организации работы


    Когда в 2017 году зашла речь о редизайне и новом UI Kit, мы начали разработку новых компонентов по-другому. Для начала у нас появилась выделенная команда.

    Команда


    Мы выбрали трех человек из продуктовых команд и сказали: «Теперь эти ребята делают UI-компоненты для других проектов».

    Что дала выделенная команда?

    • В первую очередь мы в сжатые сроки подготовили базовые компоненты для продуктовых команд. Уже через две недели после старта разработки коллеги получили самое необходимое. А дальше развивали библиотеку, ориентируясь на приоритеты заказчиков.
    • У выделенной команды был фокус именно на компоненты и UX. Нашей задачей было делать компоненты, качественные как с точки зрения конечного пользователя интерфейсов (правильные отступы, контрасты, работа с клавиатуры — для команды кита это не было мелочью), так и с точки зрения разработчиков продуктовых команд (консистентное API, удобное подключение, расширяемость).
    • Выделенная команда — это ответственность. Если в компоненте обнаружится баг, разработчик продукта не останется с ним один на один. Критичные баги правятся с высоким приоритетом и готовится хотфикс, а менее критичные исправляются в порядке очереди. Здесь стоит отметить, что до появления выделенной команды дефекты, не критичные для продуктовых команд (например, цвет плейсхолдера в инпуте и в селекте немного различаются), могли долго лежать в бэклоге, уступая место бизнес-фичам. Но для команды кита внешний вид компонентов — первый приоритет.

    Если экспертиза по компонентам сосредоточена в одной команде, то как научить других пользоваться этими компонентами? Для этого мы сделали отличную документацию.

    Документация


    Идея переработать наш демостенд давно витала в воздухе, и разработка новой библиотеки позволила это сделать.

    Тогда Storybook для Angular еще не вышел, поэтому мы пошли своим путем. Оно и к лучшему: собственная реализация не ограничивала нашу фантазию, мы могли делать абсолютно все, что хотели.

    И вот что мы сделали:

    1. Добавили информацию о библиотеке в целом: пошаговое описание того, как она подключается, и список поддерживаемых браузеров.
      getting started
    2. Подготовили детальное описание каждого компонента: для чего он нужен, как подключается, какие входные-выходные параметры поддерживает (с возможностью их потыкать, а-ля Storybook), примеры типового использования, список похожих компонентов.
      components docs demo

      code examples
    3. Добавили список проектов, в которых этот компонент используется.
      usage
    4. Стали собирать статистику по использованию кита в разных проектах: в какие проекты подключен, какая версия, сколько компонентов использовано, какая версия ангуляра и кита в каждом проекте — эта информация используется для планирования несовместимых изменений и отказа от поддержки старых версий фреймворка. Описание инструмента, который собирает эту статистику, заслуживает отдельной статьи.
      statistics
    5. Добавили версионирование: просмотр документации для каждой ранее выпущенной версии UI Kit.
      versions

    Конечно, все это появилось не сразу, а развивалось эволюционно.

    В рамках нашего отдела вполне хватило бы выделенной команды и документации. Коллеги приучены использовать общий код, знают разработчиков UI Kit и в целом очень лояльны.

    Но сам по себе дизайн UI Kit позиционировался как общий для всех продуктов компании, а значит, те же компоненты нужны были еще десяткам команд — от внутренних HR-проектов до WebOffice, рабочей системы для десятков тысяч удаленных сотрудников. Поэтому перед командой кита стояла более широкая задача: сделать качественный внутренний продукт, которым будут пользоваться все Angular-команды.

    В целом коллеги из других отделов были настроены позитивно, но все же у них были некоторые сомнения: будут ли нужные им фичи разрабатываться достаточно быстро, будет ли качественно… Кто-то уже начал делать компоненты сам.

    Чтобы разрешить эти сомнения, мы организовали работу максимально прозрачно.

    Прозрачность


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

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

    Какие средства использовали мы?

    Регулярные релизы и демо. Первое демо прошло через две недели после старта разработки и далее проводилось еженедельно — в день очередного релиза. Иногда было много новых фич, а иногда только правки багов. Мы проводили демо независимо ни от чего.

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

    Changelog. Мы внедрили conventional commits и генерацию из них чейнджлога, что значительно упростило командам переход на новые версии.

    changelog

    «Отзывчивая» команда. Как и у всех команд, у нас есть свой канал в Slack, в этом нет ничего нового. На самом деле у нас даже два канала: один для общения внутри команды и один для пользователей — ответов на вопросы, объявлений, опросов, проведения демо и прочих активностей.

    Важно, что поступающие вопросы действительно решаются. Некоторые вопросы — сложные и по делу, некоторые указывали нам на пробелы в документации (или давали знать, что не все ее читают). А иногда о своих трудностях писали новички в Angular. Команда кита с одинаковой готовностью помогала всем, не ленясь созвониться, если вопрос не решается в чате, или даже скачать чужой код себе. Естественно, такие коммуникации тратят время разработчиков, но это часть работы над библиотекой.

    Сейчас в канале кита уже более 200 пользователей, и многие вопросы решаются без участия разработчиков кита: коллеги делятся опытом и отвечают на вопросы друг друга.

    Новостные рассылки со списком изменений после релиза. Их целевая аудитория — разработчики, менеджеры и дизайнеры продуктов. В рассылках изменения описывались просто и доступно — что не всегда возможно в Changelog — и содержали картинки «было/стало». Я до сих пор уверена, что это отличный инструмент, но готовить качественные и наглядные дайджесты каждую неделю было тяжеловато. Спустя некоторое время мы перестали их рассылать, но, возможно, еще вернемся к этой практике.

    email

    Опросы пользователей дают взгляд со стороны и позволяют выявить слабые стороны. Например, по результатам очередного опроса мы узнали, что большинство коллег полностью согласны, что разработчики UI Kit готовы помочь, — значит, в этом направлении мы все делаем правильно. А вот с утверждением «Новые компоненты разрабатываются быстро» согласилось меньшее число опрошенных. Что делать с такими результатами? С одной стороны, довести до сведения всех членов команды и поработать над слабыми моментами. С другой — работать с пользователями. Если дело в скорости — уделять больше внимания объяснению, откуда такие сроки.

    feedback

    Опросы включали и открытые вопросы вида «Что мы можем улучшить?». Надо сказать, коллеги дали нам несколько ценных советов: например, сделать кнопку Copy около примеров кода или научить наши классы для дат работать с unixtime.

    Роль команды


    Выше я уже писала об «отзывчивой команде», которая многое решает для успеха библиотеки в масштабах компании. Хочу еще раз подчеркнуть эту мысль и рассказать о двух связанных практиках.

    Дежурства. В какой-то момент общения с коллегами стало так много, что казалось неизбежным введение дежурств. Так работают многие сервисные команды в Тинькофф. Раз в день, два дня, неделю один из членов команды дежурит. Он мониторит канал, отвечает на вопросы и берет на себя большую часть коммуникаций, в то время как его сокомандники не отвлекаются на поддержку.

    Мы до этого так и не дошли, а со временем необходимость отпала. Однако другим командам дежурства помогают.

    Аудит. Никто не знает компоненты кита лучше, чем команда, их разрабатывающая. А за два года разработки у ребят накопилась еще и прекрасная экспертиза в Angular. Сейчас мы вводим практику «аудитов от команды кита». Ребята подключаются к ревью продуктовых проектов, а также просматривают конечные продукты и их код. Их цель — выявить неправильные использования, доработать документацию и составить список хороших и плохих практик. О том, что из этого получится, мы расскажем в другой раз.

    Сложности и компромиссы


    Как и любой процесс с большим числом заинтересованных сторон, разработка общей библиотеки вынуждает искать компромиссы.

    Для нас оказалось непросто найти баланс между качеством кода и стабильностью API. С одной стороны, в начале работы менялся дизайн: появлялись новые компоненты или новые состояния старых компонентов; что-то, наоборот, устаревало и выпиливалось. С другой стороны — менялось видение разработчиков о правильном API компонентов. Все это неминуемо приводило к breaking changes. Но число пользователей библиотеки росло, и наши breaking changes доставляли им большие неудобства.

    Мы нашли компромисс: внесение breaking changes не чаще, чем в каждый пятый релиз, то есть раз в пять недель. Например, такие изменения могли заехать в релизы 0.50, 0.55, 0.60. Но переход с 0.50 на 0.51 не требовал никаких усилий. Это продолжалось до выхода стабильной версии 1.0.0. Сейчас API стабильно, а все масштабные изменения откладываются до 2.0.0 в неопределенном будущем.

    Несколько раз переход на новую версию требовал много рутинной работы: переименование префиксов, изменение импортов иконок и тому подобное. Для таких случаев мы реализовали скрипты миграции.

    Выводы


    В этой статье описан наш четырехлетний опыт работы над библиотеками общего кода. Какие выводы мы сделали?

    1. Даже маленькая команда не должна мириться с дублированием кода. Вынос кода в библиотеку — решение, доступное всем.
    2. Совместное владение общим кодом хорошо работает для небольших команд, до 10 проектов — пользователей библиотеки.
    3. С ростом числа проектов или вовлеченных разработчиков удобнее выделить команду с фокусом на общий код.
    4. Важная часть работы выделенной команды — общение с пользователями: демо, обучение, помощь, ведение документации.
    5. Не надо бояться мыслить шире и инвестировать время в инструменты. В нашем случае это удобная витрина с документацией и анализатор использования компонентов.
    Tinkoff
    it’s Tinkoff — просто о сложном

    Comments 16

      +3
      Было бы интересно увидеть открытый вариант (open source) Вашей библиотеки. Тогда было бы больше, что обсуждать.
        +2
        Суть не в библиотеке, а в подходе ;)
          +1
          Мы рассматриваем возможность опенсорса библиотеки.

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

          Кстати, насчет кода… мои коллеги делились некоторыми наработками, сделанными во время работы над библиотекой. Возможно вам будет интересно:

          0

          По вашему опыту, насколько реально автоматизировать и поддерживать актуальным список проектов, в которых используется тот или иной компонент?

            0
            Если проекты-пользователи — это ваши внутренние наработки, к коду которых есть доступ, то это совершенно реально. Мы пробовали разные подходы — на основе импортов модулей (у нас Angular, соответственно речь про ангуляровские модули) и на основе анализа шаблонов, у каждого есть свои плюсы и минусы.

            Думаю, об этом можно написать отдельную статью.
              +1

              Тут на неделе Денис Колесников из Авито рассказывал, какой у них Data Science навернут для этого. Рекомендую. Интересующая тематика начинается примерно с 2:14:00.
              http://longestjs.org/#talk-01

              0

              Я так понимаю, у вас только Angular компоненты. А что делаете с мажорными обновлениями фреймворка? Поддерживаете несколько версий библиотеки или обновляетесь все разом?

                0
                > Я так понимаю, у вас только Angular компоненты

                Если говорить о библиотеке компонентов, описанной в статье, то да, это именно Angular. Но в целом в компании есть и реализация дизайн-системы для проектов на React.

                > А что делаете с мажорными обновлениями фреймворка? Поддерживаете несколько версий библиотеки или обновляетесь все разом?

                Библиотеки на Angular 6/7 совместимы с приложениями на Angular 6/7/8 (более старых версий фреймворка у нас нет). Мы не поднимаем версию Angular немедленно после выхода мажорной версии фреймворка, т.к. это не требуется для приложений-пользователей на свежем Angular, но помешает приложениям на более старом.

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

                Что касается обновления всех приложений разом, мы стараемся не запускать и обновляться быстро, но когда приложений несколько десятков, и работают над ними разные команды, то обновить всё одновременно затруднительно, даем командам время.
                  0

                  Не думали о реализации общих компонент на одном из легковесных фреймворков типа $mol или Svelte, чтобы использовать их в приложениях на любом фреймворке, а не реализовывать их отдельно для Ангуляра, отдельно для Реакта, отдельно для <что там будет в моде через год>?

                    +1
                    Да, мы действительно рассматривали разные варианты, чтобы не делать две реализации. Но в итоге остановились на подходе с отдельными версиями.

                    • Так можем в полную меру использовать возможности фреймворка.
                    • Разрабатывать проще, потому что есть экспертиза в Angular. И наоборот, работая над китом, разработчики кита еще сильнее прокачиваются Angular и дают ценные советы коллегам из других команд.
                    • Разработчики из команд-пользователей могут присылать пулл-реквесты. У нас есть выделенная команда для поддержки кита, но мы поддерживаем и «внутренний open source». Для других ребят это возможность быстрее занести свои изменения и попробовать новый вид задач, не похожий на то, что делают в своем проекте.

                    Если бы кит внутри был основан на другом легковесном фреймворке или даже написан на чистом JS, развивать и поддерживать его было бы сложнее. Да и легковесный фреймворк так же может выйти из моды через год.
                0
                Расскажите подробнее, пожалуйста, о том как вы поддерживаете свою документацию по компонентам. Вручную или автоматически? Есть ли какой-то свой язык разметки или скриптов? Как собираете статистику использования компонентов в проектах?

                Перед нашей командой сейчас стоит похожая проблема: как донести до потребителей (разработчиков из других команд) подробную информацию обо всех имеющихся в библиотеке компонентах, их свойствах, примерах использования, и так далее.
                  0
                  Привет. По статистике Юля ответила ниже, просто промахнулась, похоже :) По поводу документации — в статье есть гифки с нашей интерактивной витриной. Мы вынесли её в отдельный пакет и продолжаем увеличивать её автоматизацию, пишем схематики для генерации и тому подобное. Но в корне своём она всё-таки собирается руками. Она состоит обычно из 3 вкладок:

                  1. Примеры, которые уж точно не автоматизировать. Там мы показываем несколько особенных примеров использования, покрывающие основные нужды пользователей. Часто, разработчик копирует оттуда код, как отправную точку, близкую к тому, что ему нужно.
                  2. Интерактивная документация. Тут лежит компонент и показаны все его инпуты, значения которых можно покрутить и сразу увидеть эффект.
                  3. Инструкция по подключению. Тут для большинства страниц примерно одно и то же, подключаем модуль, вставляем в шаблон.

                  У разработчиков такая витрина пользуется большой популярностью. В ней есть разделы и поиск, кроме того «натыканные» входные данные со страницы документации сохраняются в URL так что можно передавать друг другу ссылки сразу на готовое состояние компонентов. Кроме разработчиков, витрина полезна технологам и дизайнерам проектов, чтобы видеть что уже можно переиспользовать и какие возможности уже есть у тех или иных компонентов.
                    0
                    Спасибо за ответ.

                    То есть без ручной работы всё же не обойтись.

                    Мы когда-то использовали ExtJS и у нас было своё подобие Sencha Kitchen Sink как раз для этих целей, но её приходилось пополнять и поддерживать вручную одному человеку.

                    Теперь пишем на Lightning Web Components и возникла необходимость собрать нечто подобное Lightning Strike от Appiphony, однако на этот раз хочется заавтоматизировать всё это донельзя. Поэтому интересно знать как это сделано у других, чтобы не наступать на те же грабли.
                  +1
                  Я отвечу на вопрос о статистике использования компонентов, а мой коллега завтра подробнее расскажет о сборке документации, как раз он ее недавно доработал.

                  Статистику использования собираем полностью автоматически с помощью самописного решения, которое анализирует код шаблонов продуктовых проектов.

                  В первой версии получали только сам факт использования компонента. Сейчас дорабатываем новую версию, которая дополнительно сохраняет и какие параметры компонентов (inputs, outputs) используются, какие значения в них обычно передаются, умеет выявлять некоторые неправильные использования параметров (например, someInput="true" вместо [someInput]="true").

                  Подробности технической реализации, кажется, тянут на еще одну статью.

                  Дополнительно из package.json и package-lock.json берем информацию о версии библиотеки, TypeScript и Angular. Это вообще реализуется элементарно, а планировать обновления и следить за состоянием фронтенда в целом помогает очень и очень сильно.
                    0

                    Вы главное скажите: вы до сих пор разделяете поля в форме не отступами в стилях, а с помощью <br>, как на одной из первых картинок поста?

                      0
                      Нет, мы так не делаем. Да и раньше так не разделяли, за исключением этого неудачного примера из документации.

                    Only users with full accounts can post comments. Log in, please.