Мы работаем над проектами, но мы используем много кода между проектами и имеем много библиотек, которые содержат наш общий код. По мере того, как мы реализуем новые проекты, мы находим больше способов выделить общий код и поместить его в библиотеки. Библиотеки зависят друг от друга, а проекты зависят от библиотек. Каждый проект и все библиотеки, используемые в этом проекте, должны использовать одну и ту же версию всех библиотек, на которые они ссылаются. Если мы выпустим часть программного обеспечения, нам придется исправлять ошибки и, возможно, добавлять новые функции в течение многих лет, а иногда и десятилетий. У нас около дюжины библиотек, изменения часто затрагивают более двух, и несколько команд работают над несколькими проектами параллельно, внося параллельные изменения во все эти библиотеки.
Недавно мы перешли на git и создали репозитории для каждой библиотеки и каждого проекта. Мы используем stash в качестве общего репозитория, делаем новые вещи в функциональных ветках, затем делаем запросы на извлечение и объединяем их только после просмотра.
Многие из проблем, с которыми нам приходится сталкиваться в проектах, требуют от нас внесения изменений в несколько библиотек и специфический код проекта. Они часто включают изменения интерфейсов библиотеки, некоторые из которых несовместимы. (Если вам кажется, что это звучит странно: мы взаимодействуем с оборудованием и скрываем определенное оборудование за общими интерфейсами. Почти каждый раз, когда мы интегрируем оборудование другого производителя, мы сталкиваемся со случаями, которые наши текущие интерфейсы не ожидали, и поэтому должны их усовершенствовать.) Например, представьте проект с P1использованием библиотек L1, L2и L3. L1также использует L2и L3, и L2использует L3также. Граф зависимостей выглядит так:
<-------L1<--+
P1 <----+ ^ |
<-+ | | |
| +--L2 |
| ^ |
| | |
+-----L3---+
Теперь представьте, что функция для этого проекта требует изменений P1и L3изменения интерфейса L3. Теперь добавьте проекты P2и P3в смесь, которая также ссылается на эти библиотеки. Мы не можем позволить себе переключить их на новый интерфейс, выполнить все тесты и развернуть новое программное обеспечение. Так какая альтернатива?
- реализовать новый интерфейс в
L3 - сделать запрос на получение
L3и дождаться обзора - объединить изменения
- создать новый выпуск
L3 - начните работать над этой функцией
P1, сделав ссылку наL3ее новую версию, затем внедрите эту функцию вP1ветку функций - сделайте запрос на удаление, проверьте это и объедините
(Я только что заметил, что забыл переключиться L1и L2на новый релиз. И я даже не знаю, куда это вставить, потому что это должно было бы быть сделано параллельно с P1...)
Это утомительный, подверженный ошибкам и очень длительный процесс для реализации этой функции, он требует независимых проверок (что делает проверку намного сложнее), вообще не масштабируется и, вероятно, выведет нас из бизнеса, потому что мы настолько увязнуть в процессе, что мы ничего не сделаем.
Но как мы используем ветвление и тегирование для создания процесса, который позволяет нам реализовывать новые функции в новых проектах без лишних затрат?