Делай как вGoogle
Скачать 5.77 Mb.
|
329 телей, не говоря уже об эффектах закона Хайрама, связывающих нас с конкретной VCS и ее интерфейсом 1 . И это неудивительно: большинство существующих VCS не способны справиться с поддержкой 50 000 инженеров и десятков миллионов фик- саций 2 . Модель DVCS, которая часто (но не всегда) предполагает передачу истории и метаданных, требует большого количества данных, чтобы развернуть репозиторий. Централизация и облачное хранилище для кодовой базы в нашем рабочем процессе не очень хорошо масштабируется. Модель DVCS основана на идее загрузки всей кодовой базы и ее локального использования. Однако с течением времени и по мере роста организации каждый разработчик работает с постоянно уменьшающимся про- центом файлов в репозитории и ограниченным кругом их версий. С ростом коли- чества файлов и инженеров передача всей кодовой базы превращается в напрасную трату ресурсов. Единственная потребность в локальности возникает при сборке, но распределенные (и воспроизводимые) системы сборки (глава 18) лучше подходят и для решения этой задачи, чем DVCS. Источник истины Централизованные VCS (Subversion, CVS, Perforce и др.) включают в свой дизайн понятие источника истины: все, что было последним зафиксировано в главной вет- ви, является текущей версией. Когда разработчик извлекает проект, по умолчанию ему передается именно эта версия из главной ветви. Ваши изменения станут частью «текущей версии», когда вы зафиксируете их поверх прежней текущей версии. В отличие от централизованных VCS, в распределенных системах отсутствует врож- денное понятие единого источника истины. Теоретически можно передавать теги фиксации и запросы на включение изменений (PR, pull request) без централизации или координации, но это способствует беспрепятственному распространению раз- розненных ветвей разработки и, как следствие, риску концептуального возврата к Presentation v5 - final - redlines - Josh’s version v2 . По этой причине применение DVCS требует определения более четких правил и норм, чем применение централизован- ной VCS. В хорошо управляемых проектах, использующих DVCS, одна конкретная ветвь в одном конкретном репозитории объявляется источником истины, что исключает хаотичное появление других ветвей. Мы часто наблюдаем это на практике, в решениях с распределенным размещением на основе DVCS, таких как GitHub или GitLab, — пользователи могут клонировать репозиторий проекта и создавать свои ветви, но 1 Больше всего неприятностей доставляет использование монотонно увеличивающихся но- меров версий вместо хешей фиксации. Многие системы и сценарии были созданы в экоси- стеме разработчиков Google, которые предполагали, что нумерация фиксаций всегда будет совпадать с их очередностью во времени, — отменить такие скрытые зависимости сложно. 2 К слову, на момент публикации статьи «Monorepo» в репозитории хранилось около 86 Тбайт данных и метаданных без учета ветвей с версиями. Создать копию такого репозитория не- посредственно на рабочей станции разработчика было бы... сложновато. 330 Глава 16. Управление версиями и ветвями изменения становятся частью «текущей версии» только после их фиксации в главной ветви первичного репозитория. Нас не удивляет, что централизация и понятие источника истины нашли применение даже в мире DVCS. Чтобы проиллюстрировать, насколько важна идея источника истины, представим, что может случиться при его отсутствии. Сценарий: нет четко определенного источника истины Представьте, что ваша команда решила придерживаться философии DVCS до такой степени, что отказалась определить конкретные ветвь и репозиторий в качестве единого источника истины. В некотором смысле это напоминает модель Presentation v5 - final - redlines - Josh’s version v2 — после извлечения кода из репозитория своего товарища по команде вы не сможете четко определить, какие изменения зафиксированы, а какие — нет. Это неплохо, потому что модель DVCS отслеживает слияние отдельных «заплат» с го- раздо более высокой степенью детализации, чем специальные схемы именования, но одно дело, когда DVCS «знает», какие изменения зафиксированы, и совсем другое, когда каждый инженер ошибочно уверен, что он в курсе всех прошлых и текущих изменений. Подумайте, как в таких условиях обеспечить включение в сборку новой версии всех функций, написанных каждым разработчиком за последние несколько недель. Какие существуют (децентрализованные и масштабируемые) механизмы для этого? Можно ли разработать политику, которая в корне лучше, чем простое соглашение? Не повлечет ли рост команды экспоненциального роста человеческих усилий для поддержания инфраструктуры? Сохранится ли нормальная работа инфраструктуры с увеличением числа разработчиков в команде? Насколько мы можем судить, ответы на все вопросы — нет. Без центрального источника истины кто-то должен будет вести список функций, потенциально готовых к включению в следующую версию. В итоге этот учет будет воспроизводить модель централизованного источника истины. Теперь представьте, что к команде присоединяется новый разработчик. Откуда он получит самую свежую копию кода? DVCS поддерживают множество рабочих процессов и моделей использования. Но если вам нужна система, для управления которой не требуется экспоненциального увеличения человеческих усилий с ростом команды, то вам придется организовать единое хранилище (и одну ветвь), которое фактически будет служить источником истины. В этом понятии источника истины есть некоторая относительность: источником истины для одного и того же проекта в разных организациях могут служить разные репозитории. Это важно: для инженеров Google и RedHat разумно иметь разные ис- точники истины для исправлений ядра Linux, которые, в свою очередь, отличаются от источника истины, который использует сам Линус (создатель ядра Linux). DVCS отлично работает, когда организации и их источники истины имеют иерархическую Управление ветвями 331 структуру (и невидимы за пределами организации), — это, пожалуй, наиболее ценное достоинство модели DVCS. Инженер из RedHat может фиксировать свои наработки в локальном репозитории — источнике истины — и оттуда периодически передавать изменения вверх по иерархии, при этом у Линуса будет совершенно иное представление об источнике истины. До тех пор пока есть определенность в том, куда направлять изменения, вы можете не беспокоиться о проблеме хаотического масштабирования в модели DVCS. Во всех этих размышлениях мы придаем особое значение главной ветви. Конечно, выбор главной ветви в вашей VCS — это необходимость по умолчанию, и кроме этого, организация может устанавливать свои правила. Может так случиться, что ветвь по умолчанию будет оставлена и вся работа фактически сосредоточится в какой-то другой ветви — кроме дополнительной необходимости указывать имя ветви в большом количестве операций, в этом подходе нет ничего принципиально неправильного — просто он нестандартный. В обсуждениях управления версиями предполагается (часто негласно), что применение технологии VCS должно быть согласовано со сводом правил и соглашений в организации. Ни одна тема в сфере управления версиями не содержит больше правил и соглаше- ний, чем обсуждение особенностей использования ветвей и управления ими. Мы рассмотрим управление ветвями более подробно в следующем разделе. Управление версиями и управление зависимостями Обсуждения правил управления версиями и управления зависимостями (глава 21) имеют массу концептуальных сходств. Различия этих видов управления в основном проявляются в двух аспектах: правила использования VCS обычно более детализиро- ваны и в основном затрагивают управление своим кодом, в то время как управление зависимостями требует больше нестандартных решений и касается проектов, управ- ляемых другими организациями. Далее в книге мы подробно обсудим эти аспекты. Управление ветвями Возможность отслеживать различные исправления в VCS открывает множество под- ходов к управлению версиями. В совокупности эти подходы называются управлением ветвями, растущими от главной ветви. Незавершенная работа похожа на ветвь В любом обсуждении правил управления ветвями внутри организации должно учитываться, что всякая незавершенная работа эквивалентна ветви. Это особенно сильно проявляется в DVCS, где разработчики могут совершать множество локальных промежуточных фиксаций, прежде чем передать изменения в источник истины. Это также верно для централизованных VCS: незафиксированные локальные изменения ничем не отличаются от изменений, зафиксированных в отдельной ветви, просто их 332 Глава 16. Управление версиями и ветвями труднее найти и сравнить. Некоторые централизованные системы даже стараются сделать такие ветви более явными. Например, в Perforce каждому изменению при- сваиваются два номера: один определяет неявную точку ветвления, в которой было создано изменение, а другой — точку, в которой оно было зафиксировано (рис. 16.1). Пользователи Perforce могут проверять, у кого есть незавершенные изменения в фай- ле, исследовать ожидающие фиксации изменения других пользователей и делать многое другое. Perforce Git Изменение Правка изменений с номером ожидающей ревизии Повторные синхронизации Время (и фиксации в ствол) Время (и фиксации в главной ветви) Слияние Получение изменений Создание ветви Правка ветви Фиксация, номер окончательной версии Рис. 16.1. Два номера ревизии в Perforce Идея «сходства незавершенной работы с ветвью» особенно актуальна при рефакто- ринге. Представьте, что разработчику говорят: «Переименуй Widget в OldWidget ». В зависимости от политики управления ветвями и понимания, что считается ветвью и какие ветви имеют значение, переименовать Widget можно по-разному: y в главной ветви репозитория, служащего источником истины; y во всех ветвях в репозитории, служащем источником истины; y во всех ветвях в репозитории, служащем источником истины, и найти всех раз- работчиков, имеющих незавершенные изменения в файлах, которые содержат ссылки на Widget Продолжая рассуждения в этом направлении, можно дойти до варианта «переиме- новать везде, даже в незавершенных изменениях», и тогда станет ясно, почему ком- мерческие централизованные VCS, как правило, отслеживают, «у каких инженеров этот файл открыт для редактирования?» (Мы не считаем масштабируемым такой способ выполнения рефакторинга, но относимся к нему с пониманием.) Ветви разработки В эпоху, предшествовавшую распространению юнит-тестирования (глава 11), ког- да любое изменение было сопряжено с немалым риском что-то сломать в другом Управление ветвями 333 месте, имело смысл с особым трепетом относиться к главной ветви. Технический руководитель мог сказать: «Мы не будем фиксировать изменения в главной ветви, пока они не пройдут полный цикл тестирования. Наша команда будет использовать специализированные ветви для разработки». Ветвь разработки (dev branch) — это промежуточная точка между «это сделано, но не зафиксировано» и «это текущая версия, на которой будет основана новая работа». Проблема, которую такие ветви пытаются решить (нестабильность продукта), оче- видна, но мы обнаружили, что она решается лучше более широким использованием тестов, непрерывной интеграцией (глава 23) и качественным набором практик, таких как обзор кода. Мы считаем, что политика управления версиями, широко использующая ветви раз- работки как средство обеспечения стабильности продукта, изначально ошибочна, поскольку те же самые изменения все равно придется фиксировать в главной ветви. Маленькие слияния делаются проще, чем большие. Слияние, выполняемое автором изменений, происходит проще, чем пакетное слияние несвязанных изменений (что в конечном итоге происходит, если команда использует ветвь разработки). Если предварительное тестирование перед слиянием выявит новые проблемы, опреде- лить, чьи изменения являются причиной ошибки, легче, если изменения принад- лежат одному инженеру. Слияние большой ветви разработки предполагает участие в предварительном тестировании большого количества изменений, что затрудняет локализацию сбоев. Выяснить причину проблемы будет сложно, а исправить ее — еще сложнее. Помимо отсутствия опыта слияния ветвей и врожденных проблем этого процесса учитывайте значительные риски потери масштабирования и снижения продуктив- ности инженеров. Когда в течение длительных периодов разрабатывается сразу не- сколько ветвей, координация операций слияния становится более дорогостоящей (и рискованной), чем разработка в главной ветви. Как появилась зависимость от ветвей разработки? Легко увидеть, как организации попадают в эту ловушку: они видят, что «сли- яние этой долгоживущей ветви разработки снижает стабильность», и приходят к выводу, что «слияние ветвей сопряжено с риском». Вместо того чтобы улуч- шить тестирование и отказаться от политики разработки на основе ветвей, они создают новые ветви на основе действующих ветвей. Команды, работающие над долгоживущей ветвью разработки, могут регулярно синхронизировать эту ветвь с главной ветвью разработки. Но с увеличением масштабов организации также растет число ветвей разработки и приходится прикладывать все больше усилий для координации слияния ветвей, которое не масштабируется. Один несчастный инженер начинает координировать сборку, слияния и управление содержимым и становится главным координатором слияния всех разрозненных ветвей в ор- ганизации. На регулярно проводимых встречах делаются попытки «выработать 334 Глава 16. Управление версиями и ветвями стратегию слияния на неделю» 1 . Команды, слияние ветвей которых отложено на будущее, вынуждены повторно синхронизироваться и тестировать свой код после каждого крупного слияния. Все эти усилия по слиянию и повторному тестированию — это чистые накладные расходы. Альтернативный подход требует другой парадигмы: разработки в главной ветви, сильной зависимости от тестирования и непрерывной интеграции, сохранения зеленого индикатора на этапе сборки и отключения незаконченных/непроверен- ных функций во время выполнения. Каждый несет свою долю ответственности за синхронизацию с главной ветвью и фиксацию изменений. Никаких встреч по «по- литике слияния», никаких крупных дорогих слияний, никаких жарких дискуссий о выборе версии библиотеки для использования — есть только одна действующая версия и один источник истины. В конце концов, выпускаться будет только одно исправление: ограничение единственным источником истины — это просто «сдвиг влево» с целью определить, что нужно включить в это исправление. Ветви версий Если период между версиями (срок действия версий) продукта превышает не- сколько часов, разумно создать ветвь версии, которая определяет код, вошедший в сборку версии продукта. Если между текущей и следующей версиями продукта обнаружатся какие-либо критические недостатки, необходимые исправления можно будет добавить из главной ветви в ветвь версии (выполнить минимальное целевое слияние). По сравнению с ветвями разработки, ветви версий обычно безобидны: проблема не в технологии ветвей, а в их использовании. Основное отличие ветвей разработки от ветвей версий — ожидаемое конечное состояние: ожидается, что ветвь разработки снова объединится с главной ветвью и, возможно, будет разветвлена другой командой. Ветвь версии, напротив, останется в фиксированном состоянии. В наиболее эффективных технологических организациях, выявленных компанией Google DevOps Research and Assessment (DORA), ветви версий практически не со- здаются. Компании, организовавшие у себя процесс непрерывного развертывания, позволяющий выпускать обновления из основной ветви несколько раз в день, скорее всего, не будут создавать ветви версий: для них намного проще добавить исправление и выполнить повторное развертывание. Они считают создание ветвей ненужными затратами. Очевидно, ветви версий больше применимы к организациям, которые развертывают продукты в цифровом виде (например, веб-службы и приложения), чем к компаниям, которые продвигают свои продукты в материальном виде. Для последних важно, что именно было предложено клиентам. 1 Недавно проведенный неофициальный опрос в Twitter показывает, что около 25 % инже- неров-программистов участвовали в «регулярных» встречах по определению политики слияния. Управление версиями в Google 335 В том же исследовании DORA отмечается сильная положительная корреляция между «разработкой в главной ветви», «отсутствием долгоживущих ветвей разработки» и хорошими техническими результатами. Суть этих идей кажется очевидной: ветви снижают продуктивность. Во многих случаях мы воспринимаем сложные политики ветвления и слияния как поддержку безопасности — попытку сохранить стабиль- ность главной ветви. Как не раз было показано в этой книге, есть и другие способы достижения этой цели. Управление версиями в Google Подавляющее большинство исходного кода в Google хранится в едином (монолит- ном) репозитории, которым пользуются примерно 50 000 инженеров. Там находятся почти все проекты, принадлежащие Google, за исключением крупных проектов с от- крытым исходным кодом, таких как Chromium и Android. К ним относятся такие общедоступные продукты, как Google Search, Gmail, наши рекламные продукты, Google Cloud Platform, а также инфраструктура для поддержки и развития всех этих продуктов. Мы полагаемся на разработанную у нас централизованную VCS под названием Piper, которая действует как микросервис в нашем продакшене. Она позволяет использо- вать стандартные в Google технологии хранения, связи и вычислений как услуги для организации глобально доступной VCS, в которой хранится более 80 Тбайт данных и метаданных. Каждый день с монолитным репозиторием Piper одновре- менно работают тысячи инженеров. Сотрудники и полуавтоматические процессы, которые используют управление версиями (улучшают что-то, хранящееся в VCS), каждый день выполняют от 60 000 до 70 000 фиксаций. В Piper распространены двоичные артефакты, потому что они не требуют передачи полного репозитория и к ним не применяются обычные затраты. Из-за ориентации на масштабирование с самого начала развития компании Google операции в экосистеме VCS остаются недорогими с точки зрения продуктивности: создание нового клиента в главной ветви, добавление файла и фиксация (непроверенного) изменения занимают всего 15 секунд. Такая малая задержка и хорошо понятное масштабирование значительно упрощают работу инженера. Поскольку Piper — внутренний продукт, у нас есть возможность настраивать его и применять любые политики управления версиями. Например, мы используем дробное владение в монолитном репозитории: на каждом уровне иерархии файлов есть файлы OWNERS с фамилиями инженеров, которым разрешено одобрять изме- нения в поддереве (в дополнение к владельцам, перечисленным в файлах OWNERS на более высоких уровнях дерева). В среде с множеством репозиториев аналогичного владения можно достичь за счет создания отдельных репозиториев, имеющих до- ступ к файловой системе и управляющих возможностью фиксации, или с помощью «ловушек фиксаций» в Git (процедур, запускаемых во время фиксации) для про- верки разрешений. Управляя своей VCS, мы можем сделать владение и одобрение |