2.6

Смотреть на английском

2.6 Контроль версий и управление исходным кодом

Обзор и мотивация

Воспринимайте контроль версий как систему записи вашей кодовой базы. Он фиксирует каждое изменение, включая кто его сделал, когда и зачем, и позволяет многим людям работать над одной программой, не затирая работу друг друга. Для большой организации это гораздо больше, чем резервная копия. Это фундамент, на котором держатся сотрудничество, непрерывная интеграция (CI), аудит и управление выпусками. Ваш выбор ветвления, структуры репозиториев и дисциплины коммитов определяет, как быстро и насколько безопасно может двигаться ваша команда.

Для больших команд управление исходным кодом по сути задача координации в масштабе. Когда сотни инженеров вливают изменения в общий код, им нужна стратегия, удерживающая слияния небольшими, основную ветку готовой к выпуску, а историю читаемой. Команда, интегрирующаяся непрерывно, течёт гладко. Команда, позволяющая веткам расходиться неделями, перескакивает от одного кризиса интеграции к другому. Структура ваших репозиториев (один большой или много) тоже определяет, как команды делятся кодом и координируются.

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

Ключевые принципы

  • Интегрируйте небольшие изменения часто; долгое расхождение корень боли слияний.
  • Держите основную ветку всегда готовой к выпуску.
  • История это документация; пишите коммиты для будущего читателя, которому нужно понять почему.
  • Никогда не коммитьте секреты; считайте скомпрометированным любой секрет, попавший в историю.
  • Автоматизируйте обеспечение гигиены (хуки, проверки CI), а не полагайтесь лишь на дисциплину.
  • Выбирайте структуру репозиториев (моно или поли) по тому, как команды на самом деле делятся кодом и координируются, а не по моде.
  • Связывайте изменения с их обоснованием (рабочие единицы, тикеты или решения) для прослеживаемости.

Рекомендации

Предпочитайте разработку на основе ствола с недолговечными ветками

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

Выбирайте модель ветвления, подходящую ритму выпусков

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

Решайте «монорепозиторий или полирепозиторий» намеренно

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

Обеспечивайте гигиену коммитов и соглашение о коммитах

Требуйте сообщения коммитов, объясняющие, почему сделано изменение, а не только что. Примите соглашение вроде conventional commits, чтобы сообщения были структурированы и машиночитаемы, что позволяет автоматизировать журналы изменений и версионирование. Держите коммиты атомарными, по одному логическому изменению, чтобы история оставалась пригодной для бисекции и лёгкой для отката. Пусть хуки и проверки CI обеспечивают формат сообщений и базовую гигиену, а не память.

Держите большие бинарные файлы и сгенерированный код вне обычной истории

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

Не допускайте попадания секретов в репозиторий

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

Установите контроль доступа и прослеживаемость

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

Компромиссы: плюсы и минусы

ВыборПлюсыМинусы
Разработка на основе стволаНепрерывная интеграция; малые слияния; высокий потокТребует переключателей функций и дисциплины; меньше изоляции
Долгоживущие ветки функцийСильная изоляция незавершённой работыБолезненные слияния; отложенная интеграция; дрейф
МонорепозиторийАтомарные изменения между проектами; общие инструменты; видимостьНужны масштабированные инструменты сборки; по умолчанию грубый контроль доступа
ПолирепозиторийНезависимые выпуски; изолированный доступ; простые инструменты на репозиторийТрудные изменения между репозиториями; накладные расходы на координацию версий
Conventional commitsАвтоматические журналы изменений и версионирование; согласованная историяПредварительное соглашение; нужно обеспечение

Главный компромисс здесь частота интеграции против изоляции. Долгоживущие ветки кажутся безопаснее, потому что ваша работа лежит отдельно, но именно эта изоляция вызывает дорогие слияния и сюрпризы интеграции позже. Разработка на основе ствола отказывается от этого ощущения изоляции в обмен на непрерывную дешёвую интеграцию и просит вас принести переключатели функций и дисциплину. Решение «моно или поли» меняет лёгкость между проектами на независимость команд. Выбирайте то, что соответствует тому, насколько плотно ваш код на самом деле сцеплен.

