29 мая 2019

Монорепозитории: пожалуйста, надо

Блог компании OTUS. Онлайн-образованиеDevOps
Автор оригинала: Adam Jacob

image


Перевод статьи подготовлен для студентов курса «DevOps практики и инструменты» в образовательном проекте OTUS.




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


Почему мы говорим об этом?


Мэтт Кляйн (Matt Klein) написал статью «Monorepos: Please don’t!»  (прим. переводчика: перевод на хабре «Монорепозитории: пожалуйста не надо»). Мне нравится Мэтт, я думаю, что он очень умён, и вы должны прочитать его точку зрения. Первоначально он опубликовал опрос в твиттере:


image


Перевод:
В этот новогодний день я поспорю о том, насколько нелепы монорепозитории. 2019 год начался незаметно. В духе этого я предлагаю вам опрос. Кто большие фанатики? Сторонники:
Монорепозитория
Rust
Неправильный опрос / и те и те


Мой ответ был: «Я буквально оба этих человека». Вместо того чтобы говорить о том, какой Rust наркотик, давайте разберёмся, почему я думаю, что он ошибается насчёт монорепозиториев. Немного о себе. Я технический директор Chef Software. У нас около 100 инженеров, кодовая база, насчитывающая около 11–12 лет, и 4 основных продукта. Часть этого кода находится в полирепозитории (моя стартовая позиция), часть в монорепозитории (моя текущая позиция).


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


Я согласен с первой частью точки зрения Мэтта:


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


Вам предстоит решить одинаковые проблемы вне зависимости от того выберете вы монорепозиторий или полирепозиторий. Как вы выпускаете релизы? Какой у вас подход к обновлениям? Обратная совместимость? Перекрёстные зависимости проектов? Какие архитектурные стили приемлемы? Как вы управляете своей инфраструктурой сборки и тестирования? Список бесконечен. И вы будете решать их все по мере того, как растёте. Бесплатного сыра не бывает.


Я думаю, что аргумент Мэтта похож на взгляды, разделяемые многими инженерами (и менеджерами), которых я уважаю. Это происходит с точки зрения инженера, работающего над компонентом, или команды, работающей над компонентом. Вы слышите такие вещи, как:


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

Безусловно, все эти пункты являются обоснованными. Это происходит в обоих случаях — в полирепозитории у меня есть свой хлам, кроме того, который нужен для сборки… Мне может понадобиться ещё и другой хлам. Поэтому я «просто» создаю инструменты, которые делают чекаут всего проекта. Или я создаю фальшивый монорепозиторий с подмодулями. Мы могли бы ходить весь день вокруг этого. Но я думаю, что аргумент Мэтта пропускает основную причину, которую я довольно сильно перевернул в пользу монорепозитория:


Он провоцирует общение и показывает проблемы


Когда мы разделяем репозитории, мы де-факто создаём проблему координации и прозрачности. Это соответствует тому, как мы думаем о командах (особенно тому, как думают о них отдельные участники): мы несём ответственность за определённый компонент. Мы работаем в относительной изоляции. Границы фиксируются на моей команде и компоненте (-ах), над которым мы работаем.


При усложнении архитектуры одна команда уже не может больше управлять ею в одиночку. Очень немногие инженеры держат всю систему в голове. Допустим, вы управляете общим компонентом A, который используется командами B, C и D. Команда A проводит рефакторинг, улучшает API, а также изменяет внутреннюю реализацию. В результате изменения являются обратно не совместимыми. Какой совет вы дадите?


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

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


На самом деле никто не хочет заниматься этим. Это гораздо менее увлекательно, чем просто исправление чёртового API. Всё это человеческое и запутанное. В полирепозитории вы можете просто внести изменения, дать на ревью тем, кто работает над этим компонентом (вероятно, не B, C или D), и двигаться дальше. Команды B, C и D могут пока просто оставаться на своей текущей версии. Они обновятся, когда осознают вашу гениальность!


