Comments 40
Минусы: Размытие ответственности
Хотелось бы понять, какая зона ответственности у конкретного разработчика? Например, один разработчик доработал или что-то исправил в общей библиотеке, и начал прогон тестов. И тут выяснилось, что его изменения что-то сломали в смежных проектах других команд. Кто будет это чинить? Если ответственность на создателе коммита, получается, ему теперь нужно худо бедно разбираться во всех проектах, заехавших в монорепу? Вместо 2kk строк кода поддерживать 20kk?
Допустим, нужно перейти на новую версию Babel. Каждая команда по отдельности разбирается, что там поменялось, что нужно поменять в коде.
Вопрос того же плана. Вот разработчик перенес проект своей команды на новый Babel. А в остальных 9 смежных проектах сборка сломалась. Теперь ему нужно будет тратить еще N времени на миграцию смежных проектов и проталкивании своих пул реквестов. А в это время смежным проектам может быть вообще не до обновлений. В таких условиях переход на новые инструменты может оказаться большой проблемой?
Так же несостоятелен и вариант обратиться за помощью к разработчикам сервисов: у каждой команды есть свой бэклог, свой ритм и свои проблемы которые требуют решения. У них нет мотивации удовлетворять запросы других разработчиков. Особенно, если их проект не получит никаких бонусов. В лучшем случае можно надеяться, что они возьмут задачу в работу через пару-тройку недель. А если таких команд несколько, то придется ждать их всех.
Он может везде починить сам, в сложных случаях — обратиться за помощью к разработчикам этих сервисов. Монорепозиторий подталкивает к унификации всех проектов, поэтому задача разбираться в них всех не выглядит такой уж сложной. Опыт показывает, что суммарное время, которое один разработчик потратит на обновление всех проектов, меньше, чем если бы каждый из них обновлялся по отдельности.
Вы в статье справдливо минусом монорепозиториев указываете размывание ответственности, и тут же в комментариях пишете что один разработчик может править код всех проектов.
Беспрепятственное впиливание сторонним разработчиком нового функционала в любые сервисы, вместо каких-то универсальных точек расширения и обратной совместимости приведёт лишь к тому что всё связано со всем, на уровне приложения и на уровне команд.
Плюс появляются риски сломать своими изменениями чужой продукт, так как ты не имеешь представления покрыт ли этот модуль тестами.
А в случаях ручного тестирования, ты дополнительно увеличиваешь скоуп регрессионного тестирования, следовательно сдвигаются сроки.
И как тогда переходить к деплою по мере готовности.
Совместимость с PyPI — это возможность бесшовно вкорячивать пакеты с PyPI внутрь репозитория? Если да, то способ сделать такую монорепу есть.
Для bazel имеется набор правил https://github.com/bazelbuild/rules_python, в нем есть правило pip_import
импорта внешних пакетов с PyPI. Они скачиваются и становятся доступными как объекты сборки наравне с собственными библиотеками монорепы. После первоначальной настройки вся работа заключается в том, что в корне репы поддерживается файл requirements.txt
, а все бинарники в своих правилах сборки получают возможность указать необходимые зависимости из PyPI. Код импортирует эти зависимости как обычно.
При таком подходе герметичность может страдать, поскольку в bazel пока нельзя задавать контрольные суммы скаченных библиотек, а что там может случиться на pypi.org никто не знает. Эту проблему можно решить поддержкой собственного зеркала PyPI. Даже если отбросить разгерметизацию сборки, зеркало полезно, когда сборка начинает запускаться на десятках машин/сотни раз в день.
Правила сборки py_binary/par_binary
из того же bazel позволяют запаковать код в нечто, похожее на монобинарь, вместе со всеми его зависимостями.
Ну и еще я знаю, что некоторые кладут в их монорепу сам код зависимостей. Про удобство рассуждать не могу, но такое тоже работает.
Если не секрет, сколько строк (или гигабайт) кода в ней?
Не секрет, если взять только *.py файлы и выкинуть контрибы — 1.1M LOC, порядка сотни сервисов. Самих контрибов из PyPI больше 10M LOC. Кроме этого, в репо живет столько же JS кода и на порядок меньше С++/Go. И это, кстати, было гораздо более весомым аргументом в пользу Bazel, чем менеджмент Python и его зависимостей.
Один requirements.txt на всю репу означает, что никакие части вашей кодовой базы не имеет конфликта версий
Это так. Монорепа использовалась с самого начала (2017 год), количество зависимостей в requirements.txt (полный список, через pip freeze) — 318.
Три описанных сценария комментировать не буду, потому что ни один из них в полной мере не подходит. Редкие пакеты действительно могут быть старыми. Наугад потыкал сейчас в наиболее известные пакеты (numpy, flask, scipy, sqlalchemy) — все версии в пределах последних 2-4 месяцев. Обновление пакетов правда занимает больше времени, чем в случае мультирепы из-за необходимости фиксить тесты во всех местах. Из хорошего — в GitLabе есть инструменты для коллаборативных PR, так что в особо запущенные тесты можно позвать более близких к коду людей. Из-за конфликта версий можно подвиснуть и иногда даже приходится лезть во внешние гитхабы для рассылки фиксов. Это тоже проблема, но она еще ни разу не становилась полным блокером.
по-хорошему эти пакеты еще должны проходить security audit (иначе вместе с новой версией можно притащить себе уязвимость)
Здесь риски были приняты, потому что аудит кода — далеко не единственный контур защиты. Откровенная упячка не пройдет ревью, менее откровенная упячка фиксится коллективным разумом, подписанным на security digestы. В общем и целом же компромис сдвинут в сторону более быстрой разработки.
Писать на интерпретируемом языке, а потом ждать сборки бинаря для меня до сих пор выглядит диковато.
Понимаю о чем вы, мне поначалу тоже казалось странным. Со временем перестаешь обращать внимание. Ну либо у меня уже развился стокгольмский синдром:)
Как насчет многократного разбухания очереди тестов на CI? Пока были отдельные репозитории — при коммите в проект гонялись только тесты этого проекта. С монорепозиторием — каждый коммит по-умолчанию будет запускать тесты всех-всех проектов.
Какие билд системы уже так умеют?
Допустим, что конкретно для JS есть вот эта lerna, описываемая в статье. И которая требует, чтобы структура репозитория была перекроена под нее. А что с бэкэндом делать? Отдельный репозиторий? Но ведь это не монорепозиторий тогда.
Bazel умеет, он пересобирает только ту часть графа и перезапускает только те тесты, которые напрямую или транзитивно зависят от файлов, измененных с момента последнего запуска.
Основная идея проста — гоняем только те тесты, которые есть смысл прогонять для данного изменения.
Так что значимое разбухание очереди тестов на CI будет только если каждый коммит меняет все модули всех проектов или общую зависимость всех проектов.
Как это сделано во фронтенде Яндекса — понятия не имею.
В одном из докладов Авито про автоматизацию Android приложения как раз рассказывалось как гонять тесты чаще но при этом быстрее. Вполне сойдет за идею для реализации в другой прикладной сфере.
Если бы мы объединили свои проекты в монорепозиторий, получилось бы чудовище на пару десятков терабайт. Спасибо, нет :) Геймдев, если что.
Какие-то плюсы сомнительные. Ну если для ребят работает, то и флаг им в руки.
Но чем плохи в таком случае сабрепозитории — хоть git, хоть hg, хоть любая другая система — мне осталось не ясным из статьи. Казалось бы: хочешь общие куски кода — подключи их как сабрепозитории и контролируй на какой версии этих кусков сидеть, когда обновляться и т.п.
Так и слава богу что они не похожи на моно. Если весь вопрос в том что не будет новая версия общего кода сразу при пуше попадать во все репозитории которые ее используют, тем самым ломая их код и принуждая фиксить это прямо здесь и сейчас, то такое можно организовать скриптами и это будет намного гуманнее. К примеру автоматически фиксировать новую версию общего кода только когда и если все автотесты прошли успешно для этого конкретного репозитория, а иначе уведомлять разработчиков "ребята, фиксите свой код и сливайтесь с новым общим".
А неконсистентность может быть только на период пока код в репозитории ломается от новой версии общего кода, но это и монорепозиторий не решает.
К примеру автоматически фиксировать новую версию общего кода только когда и если все автотесты прошли успешно для этого конкретного репозитория, а иначе уведомлять разработчиков «ребята, фиксите свой код и сливайтесь с новым общим».
В правильно построенном CI в монорепозитории так и происходит: на каждый PR запускается набор необходимых тестов и зеленый свет на влитие включается только после их успешного прохождения.
А неконсистентность может быть только на период пока код в репозитории ломается от новой версии общего кода, но это и монорепозиторий не решает.
Ровно эту проблему монорепа и решает! Если поддерживать тесты в зеленом состоянии в каждом влитом пулл-реквесте, то в любой момент времени HEAD версия репозитория валидна и готова к бою.
Ну а принуждение к исправлению проблем «здесь и сейчас» (т.е. перед влитием в мастер/транк) автором изменения вместо «потом и неизвестно кем», когда отработает некоторая механика — это и есть один из столпов монорепозиториев. Где-то такой подход считается более удачным. Но давайте не будем об этом спорить:)
Недавно в очередной раз исследовал вопрос монорепы в плане улучшения инструментов и обнаружил, с помощью гугла, что в основном инструментарий заточен под js экосистему. А для многоязычных проектов такое ощущение, что за три года ничего не появилось и лучшим вариантом остаются gut submodule с обвязкой из баша для таких вещей как детектировпние изменений для инкрементальных билдов и тестов.
bazel/pants как билд система
vfsforgit.org для оптимизации операций с git, если разрослись до больших маштабов.
Выглядит как монорепа, ведёт себя как монорепа...
Я о том, что не могу найти нормального тулинга для настоящей монорепы, в которой живут исходники приложений и сервисов на разных языках и платформах, с разными системами управления зависимостей/пакетирования ( yarn/comooser/go mod вот прямо сейчас надо) и т. п., с поддержкой как независимого, так и синхронного бампа версий.
На уровне центральной билд-CI-системы более-менее можно разрулить как раз, а вот для локальной разработки…
go: www.pantsbuild.org/go_readme.html
node: www.pantsbuild.org/node_readme.html
jvm/python там же.
для bazel там еще больше всего должно быть.
Не в боль, а в рутину. Мне кажется тут поможет автоматизация тех процессов, которые вы описали.
Занятно, что тому же make и инкрементальной компиляции — лет 50. Всякие .net и java умеют разбивать проект на подпроекты с пеленок.
А в ноде надо отдельную тулу, которая лазит в гит чтобы понять что менялось, и делает симлинки — чтобы хакнуть историю с node_modules.
Если про фронт я ещё могу понять идею моно репа, то как вы относитесь к монорепу, где лежит и бжу и фронт?
Я вот сейчас в очередной раз (каждые 2-3 года приходится) рисерчу плюсы и минусы монорепы для всей системы.
Явный плюс — гораздо легче вносить одновременно согласованные изменения во фронт и бэк, по крайней мере в общие ветки типа develop и master. Например, изменилось поле в джсон схеме ендпоинта бэка: при раздельных репах для фронта и бэка и наличии e2e тестов в пайплайнах мы заведомо идём на то что для кого-то из них тесты упадут: или бэк уже будет ожидать новую схему, а фронт ещё слать старую, или наоборот. Настройка процесса так, чтобы билд бэкенда, не прошедший тесты потом помечался как успешно прошедший во вребя билда фронта, не тривиальна. Да и от ложноотрицательных оповещениях не спасает. Так же гораздо проще разработчику или тестировщику равзвернуть систему в акутальном состоянии локально.
Явный минус — для бэкендеров появляются "мусорные" коммиты, ветки, прочие вещи от фронтендеров и наоборот. Ну и сама репа тяжелее становится, но нам ещё далеко, до ситуации когда это может начать влиять. Мне пока кажется, что этим можно пренебречь, но продолжаю исследования в сторону поиска каких-то "эмуляторов" монорепы, начиная с git submodules/
В целом склоняюсь к мнению, что монорепа больше проявляет свои плюсы, когда изменения если не всех, то большинство компонентов системы должны разворачиваться одновременно, а плюсы полиреп сильнее проявляются когда циклы разворачивания этих компонентов независимы, ресурсы в версионность и обратную совместимость их API вкладываются by design, а не для того, чтобы обойти минусы организации..
Меня больше всего волнует мусорность коммитов и, как было описано выше, коллизии с пулреквестами. Так же сталкивались со временем сборки, которая в лучшем варианте была полчаса. Что касается зависимости бэка и фронта, то в данном случае придётся тестировать сразу 2 ПР. Мы мокали заранее оговоренный сваггер и пушили в свои ветки. Потому это все поднималось на дев среде в контейнерах и тестилось е2е. Потому все поднималось на проде. П.С. У нас был ньюанс. Было с десяток фронтовых микроаппов и около 20-ти микроаппов бэка, что не так много.
del, misscliked
Разработка в монорепозитории. Доклад Яндекса