Если ваша компания делает несколько продуктов в едином стиле, однажды вам в голову придет идея сделать библиотеку с общим кодом. Например, с UI-компонентами, сервисом авторизации или для работы со сторонними API. Возможно, вы зададитесь вопросами: кто должен поддерживать этот код? Как доносить изменения до пользователей? В конце концов, как вообще заставить их пользоваться вашей библиотекой?
С 2015 года я работаю в Тинькофф в отделе сервисов для бизнеса. За это время наша команда выросла с 3 до 60+ разработчиков, а экосистема Тинькофф Бизнес — с 3 до 50 веб-приложений. На разных этапах нашего развития мы подходили к работе с общим кодом по-разному, об этом я и хочу рассказать в этой статье.
Итак, перенесемся в 2015 год. У нас всего три веб-приложения: расчетно-кассовое обслуживание, зарплатный проект и панель управления. И столько же разработчиков.
Интерфейсы приложений выполнены в едином стиле, а общий код вынесен в библиотеку Foundation в отдельном репозитории. Библиотека не компилируется — да и, строго говоря, компилировать там нечего, весь код на ES5 — и не публикуется в npm, а подключается по названию ветки в package.json. Для релизов продуктов создавались отдельные релизные ветки Foundation, чтобы зафиксировать состояние. Если же мы забывали зафиксировать ветку Foundation, при хотфиксе оказывалось, что Foundation изменился и хотфиксная версия просто не собирается.
Так выглядело подключение Foundation в package.json:
Главный принцип библиотеки в это время — общее владение кодом.
Если для продуктовой фичи требовалась доработка Foundation — разработчик фичи делал это сам. А если они были несовместимы с прошлой версией, то он же правил использование этого кода во всех проектах. То же самое касалось и масштабных рефакторингов: хочешь отрефакторить компонент и изменить его API — пожалуйста, но заодно пройдись и по всем использованиям.
Если хочешь переиспользовать в одном проекте то, что уже есть в другом — тоже не проблема, просто вынеси это в библиотеку.
К счастью, кода было не так много. Этот подход отлично работал для трех, четырех, пяти проектов… и обладал рядом плюсов:
В этот момент у нас был минимум документации: немного JSDoc и юнит-тесты. Для UI-компонентов не хватало визуальной витрины, и мы запилили дешевое и быстрое решение — Demo UI. По сути это было ангуляр-приложение, на страницы которого вставлялись сами компоненты и соответствующая разметка.
В 2019 году вы вряд ли будете делать так же. Но вот что можно вынести из этой истории:
Время шло, экосистема Тинькофф Бизнес и команда разработки росли. Когда проектов стало больше десятка, этот подход перестал работать.
Изменение общих компонентов стало обходиться слишком дорого. Разработчик одного проекта уже не был знаком со всеми остальными проектами, поиск использований компонента и внесение изменений усложнились. Время на реализацию новых фич, затрагивающих Foundation, росло.
У новых разработчиков не было полной картины, что для чего сделано и как используется. Менять API компонента было страшно, из-за этого появлялись компоненты-франкенштейны с большим числом входных параметров.
Иногда это приводило к неконсистентности в UX. Такие детали, как работа с клавиатуры, работа с фокусом, реализовывались по-разному в разных компонентах. А где-то и вовсе не реализовывались, ведь разработчики были сосредоточены на бизнес-фичах.
В компании появились другие Angular-проекты, и мы предложили им тоже использовать нашу библиотеку. Поначалу они даже согласились… Но как только понадобились доработки, мы попали в сложную ситуацию: все наши разработчики заняты своими проектами, а у коллег нет мотивации разбираться с чужой библиотекой. За Foundation отвечали все и никто конкретно, и это не устраивало коллег.
Когда в 2017 году зашла речь о редизайне и новом UI Kit, мы начали разработку новых компонентов по-другому. Для начала у нас появилась выделенная команда.
Мы выбрали трех человек из продуктовых команд и сказали: «Теперь эти ребята делают UI-компоненты для других проектов».
Что дала выделенная команда?
Если экспертиза по компонентам сосредоточена в одной команде, то как научить других пользоваться этими компонентами? Для этого мы сделали отличную документацию.
Идея переработать наш демостенд давно витала в воздухе, и разработка новой библиотеки позволила это сделать.
Тогда Storybook для Angular еще не вышел, поэтому мы пошли своим путем. Оно и к лучшему: собственная реализация не ограничивала нашу фантазию, мы могли делать абсолютно все, что хотели.
И вот что мы сделали:
Конечно, все это появилось не сразу, а развивалось эволюционно.
В рамках нашего отдела вполне хватило бы выделенной команды и документации. Коллеги приучены использовать общий код, знают разработчиков UI Kit и в целом очень лояльны.
Но сам по себе дизайн UI Kit позиционировался как общий для всех продуктов компании, а значит, те же компоненты нужны были еще десяткам команд — от внутренних HR-проектов до WebOffice, рабочей системы для десятков тысяч удаленных сотрудников. Поэтому перед командой кита стояла более широкая задача: сделать качественный внутренний продукт, которым будут пользоваться все Angular-команды.
В целом коллеги из других отделов были настроены позитивно, но все же у них были некоторые сомнения: будут ли нужные им фичи разрабатываться достаточно быстро, будет ли качественно… Кто-то уже начал делать компоненты сам.
Чтобы разрешить эти сомнения, мы организовали работу максимально прозрачно.
Если из этой статьи вы вынесете только одну мысль, то пусть это будет следующая идея: для успеха библиотеки недостаточно делать хорошие компоненты. Вы должны быть максимально прозрачны и клиентоориентированы. В данном случае клиенты — это разработчики конечных продуктов.
Вы должны всеми возможными средствами доносить до коллег, что делаете, почему и как этим пользоваться.
Какие средства использовали мы?
Регулярные релизы и демо. Первое демо прошло через две недели после старта разработки и далее проводилось еженедельно — в день очередного релиза. Иногда было много новых фич, а иногда только правки багов. Мы проводили демо независимо ни от чего.
Забегая вперед, скажу, что сейчас основные работы завершены, API стабилизировалось и команда перешла к чередованию релизов — один релиз с фичами, один с правками и доработками — и демо проводится только на релизах с новыми фичами.
Changelog. Мы внедрили conventional commits и генерацию из них чейнджлога, что значительно упростило командам переход на новые версии.
«Отзывчивая» команда. Как и у всех команд, у нас есть свой канал в Slack, в этом нет ничего нового. На самом деле у нас даже два канала: один для общения внутри команды и один для пользователей — ответов на вопросы, объявлений, опросов, проведения демо и прочих активностей.
Важно, что поступающие вопросы действительно решаются. Некоторые вопросы — сложные и по делу, некоторые указывали нам на пробелы в документации (или давали знать, что не все ее читают). А иногда о своих трудностях писали новички в Angular. Команда кита с одинаковой готовностью помогала всем, не ленясь созвониться, если вопрос не решается в чате, или даже скачать чужой код себе. Естественно, такие коммуникации тратят время разработчиков, но это часть работы над библиотекой.
Сейчас в канале кита уже более 200 пользователей, и многие вопросы решаются без участия разработчиков кита: коллеги делятся опытом и отвечают на вопросы друг друга.
Новостные рассылки со списком изменений после релиза. Их целевая аудитория — разработчики, менеджеры и дизайнеры продуктов. В рассылках изменения описывались просто и доступно — что не всегда возможно в Changelog — и содержали картинки «было/стало». Я до сих пор уверена, что это отличный инструмент, но готовить качественные и наглядные дайджесты каждую неделю было тяжеловато. Спустя некоторое время мы перестали их рассылать, но, возможно, еще вернемся к этой практике.
Опросы пользователей дают взгляд со стороны и позволяют выявить слабые стороны. Например, по результатам очередного опроса мы узнали, что большинство коллег полностью согласны, что разработчики UI Kit готовы помочь, — значит, в этом направлении мы все делаем правильно. А вот с утверждением «Новые компоненты разрабатываются быстро» согласилось меньшее число опрошенных. Что делать с такими результатами? С одной стороны, довести до сведения всех членов команды и поработать над слабыми моментами. С другой — работать с пользователями. Если дело в скорости — уделять больше внимания объяснению, откуда такие сроки.
Опросы включали и открытые вопросы вида «Что мы можем улучшить?». Надо сказать, коллеги дали нам несколько ценных советов: например, сделать кнопку 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 в неопределенном будущем.
Несколько раз переход на новую версию требовал много рутинной работы: переименование префиксов, изменение импортов иконок и тому подобное. Для таких случаев мы реализовали скрипты миграции.
В этой статье описан наш четырехлетний опыт работы над библиотеками общего кода. Какие выводы мы сделали?
С 2015 года я работаю в Тинькофф в отделе сервисов для бизнеса. За это время наша команда выросла с 3 до 60+ разработчиков, а экосистема Тинькофф Бизнес — с 3 до 50 веб-приложений. На разных этапах нашего развития мы подходили к работе с общим кодом по-разному, об этом я и хочу рассказать в этой статье.
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. По сути это было ангуляр-приложение, на страницы которого вставлялись сами компоненты и соответствующая разметка.
В 2019 году вы вряд ли будете делать так же. Но вот что можно вынести из этой истории:
- Совместное владение общим кодом отлично работает для небольших команд.
- Даже если вас всего трое, не ленитесь вынести повторяющийся код, это доступно командам любого размера.
- Даже обычная страница с перечнем компонентов может существенно упростить вам жизнь.
Время шло, экосистема Тинькофф Бизнес и команда разработки росли. Когда проектов стало больше десятка, этот подход перестал работать.
Изменение общих компонентов стало обходиться слишком дорого. Разработчик одного проекта уже не был знаком со всеми остальными проектами, поиск использований компонента и внесение изменений усложнились. Время на реализацию новых фич, затрагивающих Foundation, росло.
У новых разработчиков не было полной картины, что для чего сделано и как используется. Менять API компонента было страшно, из-за этого появлялись компоненты-франкенштейны с большим числом входных параметров.
Иногда это приводило к неконсистентности в UX. Такие детали, как работа с клавиатуры, работа с фокусом, реализовывались по-разному в разных компонентах. А где-то и вовсе не реализовывались, ведь разработчики были сосредоточены на бизнес-фичах.
В компании появились другие Angular-проекты, и мы предложили им тоже использовать нашу библиотеку. Поначалу они даже согласились… Но как только понадобились доработки, мы попали в сложную ситуацию: все наши разработчики заняты своими проектами, а у коллег нет мотивации разбираться с чужой библиотекой. За Foundation отвечали все и никто конкретно, и это не устраивало коллег.
UI Kit и новый подход к организации работы
Когда в 2017 году зашла речь о редизайне и новом UI Kit, мы начали разработку новых компонентов по-другому. Для начала у нас появилась выделенная команда.
Команда
Мы выбрали трех человек из продуктовых команд и сказали: «Теперь эти ребята делают UI-компоненты для других проектов».
Что дала выделенная команда?
- В первую очередь мы в сжатые сроки подготовили базовые компоненты для продуктовых команд. Уже через две недели после старта разработки коллеги получили самое необходимое. А дальше развивали библиотеку, ориентируясь на приоритеты заказчиков.
- У выделенной команды был фокус именно на компоненты и UX. Нашей задачей было делать компоненты, качественные как с точки зрения конечного пользователя интерфейсов (правильные отступы, контрасты, работа с клавиатуры — для команды кита это не было мелочью), так и с точки зрения разработчиков продуктовых команд (консистентное API, удобное подключение, расширяемость).
- Выделенная команда — это ответственность. Если в компоненте обнаружится баг, разработчик продукта не останется с ним один на один. Критичные баги правятся с высоким приоритетом и готовится хотфикс, а менее критичные исправляются в порядке очереди. Здесь стоит отметить, что до появления выделенной команды дефекты, не критичные для продуктовых команд (например, цвет плейсхолдера в инпуте и в селекте немного различаются), могли долго лежать в бэклоге, уступая место бизнес-фичам. Но для команды кита внешний вид компонентов — первый приоритет.
Если экспертиза по компонентам сосредоточена в одной команде, то как научить других пользоваться этими компонентами? Для этого мы сделали отличную документацию.
Документация
Идея переработать наш демостенд давно витала в воздухе, и разработка новой библиотеки позволила это сделать.
Тогда Storybook для Angular еще не вышел, поэтому мы пошли своим путем. Оно и к лучшему: собственная реализация не ограничивала нашу фантазию, мы могли делать абсолютно все, что хотели.
И вот что мы сделали:
- Добавили информацию о библиотеке в целом: пошаговое описание того, как она подключается, и список поддерживаемых браузеров.
- Подготовили детальное описание каждого компонента: для чего он нужен, как подключается, какие входные-выходные параметры поддерживает (с возможностью их потыкать, а-ля Storybook), примеры типового использования, список похожих компонентов.
- Добавили список проектов, в которых этот компонент используется.
- Стали собирать статистику по использованию кита в разных проектах: в какие проекты подключен, какая версия, сколько компонентов использовано, какая версия ангуляра и кита в каждом проекте — эта информация используется для планирования несовместимых изменений и отказа от поддержки старых версий фреймворка. Описание инструмента, который собирает эту статистику, заслуживает отдельной статьи.
- Добавили версионирование: просмотр документации для каждой ранее выпущенной версии UI Kit.
Конечно, все это появилось не сразу, а развивалось эволюционно.
В рамках нашего отдела вполне хватило бы выделенной команды и документации. Коллеги приучены использовать общий код, знают разработчиков UI Kit и в целом очень лояльны.
Но сам по себе дизайн UI Kit позиционировался как общий для всех продуктов компании, а значит, те же компоненты нужны были еще десяткам команд — от внутренних HR-проектов до WebOffice, рабочей системы для десятков тысяч удаленных сотрудников. Поэтому перед командой кита стояла более широкая задача: сделать качественный внутренний продукт, которым будут пользоваться все Angular-команды.
В целом коллеги из других отделов были настроены позитивно, но все же у них были некоторые сомнения: будут ли нужные им фичи разрабатываться достаточно быстро, будет ли качественно… Кто-то уже начал делать компоненты сам.
Чтобы разрешить эти сомнения, мы организовали работу максимально прозрачно.
Прозрачность
Если из этой статьи вы вынесете только одну мысль, то пусть это будет следующая идея: для успеха библиотеки недостаточно делать хорошие компоненты. Вы должны быть максимально прозрачны и клиентоориентированы. В данном случае клиенты — это разработчики конечных продуктов.
Вы должны всеми возможными средствами доносить до коллег, что делаете, почему и как этим пользоваться.
Какие средства использовали мы?
Регулярные релизы и демо. Первое демо прошло через две недели после старта разработки и далее проводилось еженедельно — в день очередного релиза. Иногда было много новых фич, а иногда только правки багов. Мы проводили демо независимо ни от чего.
Забегая вперед, скажу, что сейчас основные работы завершены, API стабилизировалось и команда перешла к чередованию релизов — один релиз с фичами, один с правками и доработками — и демо проводится только на релизах с новыми фичами.
Changelog. Мы внедрили conventional commits и генерацию из них чейнджлога, что значительно упростило командам переход на новые версии.
«Отзывчивая» команда. Как и у всех команд, у нас есть свой канал в Slack, в этом нет ничего нового. На самом деле у нас даже два канала: один для общения внутри команды и один для пользователей — ответов на вопросы, объявлений, опросов, проведения демо и прочих активностей.
Важно, что поступающие вопросы действительно решаются. Некоторые вопросы — сложные и по делу, некоторые указывали нам на пробелы в документации (или давали знать, что не все ее читают). А иногда о своих трудностях писали новички в Angular. Команда кита с одинаковой готовностью помогала всем, не ленясь созвониться, если вопрос не решается в чате, или даже скачать чужой код себе. Естественно, такие коммуникации тратят время разработчиков, но это часть работы над библиотекой.
Сейчас в канале кита уже более 200 пользователей, и многие вопросы решаются без участия разработчиков кита: коллеги делятся опытом и отвечают на вопросы друг друга.
Новостные рассылки со списком изменений после релиза. Их целевая аудитория — разработчики, менеджеры и дизайнеры продуктов. В рассылках изменения описывались просто и доступно — что не всегда возможно в Changelog — и содержали картинки «было/стало». Я до сих пор уверена, что это отличный инструмент, но готовить качественные и наглядные дайджесты каждую неделю было тяжеловато. Спустя некоторое время мы перестали их рассылать, но, возможно, еще вернемся к этой практике.
Опросы пользователей дают взгляд со стороны и позволяют выявить слабые стороны. Например, по результатам очередного опроса мы узнали, что большинство коллег полностью согласны, что разработчики UI Kit готовы помочь, — значит, в этом направлении мы все делаем правильно. А вот с утверждением «Новые компоненты разрабатываются быстро» согласилось меньшее число опрошенных. Что делать с такими результатами? С одной стороны, довести до сведения всех членов команды и поработать над слабыми моментами. С другой — работать с пользователями. Если дело в скорости — уделять больше внимания объяснению, откуда такие сроки.
Опросы включали и открытые вопросы вида «Что мы можем улучшить?». Надо сказать, коллеги дали нам несколько ценных советов: например, сделать кнопку 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 в неопределенном будущем.
Несколько раз переход на новую версию требовал много рутинной работы: переименование префиксов, изменение импортов иконок и тому подобное. Для таких случаев мы реализовали скрипты миграции.
Выводы
В этой статье описан наш четырехлетний опыт работы над библиотеками общего кода. Какие выводы мы сделали?
- Даже маленькая команда не должна мириться с дублированием кода. Вынос кода в библиотеку — решение, доступное всем.
- Совместное владение общим кодом хорошо работает для небольших команд, до 10 проектов — пользователей библиотеки.
- С ростом числа проектов или вовлеченных разработчиков удобнее выделить команду с фокусом на общий код.
- Важная часть работы выделенной команды — общение с пользователями: демо, обучение, помощь, ведение документации.
- Не надо бояться мыслить шире и инвестировать время в инструменты. В нашем случае это удобная витрина с документацией и анализатор использования компонентов.