8 октября 2021

Блог

Как хранение кода влияет на конкурентоспособность ИТ-продукта

Если не погружаться в детали и не задумываться о гибридных вариантах, хранилища делятся на два типа:

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

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

Как работает монорепозиторий

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

Такой подход используют многие мировые компании, например, Google, Яндекс, Microsoft – последние вообще несколько лет назад отказались от распределенных репозиториев в пользу единого.

Преимущества монорепозитория:

  • Проще управлять зависимостями в коде. Разработчики могут создать общие компоненты, которыми будут пользоваться разные модули продукта. Это помогает контролировать объем пакетов развертывания.
  • Проще находить ошибки и проводить рефакторинг. Тут все достаточно очевидно – привести в порядок одно хранилище можно гораздо быстрее, чем десять.
  • Участники команды больше взаимодействуют между собой. В едином репозитории быстрее вырабатывается общий стиль, появляется больше стимулов к сотрудничеству и совместной работе над продуктом.
  • Новых разработчиков проще погружать в проект. Чем больше репозиториев, тем сложнее понять общую картину и взаимосвязи между модулями продукта.

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

Но самое неприятное – это сложности с CI. Когда несколько человек работают в одном и том же репозитории, процессы поставки будут сильно тормозить из-за постоянных сборок. В самых тяжелых случаях – до полной остановки, как это показывает автор одного из хабрапостов по теме:

«Представьте, что время сборки вашего проекта – 30 минут. Над проектом работает 100 разработчиков, каждый из них пушит по 1 коммиту в день.

Внимание, вопрос: сколько часов должно быть в сутках, чтобы CI-сервер [который позволяет мерджить изменения в мастер только после того, как они были применены к самому свежему коммиту] смог смерджить изменения от всех разработчиков? Правильный ответ, 50».

В результате неправильно настроенный CI заметно повысит стоимость разработки, увеличит Time-to-Market, как следствие – ухудшит конкурентные позиции продукта. Компания будет переплачивать за ненужную работу, поскольку система будет восстанавливать все службы при каждом нажатии.

Мультирепозиторий помогает эти проблемы решить.

Как работает мультирепозиторий

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

Среди сторонников такого подхода – Amazon и Netflix. С недавнего времени мы в True Engineering тоже двигаемся в эту сторону, и вот почему.

Преимущества мультирепозитория:

  • Высокая производительность в крупных проектах. Это главный довод в пользу мультирепозиториев, поэтому мы повторим его еще раз. Уже упомянутые Amazon и Netflix используют распределенный подход, чтобы чаще выпускать обновления, быстрее получать обратную связь от пользователей и в итоге укреплять свои позиции на рынке.
  • Проще тестировать релизы. Не нужно каждый раз проверять, как все работает со всем. Если что-то ломается, изменения очень легко откатить.
  • Проще управлять жизненными циклами микросервисов. По той же причине – чем меньше внутренних взаимосвязей, тем проще вытащить один компонент и добавить другой.
  • Возможность легко переиспользовать компоненты в других системах

В True Engineering нет отдельных команд внутри одного продукта. Мы стремимся построить модель Trunk Based Development, которая предполагает общее владение кодом. В распределенных репозиториях общую логику выделять сложнее. Чтобы разработчики могли переиспользовать код, мы выделяем их компоненты в отдельные библиотеки, которые можно подключать к любым сервисам в любых репозиториях.

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

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

Кроме того, у вас появляется больше версий разных компонентов, ими нужно глобально как-то управлять. Разработчикам становится сложнее понять общую картину, проверки кода могут быть менее эффективными, чем хотелось бы. Несколько компонентов независимо друг от друга могут попасть в релиз, и нам нужно понимать, какие задачи в итоге вошли в сборку. Тут нам приходит на помощь Azure DevOps и наши платформенные настройки над ним. Наш CI/CD версионирует наши приложения и в связанных задачах проставляет тег с именем окружения, на котором развернуто изменение для этой задачи, может проставить версию соответствующего сервиса.

Но обратите внимание, что эти трудности можно победить, если у команд есть единые процессы, если они обмениваются общим опытом – в общем, если у вас есть платформа разработки. Например, в последний год мы в True Engineering активно внедряем шаблоны микросервисов, и мультирепозитории помогают нам поставить их создание на поток. В шаблон уже включены настройки пайплайнов, параметры сборки и развертывания, так что разработчику достаточно прописать бизнес-логику – и в бой.

При этом проблемы с производительностью монорепозитория правильными процессами не победишь. Когда продукт дорастет до определенного уровня, он физически перестанет быстро собираться.

Наш опыт и выводы

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

Разработчикам может быть сложнее работать с несколькими репозиториями, зато бизнес от такого подхода получит значительную пользу. И низкий Time-to-Market, и постоянные релизы, и даже развитый мониторинг системы, который покажет, если после релиза показатели пошли вниз – все это повышает ценность продукта для конечного пользователя.

Поэтому мы в своих проектах делаем выбор в пользу мультирепозиториев, а возможные трудности в командах снижаем за счет единых стандартов и платформенных процессов. Это уже упомянутые общие библиотеки, шаблоны микросервисов и проектов, тесты и т.д.

Однако на данный момент среди наших решений в раздельных репозиториях пока работают всего несколько продуктов. Отчасти это связано с тем, что многие наши системы создавались почти 10 лет назад, когда в мире были совсем другие представления о разработке. Это сейчас релизы собираются скриптами, а тогда только формировалась концепция непрерывной поставки, начинались разговоры про DevOps-методологию. Ведь Docker появился в 2013-м, Kubernetes – в 2014-м.

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

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