«В одной корзине»: Немного о хранении кода

Эффективное хранение данных интересует абсолютно всех, кто хоть как-то связан с ИТ. Мы в IaaS-провайдере 1cloud постоянно анализируем опыт коллег — совсем недавно мы обсуждали, как хранят свои данные крупные компании.

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

01f06a138f8944aa97c96e5059511c6e.jpg

/ фото Dennis Skley CC

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

Монолитный репозиторий


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

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

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

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

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

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

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

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

Хранение кода в нескольких репозиториях


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

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

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

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

Как хранят код Google и Kiln


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

Доступ к Piper организован с помощью системы Clients in the Cloud (CitC), состоящей из облачного хранилища и файловой системы FUSE для Linux. У каждого разработчика есть рабочая среда, в которой хранятся измененные им файлы. Все записанные файлы хранятся в CitC в виде снэпшотов, что позволяет при необходимости «откатить» работу на несколько этапов назад.

Встроенный в CitC инструмент для поиска кода CodeSearch позволяет вносить мелкие исправления в код, а также передавать измененный код на проверку с возможностью автокоммита: если проверка пройдена, проводится тест, после которого система сама выставляет коммит.

Основу модели монолитного репозитория составляет использование «основной линии» в разработке (trunk-based development). Основная линия представляет собой последнюю версию основного кода, изменения в которую вносятся разово и последовательно. Сразу после коммита новая версия кода доступна всем пользователям Piper, то есть, по сути, у разработчика перед глазами всегда свежая версия кода.

Что касается добавления функционала, то и старый, и новый код существуют параллельно друг другу, а их использование контролируется с помощью конфигурационных флагов. Этот подход позволяет избегать проблем, которые возникают из-за слияния изменений.

Пользователи Stack Overflow советуют хранить код в едином репозитории, даже когда есть возможность разбить его на несколько хранилищ. Для этого существуют такие инструменты, как подмодули в Git, внешние объекты в Subversion и субрепозитории в Mercurial.

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

Кроме того, в Git есть возможность создавать независимые ветви, которые называют сиротскими (orphan). Они не имеют ничего общего друг с другом и сохраняют исключительно свою историю. Так создается новая сиротская ветвь:

git checkout --orphan BRANCHNAME

Каждый отдельный проект можно представить отдельной сиротской веткой. По какой-то причине в Git нужно проводить такую очистку после создания этой ветви:
rm .git/index
rm -r *

Перед очисткой убедитесь, что выставлен соответствующий коммит. После нее веткой можно смело ею пользоваться.
Другой вариант — создать несколько репозиториев и закинуть эти ветки в каждый из них (имена репозиториев не должны совпадать):
# repo 1
git push origin master:master-1
# repo 2
git push origin master:master-2

Другого мнения о хранении кода придерживаются разработчики Kiln, в свое время перешедшие с монолитного репозитория Subversion на мультирепозиторий Mercurial. Их проект разделен на пять частей: exe-клиенты, сервер для взаимодействия клиентов (Reflector), сайт, биллинговая система и библиотека Aadvark.

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

Например, чтобы развернуть новую версию сайта, берутся репозитории website-stable и aadvark-stable. К каждому прикрепляется тег, допустим, Website-000123. Затем запускается процесс сбора билда, который клонирует оба репозитория с сервера в директорию сборки, и выполняет команду hg up –C Website-000123 для переключения локальной копии на нужный тег. После сбора билда производится развертывание.

Заключение


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

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

P.S. Наши материалы о разработке IaaS-провайдера 1cloud:

  • Как создать провайдера виртуальной инфраструктуры
  • Как выбрать направление для развития ИТ-проекта
  • Что нужно знать об IaaS-провайдере до начала работы

P.P. S. Наша новая серия постов о мифах об облачных технологиях:
  • Часть 1: о «бесполезной» техподдержке и «навороченных» сервисах

Комментарии (0)

© Habrahabr.ru