Вопросы для обсуждения с командой

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

  2. Стоит ли принятие conventional commits накладных расходов на соглашение для вашей команды с учётом того, что оно автоматизирует? Глава рекомендует структурированные, машиночитаемые сообщения коммитов именно потому, что они позволяют автоматизировать журналы изменений и версионирование, и просит атомарные коммиты, чтобы история оставалась пригодной для бисекции и отката. Компромисс реален: вы платите предварительным соглашением и нуждаетесь в обеспечении в обмен на генерируемые заметки о выпуске и надёжную историю. Принесите сигнал о том, что вы делаете вручную сегодня, например пишете журналы изменений вручную или ищете, какой коммит внёс регрессию. Если вы выпускаете часто или сопровождаете несколько версий, автоматизация обычно окупается; если выпускаете редко, достаточно более лёгкого соглашения. Пусть хуки и CI обеспечивают формат, чтобы не полагаться на память.

  3. Приняли ли вы операционную стоимость, которой требует структура ваших репозиториев: инструменты монорепозитория или межрепозиторную координацию? Эта глава говорит, что и монорепозиторий, и полирепозиторий работают в масштабе, а постоянное трение создаёт неверный для вашей схемы сцепления выбор. Монорепозиторию нужны масштабированные инструменты сборки и более детальный контроль доступа, а полирепозитории превращают любое изменение, охватывающее репозитории, в проект координации с риском рассогласования версий. Принесите конкретный сигнал: как часто ваши изменения пересекают границы проектов и способны ли ваши инструменты сборки и доступа нести имеющуюся структуру. Если атомарные межпроектные изменения часты, вложитесь в инструменты монорепозитория; если команды и сервисы действительно независимы, принимайте стоимость межрепозиторной координации намеренно. Суть в том, чтобы подобрать структуру к тому, насколько плотно ваш код на самом деле сцеплен, затем финансировать инструменты, которых эта структура требует.

  4. Если бы живые учётные данные были закоммичены в оживлённый репозиторий прямо сейчас, как быстро вы бы это обнаружили и является ли ротация автоматической, а не надеждой? Эта глава рассматривает любой секрет, попавший в историю, как скомпрометированный и предупреждает, что удалить его позже трудно и ненадёжно, поэтому профилактика и быстрая ротация единственные реальные защиты. Для большой команды риск нарастает: секрет, отправленный в общий репозиторий, клонируется на десятки машин и зеркалируется в кэши CI за минуты, так что медленная человеческая реакция гарантирует утечку. Противоположное соображение это трение: агрессивное сканирование перед коммитом и принудительная ротация замедляют людей и дают ложные срабатывания, поэтому средства контроля нужно настраивать, а не отключать. Принесите свидетельства: работает ли сканирование секретов и в хуках перед коммитом, и в CI, ваше среднее время обнаружения и ротации известной утечки и есть ли у инженеров вообще система управления секретами, убирающая соблазн вшивать данные в код. В корпоративной и государственной среде привяжите это к вашему процессу инцидентов и правилам хранения, потому что утёкшие учётные данные в проверяемой истории и событие безопасности, и событие соответствия, а регулятор спросит, кто знал и как быстро действовал.

  5. Действительно ли ваши ветки недолговечны, а там, где нет, почему незаконченная работа припаркована на ветке, а не скрыта за переключателем функции? Глава сильно склоняется к разработке на основе ствола, потому что долгое расхождение корень боли слияний, и предлагает переключатели функций как механизм, позволяющий вливать неполную работу безопасно, а не изолировать её на недели. В большой команде это свойство координации, а не личное предпочтение: каждая ветка, живущая неделями, становится частной веткой реальности, которую кто-то должен будет в итоге согласовать, а стоимость этого согласования растёт с численностью. Противоположное соображение в том, что у переключателей функций своя стоимость, включая сложность времени выполнения, тестирование комбинаций и устаревшие переключатели, которые нужно убирать. Принесите данные: реальное распределение времени жизни веток, как часто интеграция порождает конфликты или сюрпризы и сколько долгоживущих веток существует прямо сейчас и почему. Для большой или регулируемой организации добавьте картину выпусков, поскольку подлинная поддержка нескольких версий может оправдать долгоживущие ветки выпусков с дисциплинированным переносом исправлений назад, а это иное решение, чем паркование повседневной работы над функциями вне основной ветки.

  6. Можно ли проследить каждое изменение в вашей истории до его автора и обоснования в пределах правильных границ безопасности и выдержало бы это аудит? Эта глава рассматривает контроль доступа, наименьшие привилегии и связывание изменений с рабочими единицами как часть системы контролей организации, а не необязательный лоск. Для большой команды прослеживаемость превращает непрозрачный поток коммитов во что-то, о чём можно рассуждать во время инцидента или проверки соответствия, а границы доступа не дают одной скомпрометированной учётной записи добраться до кода, которого она не должна касаться. Противоположное соображение это скорость разработчиков: обязательные связи с рабочими единицами, тонкие разрешения и обязательные ревью добавляют церемонию, которую небольшая быстрая команда вполне разумно может пропустить. Принесите свидетельства: требуют ли защищённые ветки заявленных проверок и ревью, ссылаются ли коммиты на самом деле на утверждённые рабочие единицы и как доступ соотносится с вашими реальными границами безопасности сегодня. В корпоративном и государственном контексте свяжите это с классификацией, хранением и обязательствами по аудиту, потому что непроверяемая история или слишком широкий доступ становятся замечанием, способным остановить программу или провалить аккредитацию.