В монорепозитории ответственность сдвигается по умолчанию. Команда A меняет свой компонент и, если не проявит осторожности, немедленно ломает B, C и D. Это приводит к тому, что B, C и D появляются у двери A, удивляясь, почему команда A сломала сборку. Это учит A тому, что они не могут пропустить мой список выше. Они должны говорить о том, что они собираются делать. Могут ли B, C и D двигаться? Что, если B и C могут, но D был тесно связан с побочным эффектом поведения старого алгоритма?


Затем мы должны поговорить о том, как мы выйдем из этой ситуации:


  1. Поддержка нескольких внутренних API, при этом старый алгоритм будет помечен устаревшим, пока D не сможет прекратить его использовать.
  2. Поддержка нескольких версий релизов, одна со старым интерфейсом, одна с новым.
  3. Задержка релиза изменений A до тех пор, пока одновременно B, C и D не смогут принять его.

Допустим, мы выбрали 1, несколько API. В этом случае у нас есть два куска кода. Старый и новый. Довольно удобно в некоторых ситуациях. Мы возвращаем старый код обратно, помечаем устаревшим (deprecated) и согласовываем график его удаления с командой D. По существу идентично для поли и для монорепозитория.


Для релиза нескольких версий нам нужна ветка. Теперь у нас есть два компонента — А1 и А2. Команды B и C используют A2, а D использует A1. Нам нужно, чтобы каждый компонент был готов к релизу, потому что, прежде чем D сможет двигаться дальше, могут потребоваться обновления безопасности и исправления других ошибок. В полирепозитории мы можем спрятать это в долгоживущей ветке, которая чувствует себя хорошо. В монорепозитории мы принудительно создаём код в новом модуле. Команде D всё ещё придётся вносить изменения в «старый» компонент. Каждый может увидеть стоимость, которую мы здесь платим — у нас теперь вдвое больше кода, и любые исправления ошибок, которые применяются к A1 и A2, должны применяться для них обоих. С подходом использования веток в полирепозитории это скрыто за cherry-pick. Мы считаем стоимость как меньшую, потому что там нет дублирования. С практической точки зрения, стоимость одинакова: вы будете создавать, выпускать и поддерживать две, в основном идентичные, базы кода до тех пор, пока не сможете удалить одну из них. Разница в том, что у монорепозитория эта боль прямая и она на виду. Это ещё хуже, и это хорошо.


Наконец, мы добрались до третьего пункта. Задержка релиза. Возможно, что изменения, внесённые А, улучшат жизнь команды А. Важно, но не срочно. Можем ли мы просто задержать? В полирепозитории мы подталкиваем это к закреплению артефакта. Конечно, мы говорим об этом команде D. Просто оставайтесь на старой версии, пока не догоните! Это настраивает на игру в труса. Команда A продолжает работать над своим компонентом, игнорируя тот факт, что команда D использует всё более устаревшую версию (это проблема команды D, они глупые). Тем временем команда D говорит плохо о неосторожном отношении команды A к стабильности кода, если они вообще говорят об этом. Проходят месяцы. Наконец, команда D решает взглянуть на возможность обновления, но изменений в A стало только больше. Команда А едва помнит, когда и как они сломали D. Обновление более болезненно и займёт больше времени. Что отправляет его дальше вниз по стеку приоритетов. До того дня, пока у нас не возникнет проблема безопасности в А, что заставляет нас делать ветку. Команда A должна вернуться назад во времени, найти момент, когда D был стабильным, исправить там проблему и сделать его готовым к релизу. Это де-факто выбор, который делают люди, и он, безусловно, худший. Кажется, что это хорошо как для команды A, так и для D, пока мы можем игнорировать друг друга.


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


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


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

Теги:devopscontinuous deliverymonorepootusotus.ru
Хабы: Блог компании OTUS. Онлайн-образование DevOps
+14
4,3k 28
Комментарии 8
Лучшие публикации за сутки