Ежедневно стабильный проект, и релизы по чёткому графику? Легко!

У многих компаний-разработчиков цикл развития программного продукта выглядит так:

– делаем, делаем, делаем (при этом тестировщики особо не в напряге);
– тестируем (при этом производство новых фич стоит, а тестировщики взмылены до предела);
– собираем и выпускаем (тут тестировщки снова почти бездельничают);
– некоторое время ждём багрепортов (начинать что-то новоё и сложное – стрёмно, а то вдруг придётся перевыпускать релиз).

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

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

Начнём с того, что каждый уважающий себя разработчик (и как человек, и как организация) ведёт проекты под системой контроля версий (VCS, version control system) – например, популярные svn или git. Если ваш босс не понимает ценность этой штуки – сегодня же пишите заявление по собственному желанию, и приходите работать ко мне.

Однако VCS, как инструмент, нужно не просто иметь – а правильно использовать. Большинство тупо держит там один каталог, в котором ведётся вся разработка. Перед релизом там “наводится порядок”, затем экспортируются файлы и обновляется production-сервер. Это реализация того цикла, который озвучен в начале поста. Если у вас такая схема на достаточно сложном проекте – выкидывайте её в помойку.

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

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

Но это не круто, потому что у вас есть две постоянные и долговременные ветви проекта. Это не круто, потому что редкие слияния (merge) порождают сильное расхождение веток и конфликты кода при мержинге – я видел людей, которые просто боялись делать мерж. Это не круто, потому что вам опять приходится выполнять задачи строго последовательно, потому что вы не можете бросить что-то на полпути и сделать политически важную фичу вот прямо в этом релизе.

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

Сейчас я сломаю вам мозг одной совершенно киллинг-идеей, которая в своё время сломала мозг мне и заставила смотреть на внутренний мир разработки программного продукта совершенно по-другому. Теперь я привношу её в производство UMI.CMS – которым занимаюсь с 2010 года.

Идея называется feature branches.

Она возникла в сообществе веб-разработчиков под системой контроля версий git. Но я не буду рассказывать про git, так как во-первых он для многих менее привычен, а во-вторых – дело не в уникальной технологии, а в менеджменте проекта.

Идея заключается в том, чтобы на каждую достаточно крупную фичу (feature) выделять отдельную ветку (branch). Да, именно так. Много параллельных веток, в каждой из которых реализуется одна и только одна конкретная функциональность. Когда нужно переключиться на другую задачу – разработчик просто переключается на другую ветку. В современном мире, когда не только виртуальные хосты и базы данных, но и целые виртуальные машины создаются “по кнопке” за пару секунд, переключение среды разработки – это плёвое дело.

Таким образом, ваш проект состоит из множества параллельных веток, в которых своим темпом не спеша развиваются те или иные фичи. Кроме этих веток, есть ещё одна – центральная девелоперская (development branch), в которой исправляются совсем мелкие баги и делаются совсем мелкие задачки, и ещё пара специальных (о них я расскажу позже). А дальше начинается магия:

Когда подходит время к очередному релизу, вы просто выбираете те ветки, где разработка фич “дошла до кондиции”, и мержите их в релиз.

Бинго! У вас волшебным образом появляется стабильный график выхода, причём в каждом релизе вам волшебным образом удаётся вытащить в мир что-то новое. Со стороны это выглядит просто сногсшибательно, ваши коллеги будут долго пытаться разгадать ваш секрет, а конкуренты будут кусать локти.

Естественным образом вы создаёте и изолированную среду для обучения или работы менее квалифицированных сотрудников. Пусть они работают в ветках, которые вы им выдали для решения определённых задач, а вливание в основной продукт будет проводится с соответствующим code-review под контролем опытных коллег. И вам больше никогда не придётся “выпиливать” код безалаберного коллеги, которого вы уволили за регулярные косяки.

Причём у вас теперь в принципе нет “взмыленной” предрелизной гонки, потому что вы вытащили фичи из бранчей, в которых они были в своё время спокойно протестированы. Вам нужно просто смержить нужные feature branches с development branch, и ответвить веточку с номером нового релиза.

Так, стоп, ещё одна ветка? Да. Ветвите больше, и не стесняйтесь. Когда вы слили бранчи, вам нужно вытащить текущее состояние в “release <N> branch” и дополнительно потестить перед выпуском. Смысл вытаскивания состоит в том, что все хотфиксы (и перед выпуском и сразу после – если потребуется) вы будете делать в этой самой релизной ветке. А потом – сливать обратно в development.

Таким образом вы не просто даёте людям в development-ветке спокойно работать по плану, пока вы выпускаете релиз. Клиенты старых версий вашего продукта могут обратиться к вам за исправлением проблем, не желая обновляться до последней версии. В таком случае вы просто переключитесь на старый release branch, пофиксите проблему и отдадите клиенту патч.

Более того, так как каждая фича живёт в своём бранче, вы даже через недели и месяцы можете вернуться к ней и продолжать развивать с той точки, где вы “нажали на паузу”. Влить новую версию фичи в обновлённый продукт – не труднее задачи обычного слияния ветвей.

Именно такой принцип мы реализовали в нашем девелоперском коммьюнити UMI.CMS, в котором каждый проект – это по сути feature branch либо от центрального продукта (самой Юми), либо от чьей-то развивающейся ветки. Любой разработчик может придти в коммьюнити, одной кнопкой ответвить себе бранч (дефолтный набор подкаталогов в репозитории создаётся автоматически) и реализовать там новую фичу. Если его фича окажется достаточно крутой, то мы смержим её в наш продукт.

Более того, если он ответвился от чьей-то разработки (например модуля), то он точно так же может послать request (запрос на включение изменений) её автору. А благодаря тому, что вся структура – единый репозиторий, другие разработчики могут выбирать и вытаскивать те или иные фичи как из оригинальных feature branches, так и из их клонов (которые могут быть и поинтереснее оригинальных).

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