Взгляд по секторам

Стартап. Побеждают скорость и выживание. Используйте один репозиторий, работайте на основе ствола, сливайте недолговечные ветки несколько раз в день и скрывайте незаконченную работу за простыми переключателями функций, а не длинными ветками. Включите сканирование секретов с самого первого коммита, потому что утёкший ключ в публичном репозитории может потопить компанию без команды безопасности, способной сдержать последствия. Пропустите сложные модели ветвления и тяжёлый процесс; защищённая основная ветка и содержательные сообщения коммитов это достаточная дисциплина, чтобы двигаться быстро.

Малый бизнес. Без выделенного платформенного специалиста или DevOps и при ограниченном бюджете покупайте управляемые умолчания, а не стройте их. Хостинговый провайдер Git даёт защиту веток, обязательные ревью и сканирование секретов из коробки, так что опирайтесь на них вместо самостоятельного хостинга сервера, который вы не сможете поддерживать. Формулируйте решение как гигиену данных: знайте, какие репозитории хранят чувствительную конфигурацию, держите учётные данные в менеджере секретов провайдера и позвольте платформе обеспечивать те немногие правила, которые вам действительно нужны.

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

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

Примеры

Стартап. Стартап из трёх человек работает на основе ствола и по привычке, и по необходимости, сливая недолговечные ветки в основную несколько раз в день и скрывая недоделанные функции за простыми переключателями. Они включают сканирование секретов в CI с первого коммита, потому что утёкший ключ API в публичном репозитории мог бы потопить компанию без команды безопасности, способной сдержать последствия. Один репозиторий, защищённая основная ветка и содержательные сообщения коммитов дают им достаточно дисциплины, чтобы двигаться быстро, не спотыкаясь о собственную историю.

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

Государственный сектор. Национальный оборонный подрядчик придерживается строгой прослеживаемости. Каждый коммит должен ссылаться на утверждённую рабочую единицу. Защита веток требует проходящих проверок безопасности и независимого ревью, а доступ жёстко контролируется по границам классификации. Сканирование секретов обязательно, а любые раскрытые учётные данные запускают немедленную ротацию по процессу инцидентов. Долгоживущие ветки выпусков поддерживают несколько развёрнутых версий на площадках, которые не могут обновиться одновременно, с дисциплинированным переносом исправлений безопасности назад.

