Codefreeze и непрерывная поставка

    image Пробки начинаются на перекрестках, когда транспортные потоки вступают в конфликт. Пока один поток движется, другой вынужден ждать. Что-то похожее происходит при выпуске очередного релиза программного продукта: чтобы выпустить продукт достойного качества, приходится останавливать разработку.

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

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

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

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

    Но для программного обеспечения марсохода я бы его рекомендовать не стал.


    Другая крайность — это классический Waterfall, который призывает не смешивать одно с другим. Сначала все спроектировать, потом аккуратно реализовать, внимательно протестировать, исправить ошибки и результат передать заказчику.

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

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

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

    Период стабилизации необходим, он ограничивает количество изменений в коде и делает процесс сходящимся. На практике оказалось, что значительная часть команды в это время скучает: ошибок на всех не хватает. Конечно, всегда есть работа и за пределами итерации, но вносить изменения нельзя — идет стабилизация. А в начале следующей итерации скучают в ожидании новых ошибок тестировщики… Руководство, видя что что работники простаивают, грозится перевести их на другие проекты.

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

    Казалось бы, ситуация безвыходная… Но кто сказал, что изменения нужно вносить туда же, где идет стабилизация?

    Сейчас мы работаем по модели «трех уровней нестабильности»:

    1. Ветка разработки — слабо контролируемая стабильность. Здесь делаются задачи «на будущее», которые не войдут в текущий релиз.
    2. Ветка релиза — контролируемое снижение нестабильности. Набор изменений, который будет реализован в этой ветке, ограничен и довольно жестко зафиксирован.
    3. Стабильная версия — зафиксированное в какой-то момент состояние с допустимым уровнем качества. Изменения вносятся крайне редко и очень небольшие (хотфиксы)

    Работает это так.

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


    Позвольте, скажет кто-то, но тогда в ветке разработки нет актуальных правок. Да и с ошибками неудобно, их приходится править в двух ветках сразу. Чтобы таких проблем не было, мы договорились любую работу в ветке разработки предварять вливанием изменений из релизной ветки. После этой процедуры ветка разработки содержит все актуальные наработки и исправления ошибок.


    Изменения из ветки разработки не попадают в релизную до окончания стабилизации. Это даёт возможность завершить релиз и передать стабильную версию в отдел продаж. А перед началом следующего релиза все наработки попадают в релизную ветку.


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

    Следующая точка приложения усилий — сокращение срока стабилизации для уменьшения разрыва между ветками и сокращения общего времени TimeToMarket.
    Ростелеком-Солар
    Безопасность по имени Солнце

    Comments 8

      +1
      Мне кажется вы придумали GitFlow
        +1
        Почти. Мы начинали работать по GitFlow, а потом немного расширили модель, «развалив» ветку develop на две: dev и release.
            +1
            Да, на первый взгляд одно и то же, различие в деталях. В GitFlow для каждого релиза стартуют отдельную ветку, которая потом сливается с мастером. Использование release branch полезно, когда нужно релизить несколько версий одновременно, но мы предпочли сфокусироваться на коротком цикле выпуска. У нас ветка релиза всегда одна, каждый следующий планируемый релиз просто вливается в неё из dev. Ветки dev & release друг от друга по сути почти не отличаются и больше похожи на develop из GitFlow. А то что в GitFlow называется release branch у нас не прижилось. Ветки обрабатываются сервером сборки абсолютно одинаково, результаты сборки деплоятся на разных стендах. В ветке release ведется разработка также как и в dev, но границы возможных изменений в release жестко ограничены, чтобы процесс был сходящимся. Также существенная разница приоритетах усилий по тестированию и исправлению ошибок — основное внимание уделяется ветке release, dev тестируется и правится по остаточному принципу. Все усилия фокусируются на стабилизации и выпуске релиза.

        0
        Подскажите пожалуйста:
        1. Сколько разработчиков работают одновременно?
        2. Я правильно понимаю, что проект состоит из одного репозитория?
        3. Что если в разработке в данный момент находятся фичи аля Proof Of Concept или разные реализации одной задачи для проверки производительности или еще что то, которые не нужно нести в релиз? Они в каких-то отдельных ветках? Кто и как часто следит за их обновлением? Или такого не бывает?
        4. Как осуществляется поддержка старых версий, работающих у клиентов? Ну например нужно поправить баг в пред-пред-стабильной версии. Небольшой. Или это не релевантно и везде используется только последняя версия?

        Спасибо.
          +1
          1. Одновременно работают 6 разработчиков. Планируем расти.
          2. Да, проект состоит из одного (довольно большого) репозитория. На нескольких организовать управляемый процесс довольно трудно :(
          3. Каждая задача до завершения ведется в отдельной ветке и вливается в один из стволов (dev || release) только после завершения и предварительного тестирования на стенде разработчика. Я не стал пока про это писать, чтобы не усложнять. Таких веток в работе в идеальном случае — по количеству разработчиков, но иногда какие-то ProofOfConcept могут жить подольше. Могут двое работать в одной feature-ветке, например делая согласованные изменения на фронтенде и бэкенде. Вообще, стараемся делать так, чтобы эти ветки не висели долго. Делим задачи так, чтобы они делались за несколько дней и вливались в ствол. Старые ветки периодически чистим.
          4. Для поддержки старых версий есть два варианта. Первый — очень-очень осторожно сделать маленький-маленький хотфикс с последующим Sanity тестированием. Редкий кейс, стараемся избегать. Предпочитаем исправить обнаруженный баг в текущем релизе и обычным порядком выставить обновление.
          0
          правильно понимаю, что у вас 1 релиз в месяц?
            0
            Да, промежуточные релизы у нас выходят примерно раз в месяц

          Only users with full accounts can post comments. Log in, please.