Monolithic Repos Are Evil

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

Мы все храним наш код в репозиториях системы контроля версий, таких как Git. Вопрос в том, следует ли создавать новый репозиторий для каждого нового модуля или пытаться сохранить как можно больше в одном так называемом “монолитном” репозитории (или просто монорепозитории). Лидеры рынка, такие как Facebook и Google, отстаивают второй подход. Я считаю, что они ошибаются.

Давайте в качестве примера использовать следующую функцию на JavaScript. Она загружает JSON-документ с узла Zold (с использованием jQuery) и размещает часть его содержимого на веб-странице HTML. Затем она окрашивает данные в соответствии с их значением.

Довольно очевидно, не так ли? Просто один файл main.js, который делает все, что нам нужно. Мы просто добавляем его в HTML, и это работает.

Теперь позвольте мне провести рефакторинг. Разобью его на две части. Первая часть будет загружать данные, а вторая - плагин jQuery для раскрашивания содержимого HTML в соответствии с данными. Вот так будет выглядеть плагин:

main.js будет выглядеть так:

Теперь, вместо единого монолитного кода, у нас есть две меньшие части, которые должны быть загружены вместе в целевой HTML.

Два куска лучше, чем один? Кажется, Google, Digital Ocean и Mozilla так не считают.

Чтобы проиллюстрировать свою точку зрения, я извлек функцию JavaScript в новый автономный плагин jQuery. Вот что я сделал:

  • Прочтите инструкции;

  • Провел некоторое исследование плагинов jQuery, изучил несколько примеров;

  • Обнаружил, что большинство из них использовали Gulp, о котором я раньше не слышал;

  • Решил использовать npm для упаковки JavaScript (что еще, верно?).

  • Создан package.json для npm;

  • Переименовал репозиторий GitHub в colorizejs, узнав, что уже существует пакет npm colorize.

  • Настроен .travis.yml для Travis;

  • Создан файл README.md, в котором объяснено, как его использовать и установить.

  • Решили использовать лицензию MIT и создали LICENSE.txt;

  • Настроена PDD для автоматического сбора головоломок;

  • Настроен .rultor.yml для Rultor;

  • Попытался создать модульный тест и потерпел полное фиаско (после часа исследований), поскольку у меня почти нет опыта в модульном тестировании на JS.

  • Опубликовал вопрос на Stack Overflow;

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

  • Ответ от @brian-lives-outdoors был лучшим, и он даже предложил запрос на объединение с модульным тестом, который я включил.

  • Выпущена первая версия 0.0.1 на npmjs.com;

  • Изменен код, чтобы он работал как с классами, так и с цветами.

  • Реализовано и выпущена следующая версия 0.1.0;

  • Добавили его во фронт-энд Zold, протестировали и выпустили—проверьте здесь.

Это заняло почти три недели ожидания и четыре часа работы, чтобы переместить небольшой фрагмент JavaScript-кода в новый репозиторий и выпустить его отдельно. Стоило это того? Я думаю, что да. Но большинство других авторов блогов, которых мне удалось найти, считают, что лучше было бы хранить все в одном монолитном репозитории, в основном из-за повышения продуктивности. Например, Преимущества монорепозиториев от Дэна Лу, Преимущества и недостатки монолитного репозитория (кейс-стади в Google) от Ciera Jaspan и др., и Как монолитный репозиторий в Open Source спас мою лень от Томаса Вотрубы.

Существуют также несколько хороших анализов обоих подходов, например речь Монолитные репозитории против множества репозиториев Фабиена Потенсье на dotScale 2016 и Войны стилей репозитория: Моно против Мульти Питера Сейбеля.

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

Все эти “разумные” аргументы напоминают мне то, что я слышу, когда проповедую декомпозицию объектов и предлагаю использовать несколько объектов вместо одного большого. Представьте себе большой класс из 3 000 строк кода, который делает много разных вещей, и они все очень тесно связаны между собой. Его “легко” протестировать, внести изменения, развернуть, просмотреть и т.д. Потому что все находится в одном файле, верно? Нам не нужно переходить от класса к классу, чтобы понять структуру. Мы просто смотрим на один экран, прокручиваем его вверх и вниз, и все готово. Верно? Абсолютно неправильно!

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

Почему бы нам не применить ту же логику к репозиториям? Я верю, что мы можем. Конечно, так же, как в объектно-ориентированном программировании, детализированный дизайн требует большего опыта и времени. Посмотрите, что я должен был сделать с этим небольшим плагином jQuery. Я провел часы над кодированием и размышлениями. Я даже должен был изучить Gulp и Jasmine, которые, вероятно, больше не буду использовать. Но польза, которую мы получаем от этого, огромна. Вот мой краткий список:

  • Быстрые сборки. Когда репозиторий небольшой, время, затрачиваемое на автоматическую сборку, также невелико. Посмотрите на время, которое занимает Travis в этом сборке моего плагина jQuery. Это 51 секунда. Это быстро. Мы все знаем, что чем быстрее сборка, тем лучше это для производительности, так как сборку легче использовать как инструмент для разработки.

  • Точные метрики. Я не знаю, полагаетесь ли вы на метрики в своих проектах, но мы в Zerocracy обращаем внимание на числа, такие как строки кода, количество вызовов кода, количество коммитов, классы, методы, связность, связывание и т.д. Вопрос всегда заключается в том, насколько точны эти метрики. Подсчет строк кода для большого репозитория не имеет смысла, поскольку число будет включать множество файлов из совершенно разных частей приложения. Более того, в репозитории могут быть файлы на разных языках программирования и в разных форматах. Предположим, у репозитория есть 200 тыс. строк кода на Java, 150 тыс. строк кода в формате XML, 50 тыс. строк кода на JavaScript и 40 тыс. строк кода на Ruby. Можете ли вы сказать что-то конкретное об этом репозитории? Является ли он большим? Является ли он репозиторием Java? И, что более важно, можно ли его сравнить с другими репозиториями? На самом деле нельзя. Это просто большое беспорядочное хранилище файлов.

  • Однородные задачи. У менее крупных репозиториев обычно меньше технологических стеков, что означает, что каждый из них использует лишь несколько языков и фреймворков, или (и это предпочтительная ситуация) — по одному языку или технологии на репозиторий. Благодаря этому управление программистами становится проще, поскольку любую задачу/проблему можно поручить любому. Также легче сделать задачи схожими по размеру и сложности. Это, очевидно, обеспечивает лучшую управляемость проекта.

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

  • Краткие имена. В каждом репозитории, неизбежно, будет свое собственное пространство имен. Например, в только что созданном репозитории JS у меня есть всего два файла: colorizejs.js и test-colorizejs.js. Мне не особо важно, как именовать внутри них, поскольку пространство имен очень маленькое. Я даже могу использовать глобальные переменные. Более короткие имена и меньшие пространства имен означают лучшую поддерживаемость.

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

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

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-15 at 07:06

sixnines availability badge   GitHub stars