Бизнес-обоснование: мотивы, ROI и TCO

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

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

Антипаттерны и ловушки

  • Долгоживущие расходящиеся ветки: недели изолированной работы, сливающейся в болезненные рискованные события интеграции.
  • Секреты в истории: вшитые учётные данные, остающиеся в клонах навсегда и требующие ротации после раскрытия.
  • Коммит больших бинарных файлов в основную историю: навсегда раздувает каждый клон и замедляет все операции.
  • Бессмысленные сообщения коммитов: «fix», «wip», «changes», уничтожающие ценность истории как документации.
  • Коммит сгенерированного кода как написанного вручную: шумные diff, конфликты слияния и путаница в том, что источник истины.
  • Неверная структура репозиториев для схемы сцепления: полирепозитории для плотно сцепленного кода или монорепозитории без масштабированных инструментов.
  • Незащищённая основная ветка: нет обязательных проверок, поэтому сломанный или нерецензированный код попадает в ветку, от которой все зависят.

Модель зрелости

  • Уровень 1, Начальный: Стихийно и реактивно. Ветвление импровизируется, ветки живут неделями, сообщения коммитов говорят «fix» или «wip», сканирования секретов нет, а интеграция перескакивает от одного кризиса слияния к другому.
  • Уровень 2, Развитие: Появляются базовые практики, но они различаются по командам. Модель ветвления и соглашения о сообщениях существуют местами, однако ветки всё ещё живут слишком долго, обеспечение частично, сканирование секретов неровное, а структура репозиториев унаследована, а не выбрана.
  • Уровень 3, Стандартизация: Практики задокументированы и соблюдаются по всей организации: разработка на основе ствола с короткими ветками, защищённая и всегда готовая к выпуску основная ветка, обеспечиваемые соглашения о коммитах, сканирование секретов и в хуках, и в CI, доступ по наименьшим привилегиям и намеренный выбор монорепозитория или полирепозитория.
  • Уровень 4, Управление: Практики работы с исходным кодом измеряются и контролируются данными. Вы отслеживаете время жизни веток, частоту интеграции, долю поломок основной ветки, среднее время обнаружения и ротации утёкшего секрета и прослеживаемость изменения до рабочей единицы относительно согласованных базовых значений и действуете при дрейфе чисел, а не ждёте следующего инцидента.
  • Уровень 5, Оркестрация: Практики непрерывно улучшаются и интегрированы по организации. Ветвление, структура репозиториев и инструменты адаптируются по мере изменения команд и сцепления кода, автоматизация обеспечивает гигиену от начала до конца, а данные контроля версий питают решения по поставке, безопасности и рискам по всей организации.

Идеи для обсуждения

  • Действительно ли время жизни веток вашей команды коротко и если нет, что мешает непрерывной интеграции?
  • Соответствует ли ваш выбор монорепозитория или полирепозитория тому, насколько на самом деле сцеплен ваш код?
  • Как вы сегодня обращаетесь с большими бинарными файлами и сгенерированными артефактами и чего это вам стоит?
  • Что случилось бы, если бы живые учётные данные были закоммичены прямо сейчас и как быстро вы бы их обнаружили и ротировали?
  • Сколько дисциплины сообщений коммитов и прослеживаемости стоит обеспечивать в вашем контексте?
  • Как переключатели функций меняют вашу стратегию ветвления и какие новые риски они вносят?

Главные выводы

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

Источники и дополнительное чтение

  • Nicole Forsgren, Jez Humble, Gene Kim, Accelerate: The Science of Lean Software and DevOps
  • Jez Humble and David Farley, Continuous Delivery
  • Scott Chacon and Ben Straub, Pro Git
  • Paul Hammant and others, writings on trunk-based development
  • Conventional Commits specification (as a reference standard)
  • Martin Fowler, articles on branching patterns and continuous integration