Как стать автором
Обновить

FeatureBranch

Время на прочтение8 мин
Количество просмотров22K
Всего голосов 40: ↑38 и ↓2+36
Комментарии27

Комментарии 27

которую я с вашего позволения назову «нерегулярной интеграцией» (promiscuous integration, PI),

Не «нерегулярная», а «беспорядочная». Как «беспорядочные половые связи», они же промискуитет.
Вы правы, так звучит значительно лучше. Исправляю.
Постоянная интеграция избавляет от проблем с мержами, но хорошо ли в основную ветку сливать незаконченный функционал?
У нас в проекте по этой причине есть 2 дополнительные ветки, помимо trunk: для тестироавния багфиксов, которые нашлись в стабильной версии и для тестирования нового функционала. Вот с ними идет постоянная синхронизация. Но при этом в стабильную версию(trunk) сливаются только исправления.
А из функциональных веток уже создаются билды и потом один из них становится следующей стабильной версией.
Мартин говорит, что он предпочитает просто не включать его, т.е не добавлять обращение к методу, кнопку на экран, пунк в менюшку, открывать WS и т.д. Согласен что всегда может быть факап, но тот нету серебряной пули и надо плясать от различных факторов, среди которых количество разработчиков (100 кодеров фул тайм над одной системой без CI явно до добра не доведут), насколько взаимосвязаны компоненты системы над которой идет разработка (опять же, чем более связаны — тем больше нужда в CI).
В моей прошлой фирме у нас был отдельный брэнч для релиза и перед каждым релизом в него мерджились из транка. Но тут возникала другая проблема, из за того что релиз был вечный народ чинил баги в релизе и не сливал назад в транк. К тому же мерджи из транка иногда делались «пинцетом», чтобы не вытащить то, что не хотели и в результате транк потихоньку терял свою актуальность. Я думаю, что классический, проверенный временем подход — транк для разработки, брэнч на каждый релиз — имеет наилучший баланс между всех зол. Все понимают что через две недели этот брэнч умрет, и поэтому чинят сначала в транке, чтобы не получить по башке за некромансию из старых брэнчей, с другой стороны люди будут писать легко включаемые — выключаемые фичи и это будет способствовать модулярному подходу (только тут есть опасность закоментированного кода в VCS, что выглядит ужасно). В Вашем случае у меня сложилось впечатление что ваш транк стал релиз кандидатом и люди в нем не работали, т.е не использовали как мэйнлайн и в результате не получали плюшки CI. Тут уже вопрос что важнее для каждой фирмы, на эту тему я уже рассуждал в прошлом абзаце :-)
Да, в транке работа практически не ведется, только исправляются ошибки, т.е. поддерживается его стабильность. А в качестве mainline используется ветка feature-testing, которая начинается от транка и используется для совместного тестирвания нового функционала.
Однако мне кажется что держать функционал в mainline и контролировать его использование/неиспользование сложнее, чем просто собирать билды из feature-веток. Особенно если компоненты приложения слабосвязаны. Ну то есть можно наверное даже комбинировать подходы, просто вариант с ветками мне кажется более упорядоченым и очевидным. По крайней мере можно быстро посмотерть какие ветки слиты в билд и соотв. какие функции есть, а каких нет.
Многие используют подход "git flow" для ведения веток в git (перевод на Хабре, правд куда-то пропали картинки). Так, чтобы отделить незаконченный функционал от релиза, в git flow используется ветка develop для кода «в разработке», а в master сливаются уже стабильные версии.
Угу, похожая модель используется у нас. А за ссылочку спасибо)
Как это все напомнило мне времена бурного развития Cisco IOS… Каждое устройство имело с полтора десятка иногда взаимоисключающих фичерпаков, и все это помножено на несколько веток. Фраза «вам следует обновиться до более старой версии» была нарицательной…
При подобной схеме мы получаем:
а) постоянно поломанный trunc из-за влития нестабильного кода
б) постоянно поломанные зелёную и фиолетовую ветки: баги из зелёной бесконтрольно приходят в фиолетовую, а баги фиолетовой — в зелёную
в) нерабочий trunc до тех пор, пока обе feature ветки не будут доделаны и финально влиты в основную ветку

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

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

Первый способ: от trunc/master ответвляется специальная для этих двух людей ветка, от которой они в свою очередь тоже ответвляются и периодически делают в неё мерж своих изменений, а так же периодически делают на неё ребейз, чтобы подхватить изменения соседа. При этом эту специальную ветку ни в коем случае нельзя ребейзить.
Фактически это способ, описанный в статье, но применяемый не к trunc/master, а к отдельной ответвившейся от него ветке.

Второй способ: ответвиться оба могут и от trunc/master, но когда становится ясно, что ведётся работа над пересекающимся функционалом, то зелёная ветка должна ребейзнуться на фиолетовую(либо наоборот). В таком случае фиолетовая ветка может продолжать работать как обычно, не имея никаких проблем, кроме запрета на ребейз своей ветки. А зелёная ветка теперь должна периодически делать ребейз на фиолетовую, правя пересекающийся код и получая чужие баги. Но страдает по этой схеме только один человек, зелёный, а не двое.

Если кто знает способы лучше, пожалуйста, поделитесь.
Вы говорите про CI? Если да, то

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

