Comments 85
docker-compose up
в корне монорепы и получать целиком рабочий проект. И эту команду выполняет каждый разработчик каждый день и она редко ломается, показывает как проект работает целиком. Монорепа уменьшает страхи сделать ещё один подпроект в виде микросервиса (в большой компании вам нужно пройти все круги ада на одобрение такого и рассказать всей команде, что нужно запускать +1 проект в деве). Единый процесс CI/CD, который крайне просто менять, потому что он один, а не 10+ в разных проектах (infrastructure as code, сейчас код деплоя лежит рядом с кодом приложения).
На другой стороне примеры проектов с десятком репозиториев, которые поддерживаются уже второй-третий год. Чтобы запустить каждый нужно немного магии, немного магии * 10 — это крайне МНОГО магии. Часто граф зависимостей не описан.
В идеальной компании полирепозиторий — хорошо, но такий крайне мало. В стартапе на 3-5 человек 10 репозиториев не нужны.
PS. Мы держим каждый frontend на react отдельно, потому что чисто фронтендщики не хотят заморачиваться или это не позволяет делать windows без боли. А весь бек лежит в монорепе.
ГитБук же: git-scm.com/book/ru/v1/%D0%98%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D1%8B-Git-%D0%9F%D0%BE%D0%B4%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D0%B8
из минусов — надо всегда держать в голове, какие коммиты ты должен сохранить и не ребейзить (обычно это все release/* ветки, но бывают исключения).
Мне они нравятся из-за какой-то своей ODR-ности, что ли.
В один репозиторий 10-15 сабмодулей подключается если что.
Проблема с сабмодули, что они ссылаются на конкретный коммит. Таким образом, держать такой репозиторий в актуальном состоянии довольно трудно. Можно, конечно, автоматизировать, чтобы всегда вытягивать, например, свежие релизные версии… Но не очень понятно, какую проблему это решает. Если хочется запускать полностью всю систему локально, то проще залить артифакты в registry/bintray/куда-то ещё и точно также запустить docker-compose
git-subtree (https://habr.com/post/429014/) по идее должен решать эту проблему, но я его пока не использовал, так что ничего сказать не могу.
Публиковать артефакты — это тормоза, мусорные коммиты с подъёмом версий зависимостей, конфликты в версиях при мёрже веток и разъезжание кода в репе с тем, что опубликовано. В общем, боль и страдания.
сабмодули позволяют грубо говоря всегда иметь точно ту же самую версию кода.
subtree в принципе может быть вариантом, если конкретный репозиторий всегда меняет ровно одна команда и никакая другая (так что конфликты исключены). Иначе — не в ту сторону решенный конфликт является багом замедленного действия (который ты не выявишь diff-ом, ибо дифф-то и должен быть в ситуации правок с нескольких источников).
Я бы сказал, что если у вас есть сильная связь между репозитоиями, вы неправильно определили границы.
Если все же границы определены верно, то не нужно писать этот код вместе — добавьте изменения в первый репозиторий, задеплойте его, потом добавьте во второй — задеплойте его.
В гите нет веток. Есть только ревизии и указатели на ревизии. Субмодуль может указывать на любую ревизию. Даже если после неё нет ревизии, на которую указывает мастер. А git submodule update
не смотря на название просто выкачивает эту самую ревизию в рабочую копию. У гита всё плохо с командами, да.
А чем это отличается от обычного монорепа? Например, тот же svn уже стопицот лет умеет partial checkout, и каждый подкаталог, и даже файл можно выкачать нужной ревизии. Только, в отличие от git'а, выкачивается не вся история, а только нужная ревизия.
Это правда, что в проекте из 2-3 человек не нужно несколько репозиториев. Это просто добавить слишком много накладных расходов. Так что это вполне естественно начинать проект с монорепозитория. Проблемы начинаются тогда, когда у вас 200+ разработчиков, 500+ комитетов в день и билд занимает 40 минут.
Главное не пропустить тот момент, когда код будет настолько связан, что разделить его уже не получится
Это правда, что в проекте из 2-3 человек не нужно несколько репозиториев. Это просто добавить слишком много накладных расходов.
Это зависит не от количества людей, а от сложности проекта.
Одно из преимуществ, которое мне нравится — подмодули заставляют тебя так декомпозировать код, что ты не можешь сделать проекты A и B взаимозависимыми (если всё правильно организовано). А если случайно так получилось, — это требуется исправить, а результат гарантированно можно проверить, просто собрав эти два подмодуля по-отдельности.
Ещё одно преимещуство — это пакетирование. CI/CD автоматически собирает каждый проект, отдельно его тестирует и пакетирует, например, в свой deb- или rpm-пакет под заданные платформы. Но тут сложнее, т. к. требуется автоматизировать зависимую сборку проектов. Зато не требуются такие надстройки как над монорепозиторием.
Ещё одно преимущество — можно делать глобальный рефакторинг не по всему проекту сразу, а частями через подмодули, таким образом распределив задачу по разным людям. При этом нигде ничего не ломается.
И от количества людей тоже (мало людей не смогут нагенерировать поток коммитов и создать проблемный размер для репозитория), так что если 2-3 человека, то основные проблемы монорепозитория не очень проявляют себя.
> Одно из преимуществ, которое мне нравится — подмодули заставляют тебя так декомпозировать код,
Это указано в статье, тоже считаю это преимуществом
> Ещё одно преимещуство — это пакетирование.
вот тут не вижу взаимосвязи с моно- или полирепозиторием, мы в своем монорепозитории собираем отдельные пакеты для каждого микросервиса под разные платформы, работало и на maven, работает и на bazel
> Ещё одно преимущество — можно делать глобальный рефакторинг не по всему проекту сразу, а частями через подмодули
ну прямо скажем, в монорепозитории тоже так можно. Даже более того, (об этом сказано в статье) другого варианта у вас нет даже в монорепозитории из-за неатомарности деплоя.
вот тут не вижу взаимосвязи с моно- или полирепозиторием, мы в своем монорепозитории собираем отдельные пакеты для каждого микросервиса под разные платформы, работало и на maven, работает и на bazel
К сожалению Java хоть и знаю, но в продакшене пока ещё не использую (равно как и монорепозитории, которые с самого начала решил не делать), поэтому мне сложно в этом контексте что-либо оценивать.
Идея, которую я пытался донести в том, что каждый подпроект описывается отдельно и подключается там, где это требуется как подмодуль. При этом сам подпроект обрабатывается Gitlab (CI/CD) как самостоятельный проект, который не знает о существовании чего-либо ещё. При этом для каждого проекта создаётся шаблонный yaml-файл для CI/CD в котором по сути меняется лишь название проекта. А сама сборка осуществляется с помощью ещё одного универсального подмодуля, который подключается к каждому проекту. А уже этот подмодуль берёт из проекта файл конфигурации под каждую платформу для текущего проекта.
В результате любой разработчик может быстро склонировать всего лишь один репозиторий (с рекурсивными зависимостями), перейти на любой коммит и собрать пакет, например, для тестов. Или тесты прогнать, дописав новый. Система сборки в этом случае будет самодостаточной.
Если у вас проект целиком запускается на одной машине, это маленький проект ;)
В моей практике была система, минимальная конфигурация которой содержала 20 виртуалок. Никто даже не пытался запускать ее на локальной машине, т.к. там было несколько инстансов Оракла с кросс-локейшн репликацией
Частично да, но иногда, к сожалению, нужно протестировать интеграционные сценарии. А что того хуже, иногда их нужно ещё и продебажить. Так что хорошо иметь возможность запустить хотя бы часть системы (несколько сервисов, в зависимости от воспроизводимого сценария) локально из IDE в дебаг режиме
Потому что, на большом масштабе монорепозиторий будет решать все те же самые проблемы, которые решает и полирепозиторий, но при этом провоцируя вас к сильной связанности вашего кода и требуя невероятных усилий по увеличению масштабируемости вашей системы контроля версий. Таким образом, в среднесрочной и долгосрочной перспективе монорепозиторий не дает никаких организационных преимуществ, в то время как оставляет лучших инженеров компании с пост-травматическим синдромом (проявляется в виде пускания слюней и бессвязного бормотания о производительности git).
что я подразумеваю под «на большом масштабе»? Нет однозначного ответа на этот вопрос, но т.к. я уверен, что вы спросите меня об этом, давайте скажем, что это около 100 разработчиков, пишущих код фул-тайм.
Исходя из того, что разработчик в каждый момент времени будет иметь доступ только к небольшой порции исходного кода, есть ли хоть какая-то разница между загрузкой части монорепозитория или загрузкой нескольких независимых репозиториев? Разницы нет.
Следующий аргумент, приводимый обычно сторонниками монорепозиториев, заключается в том, что хранение всего кода в одном монорепозитории лишает вас необходимости управлять зависимостями, т.к. весь код собирается в одно и то же время. Это ложь! На большом масштабе просто нет возможности пересобирать весь исходный код и прогонять все автоматизированные тесты каждый раз, когда кто-то коммитит изменения в систему контроля версий
На больших проектах монорепозиторий действительно не нужен. Но на малых проектах, или в малых командах (и особенно если всего 1 разработчик), или вообще в начале проекта — монорепозиторий очень удобен. Если проект-прототип получит развитие, то уже далее, в какой-то момент монорепозиторий надо будет разделить.
Проведу некую аналогию — было множество споров, что лучше — монолит или микросервисы. У каждой из архитектур свои + и -. Я считаю что существует промежуточный вариант — микросервисный монолит, который будет жить в монорепозитории. Он хорош на старте проекта, и если проект «взлетит» и получит развитие, то будет рефакторинг в честные микросервисы и разные репозитории.
Ну в статье и говорится не об одном разработчике, а о проекте с 100+ разработчиками.
Могу сказать по своему опыту, про темы совсем невыдуманные. Особенно в части того, что некоторые компании бездумно копируют практики.
В данный момент у нас 200+ инженеров работают над монорепозиторием. Более 100 коммитов в день, билд на CI занимает около 40 минут. Клонирование репозитория с Гитхаба (учитывая поганый Австралийский интернет) занимает больше 10 минут.
Я уж не говорю про индекс в Идее
Про тесты — у нас в несколько потоков:
— бек интеграционные
— бек юнит
— фронт тесты
— фронт тест билда
Добавили маленькую проверку изменений в папке backend и frontend в CI чтобы зря не гонять тесты.
Все довольны :)
— одна репа на несколько *продуктов*, с разными командами, циклами релизов и т.п;
— в этой же репе лежат все «общие» компоненты, внутренние фреймворки, сервисы;
— но отсутсвие версий у библиотек/компонентов; по сути версия одна — номер коммита;
— нет (ну или почти нет) внешних зависимостей — вместо этого зависимости включаются в саму репу (подход аля Go).
Поэтому, имхо, немного неуместно говорить «а вот в нашем проекте на 3 человека и 5 микросервисом монорепа и все зашибись». Речь все же идет про разные «монорепы».
В статье этого нет, для монорепозитория у нас используется несколько систем контроля версий, которые работают над одним набором данных. На текущий момент это svn, hg и некоторые внутренние разработки.
Оторвано от контеста.
В случае с тем же JS'ом — есть lerna, хочешь монорепку — делай моно, хочешь полирепку — делай поли через git submodules. Погоды особо не делает.
В разных языках — разные пакетные менеджеры, и очень мало жизнеспособного инструментария для монорепок.
Еще есть вопрос по поводу универсальных либ которые удобно один раз написать и потом только использовать. Проблема в том что отладив либу для проекта, для другого почти наверняка придется сделать изменения которые могут сломать первый. Писать либу так чтобы было супер универсально далеко не всегда получается. Возможно в этом случае и полезны монорепозитории.
Забавно, а мы сейчас в команде раздумываем над объединением наших пакетов в одно монорепо. Аргументы у нас такие:
- Пакеты собираются, тестируются и релизятся только вместе
- Вся команда работает над всеми пакетами в равной мере, в конечном итоге все равно придется склонировать все репозитории локально
- Протаскивать изменения сквозь цепочку зависимостей тяжко, а это часто бывает нужно.
Размер команды – 10 человек. Если было бы на порядок больше, то там уже пришлось бы как-то разделять зоны отвественности и разъезжаться по отдельным репозиториям.
Это к вопросу о том, где хранятся исходники Windows
Когда в команде несколько десятков разработчиков, начинается гонка за коммит в head. В линуксе это работает благодаря организационной структуре: там изменения льются в апстрим по дереву доверия, и в каждом узле небольшое код-во разработчиков.
Например prow от Goggle bentheelder.io/posts/prow или сервис mergify.io
Я работал с большими кодовыми базами с использованием обоих подходов.
Монорепы — это на порядок более удобный и продуктивный подход (полирепы вспоминаю как страшный сон), если у компании есть ресурсы для настройки (а возможно, и написания с нуля) инструментов для работы с ними. Facebook и Google выкладывают много полезного для этого кода в открытый доступ (Kythe, Bazel, Hg-experimental), но инвестировать в инфраструктуру придётся много.
Не выйдет просто взять и начать сваливать весь код в одну кучу.
Все они обладают очень сильной привязкой к специфике кодовой базы разработчика. Их просто невероятно сложно начать использовать. Говорю это, как человек, переведший монорепозиторий с мавена на bazel
Говорю следующее как разработчик, ежедневно использующий монорепу с миллиардами строк кода. Начать работать с этой монорепой было очень просто, реализовывать кросс-модульные фичи — это просто сказка, навигация по всему коду напрямую из codesearch — это божественно. Видимо, в вашей монорепе нет правильных инструментов.
Остальным не надо даже пытаться, т.к. правильных инструментов у вас нет, как и ресурсов на их создание.
И какие основные языки/технологии в вашей деятельности?
Списка языков и технологий там нет.
Есть определенная разница, использовать монорепу для бекэнда на плюсах, фронтенда на TypeScript или мобильных приложения для iOS и Android.
Если работает первый случай — это круто, но не интересно. Если второй и третий — это уже интересней.
Тайпскрипт тоже, да.
Пока пользуешься рекомендованным тулингом и зависишь от других внутренних проектов — все в общем даже наверное хорошо.
Интересное начинается когда добираешься до 3p кода (например популярные фреймверки, d3 и прочие antlr'ы).
Интересное начинается когда добираешься до 3p кода
Ну вообще говоря, c 3p не так уж всё и плохо. Как всегда с монорепами, это — проблема тулинга. Написаны инструменты для импорта сторонних зависимостей из разных источников и синхронизации с открытыми репозиториями. Очень многие популярные вещи уже в third_party.
Зато получаем
- codesearch по всем зависимостям
- очередной leftpad вам ничего не сломает
- вместо двух тысяч копий leftpad у вас будет ровно одна
- версии всех зависимостей всегда консистентные (особенно актуально в каком-нибудь Haskell)
То же правило одной (на самом деле двух, для переходных периодов) версии — это палка о двух концах. С одной стороны — да, одна версия, которая всеми и используется. С другой стороны — выходит у пакета новая версия с breaking changes и все, у тебя 100+ проектов зависят от старой версии пакета, в репозитории жесткое правило одной версии, это пат. Надо обновить версию пакета и все зависимые проекты. А этим редко кто-то занимается. В результате в third_party свалка из неактуальных версий зачастую неактуальных пакетов, с которыми ничего толком не сделать. Для многих из них еще специально бекпортят фиксы безопасности, потому что нормально обновить не получается и как результат — что-то непонятное на выходе.
Про то, что логика работы менеджмента зависимостей для фронтенда в принципе плохо ложится на рельсы монорепа (не только из-за вопросов версионирования) я расписывать не буду. По большому счету мне в codesearch не нужен код ВНЕШНИХ зависимостей, мне нужно только описание их API (exports.js + typings), которое там так и так присутсвует.
Монореп может все-таки отдать управление ВНЕШНИМИ зависимостями на откуп стандартным инструментам, это ведь даже не сабмодули, просто код, который вам не принадлежит совсем. Гугл не может, по понятным и объективным причинам (security/privacy/legal), компания поменьше — может вполне.
Я не так давно переводил один фронтенд-проект на 40+ тысяч строк с git/npm/gulp/webpack на эту чудесную монорепу, проклял все. Это еще повезло что он был наполовину на совсместимом наречии написан.
40+ тысяч строк с git/npm/gulp/webpack на эту чудесную монорепу
На какую чудесную монорепу? В чём заключался перевод? Просто переписали билд на bazel/pants/buck?
Перевод проекта с системы сборки X на систему сборки Y, ∀ X, Y — это всегда много скучной, изматывающей, неблагодарной работы.
Я переводил ~500.000 строк кода и двумя десятками приложений на Java с кастомной ANT-сборки на Gradle, утомился. При чём тут монорепы?
Билд, это не самое интересное, это, как верно замечено, просто много скучной и изматывающей работы.
Самое интересное — это 3p зависимости, которые раньше подтягивались через стандартный для внешнего мира пакетный менеджер, а теперь должны быть частью собственно монорепы. В которой очевидное правило одной версии (очень далеко не всегда последней), определенные сложности с оформлением и добавлением пакетов из внешнего мира, очень плохая концептуальная совместимость с миром современного фронтенда в целом и еще разные сугубо внутренние нюансы.
Ну хотя это ведь тоже часть билда, если разбираться.
реализовывать кросс-модульные фичи — это просто сказкаЗнаем-знаем. А потом без пол-литры не разберешь, а где же у этих систем границы. Где собственно зоны отвественности и все такое… Зла нет :)
А потом без пол-литры не разберешь, а где же у этих систем границы. Где собственно зоны отвественности и все такое…
Это самый бредовый аргумент из всей статьи. У нас у каждой большой системы есть свой каталог с OWNERS файлами, в которых написано, кто владеет кодом. В нормальном воркфлоу без аппрува этой команды ничего изменить в их коде не получится. Всегда понятно, где границы, и кто за что отвечает. Сервисы взаимодействуют друг с другом через посылку сообщений поверх gRPC. Сложно представить себе более ясные границы.
Полирепы вас от плохого кода точно не спасут.
И взаимодействие через gRPC тоже не панацея. Оно может быть как в пределах одной системы (разбитой на куски), так и разных. Я же не про разбивку на бинари говорю.
Вполне возможно что ваш опыт более позитивен в этом плане, но лично мой говорит, что сильная связанность (про которую упоминается в этой статье) таки реальная проблема. Ведь разные не только проекты, но и стеки технологий совсем иные.
Связность зависит от культуры разработки, а не от структуры репозитория. Лично я не вижу никаких подтверждений этого аргумента автора статьи.
В полирепе так же просто сделать неявную зависимость между модулями: предположения о формате данных, структуре хранилища и т.п. Я точно также могу утверждать, что полирепы поощрают вас, к примеру, хранить фронт-енд и бэк-енд в разных репозиториях, и протокол между ними не генерить из общего IDL по соседству, а писать руками дважды (счастливой отладки при апдейте схемы).
И вот такое я как раз видел сплошь и рядом.
Я точно также могу утверждать, что полирепы поощрают вас, к примеру, хранить фронт-енд и бэк-енд в разных репозиториях, и протокол между ними не генерить из общего IDL по соседству, а писать руками дважды (счастливой отладки при апдейте схемы).Вот не совсем. Скорее, если культура разработки высокая, то подталкивает нормально этот протокол версионировать. Что позволяет избежать отладки при апдейтах системы :)
Немного персонального опыта.
Работал и с монорепами в команде 8+ человек, работал и с полирепой в средней по размеру команде.
Имхо. Накладные расходы увеличиваются серьезно в полирепе. У нас есть makefile, который умеет инициализировать репозитории, подбрасывать симлинки на реальные инстансы кода вместо сабмодулей и много чего еще для работы. Кроме того это требует наличия метарепозитория (инфраструктурной репы), который умеет собирать все и деплоить в прод по кнопке. Все это требует вдумчивой проработки и внимания на поддержку.
Мое мнение сложилось примерно тем же, что и с микросервисами: это круто и хорошо, об этом надо задумываться, но только тогда, когда проект реально имеет несколько крупных команд поддержки/разработки и проблемы монорепы начинают мешать
Только вчера вечером задался вопросом, а Хабр вчера утром на него даёт ответ )))
Спасибо за перевод, но кажется, что исходное утверждение излишне категорично (или автор сознательно "вбросил"). Просто каждой задаче своё решение.
Монорепозиторий крупных продуктов (100-1000 разработчиков) вполне распространенное явление: ядра Windows и Linux, .NET Core (прадва, их два 2: CLR и FX, но деление относительно естественное), IntelliJ IDEA Community Edition. Chromium сидит на двух стульях (часть компонент типа V8 отдельно, но и оставшийся булыжник весьма большой). Так посмотреть, то из крупных продуктов, тех кто на git сидит (то есть JDK и Firefox на hg выпадают) — большинство сидят в монорепозиториях.
А почему?
- Продукт единый и нет явных причин где-то строить границу (кроме объёма репы). Ну или граница есть, и это распадается на разные продукты.
- Это позволяет перекраивать верхнеуровневую структуру продукта ("одним коммитом"). Причем важно, что не только структуру кода, но и структуру команд!
- Это позволяет вносить связанные изменения одним коммитом. Да, все стремятся делать коммиты как можно компактнее, но они же должны быть еще и "атомарными". Если одно логическое изменение приходится делать в нескольких репозиториях, то обеспечение атомарности потом нужно дополнительно обеспечить (либо в "надрепозитории", либо в сборке и упровлении зависимостями)
Да, за монорепозиторий приходится платить: производительность, строгие правила, сложная структура. Но альтернативы для целостных продуктов могут оказаться существенно дороже.
НО! Если есть возможность раскидать продукт на слабозависимые репозитории, то, конечно, это надо сделать. В корпоративной среде пример — микросервисы, в OSS — Gnome и KDE. В этих случаях обычно зависимости и связи удобнее разрешать в сборке и поставке (не в subrtee/submodule)
Я считаю, что механизм subrtee/submodule придаёт совершенно ненужную хрупкость репозиторию. Лучше задачу управления зависимостями отдавать отдельным сервисам: сборке, CI, CD.
А так вот абстрактно и вообще говорить сразу за всех "пожалуйста не надо". Тем более, что аргументация слабая и есть много скорее удачных контрпримеров.
Монорепозитории: пожалуйста не надо