б) баги сливаются вместе в транк, но там их в разы легче обнаружить, потому что если они живы, то их не нашли тесты и далее их могут найти только QA (в обычной схеме), но в случае CI есть большой шанс что их найдут еще раньше, другие разработчики которые работают с этими модулями. Всяко лучше чем ВНЕЗАПНО за неделю до релиза сломать мэйнлайн нафиг тонной изолированных багов, а потом иди выясняй кто виноват.

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

Я работал в фирме, у которой был (почти, не хватало тестов, но это уже другая проблема, хотя критичная, не спорю) CI, и команда из 80+ разработчиков как раз замечательно уживалась благодаря Jenkins и общему транку.
Вот покрытие транка автоматизированными тестами настолько, чтобы быть уверенным в готовности транка к выкату в продакшн — это и есть святой Грааль continuous delivery (не CI). Прекрасный, но я ни разу не видел его достигнутым.
Под поломанным «транком» и нестабильным кодом я имею в виду даже не ситуации, когда тесты падают, а ситуации когда просто код ещё не дописан до конца, когда либо какая-то логика не доделана, либо или не завершён интерфейс приложения, когда что-то ещё не работает как надо.

Ведь если бы всё работало, и фича была бы завершена и протестирована, то незачем было бы промежуточный мерж делать, можно было бы сразу ветку с фичей мержить, верно? А раз мы мержим какой-то промежуточный код, то он по определению ещё не готов и не стабилен.

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

Зачем весь этот геморрой, если можно, используя возможности гита/меркуриала, избежать его? И вообще, глядя на эту схему, у меня складывается впечателение, что люди, использовавшие ранее svn, затем пересели на гит(меркуриал?), но продолжили работать по старому, не разобравшись с тем, какие преимущества даёт возможность лёгкого бранчинга в связке с ребейзами.
А есть у кого опыт ведения проекта состоящего из нескольких подпроектов. Подпроекты это не просто отдельные компоненты ПО, это самостоятельные проекты на разных платформах/языках но при этом они все завязаны друг на друге в одну систему.
К примеру, несколько мобильных платформ, сервер, БД, web. Интересно услышать об опыте ведения таких проектов в VCS. Какие существует стратегии или методики держать всю эту ораву разношёрстных проектов в одной узде.
А почему вы говорите, что это не отдельные компоненты, а самостоятельные проекты? В чем для вас различие?
возможно плохо написал. Под «самостоятельные» я имел в виду, что они ведутся разными разработчиками под под разные языки и платформы. По сути да, это компоненты одной системы.
И чем это отличается от работы с компонентами одной и той же системы? С точки зрения версионирования и бранчей — уж точно ничем.
Изменения компоненты в одной системе без изменения всех её использующих, скорее всего, приведет к ошибке компиляции, что CI быстро покажет. Изменения в подпроектах к ошибки компиляции в смежных продпроектах не приводят. А выявляются на этапе работы, т.е. грубо говоря, нужна полная функциональная регрессия. Полную функциональную регрессию построить не всегда удается, а у нас и подавно. И того при 4 звенной архитектуре для внедрения одной фичи нужно согласовать до 4 компонент.

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

А еще важный момент, что сейчас все подпроекты лежат в общем для них тнаке. Что опять таки не очень удобно, но и разнести их в разные репы тоже не видится решением проблемы. Так как сейчас у нас как бы есть Мажорная версия проекта, которая лежит к корне транка и она явлется камнем согласования между компонентами, что мол компонента A версии 1.3 не совместима с компонентой B если она уже версии 1.4.
Вот, примерно такая каша и происходит. Было бы интерестно послушать тех кто уже прошел этот путь.
Возникающая у вас проблема связана с кросс-языковой разработкой, а не с feature branching. Решается она банально: заводятся метаданные, описывающие контракт взаимодействия между системами, после чего из этих метаданных первым этапом сборки генерятся файлы, используемые каждой системой. Дальше — стандартные практики CI.
Можно абстрактный пример в вакууме?
Можно реальный пример не в вакууме?
Две системы взаимодействуют через сервис. Описываем сервис в WSDL, на основании него генерим интерфейсы для клиента и сервера (язык не имеет значения), эти интерфейсы реализуем и соответствено потребляем. При любом изменении контракта — меняем WSDL, перегенеряем интерфейсы, перекомпилируем сервер и клиент, смотрим на тесты.
на основании него генерим интерфейсы для клиента и сервера (язык не имеет значения)

Это как?
Ну, я исхожу из того, что для любого языка/платформы можно написать кодогенератор интерфейса из wsdl (если его нет встроенного в платформу).
Написать то можно. А что это по сути изменит? Только если при подключении клиента в первую очередь делать фулл валидацию WSDL (проверку всех интервейсов). Окей, а далее. Сервер идет в БД, WEB смотрит в БД, как их связать c WSDL?
А зачем их связывать с WSDL?

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

У вас связи должны быть попарно между компонентами, и на этих границах вы и придумываете контракты, которые вас страхуют. Везде, где у вас граница — нужно сформировать контракт и условия его проверки.

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

Я вам и рассказываю, как ее решить.
Чувствую какой я профан в многокомпонентных системах :( Опять я пытался «ремонтировать двигатель через выхлопную трубу». С другой стороны, мы сильно ограничены в ресурсах, и потянуть все эту махину это вообще невообразимо в нашем случае.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации