Однако разработка на базе основной ветки потребовала создания более эффективного автоматизированного тестирования. Грювер отмечал: «Без автоматического тестирования и непрерывной интеграции это оказывается самым быстрым способом получить большую кучу барахла. Оно никогда не скомпилируется или не будет работать правильно». Вначале цикл полного тестирования вручную требовал шести недель.
Чтобы иметь возможность автоматически протестировать все сборки микропрограмм, значительные средства вложили в эмуляторы принтеров и за шесть недель создали тестовые фермы — в течение нескольких лет две тысячи эмуляторов принтеров запускались на шести стойках с серверами и обрабатывали сборки микропрограмм, взятых из конвейера разработки. Их система непрерывной интеграции (CI) выполняла весь комплекс автоматизированного модульного, приемочного и интеграционного тестирования сборок, сделанных на базе основной ветки, как описано в предыдущей главе. Кроме того, создали культуру производства, останавливающую всю работу, как только кто-то из разработчиков повреждал конвейер развертывания. Так обеспечивался быстрый возврат системы в рабочее состояние.
Автоматизированное тестирование создает быструю обратную связь, позволяющую разработчикам быстро убедиться, что зафиксированный код действительно работает. Модульное тестирование может выполняться на рабочих станциях за минуты, три уровня такого тестирования можно запускать после каждой фиксации кода или каждые два-четыре часа. Завершающий полный регрессионный тест можно запускать каждые двадцать четыре часа. В ходе этого процесса разработчики:
• сокращают количество сборок до одной в день, что в конечном счете означает от десяти до пятнадцати сборок в день;
• переходят от примерно двадцати фиксаций кода в день, совершаемых «главным по сборкам», до свыше ста в день, совершаемых самими разработчиками;
• получают возможность изменять или добавлять до 75–100 тысяч строк кода каждый день;
• снижают длительность регрессионного тестирования с шести недель до одного дня.
Этот уровень производительности невозможно обеспечить до внедрения непрерывной интеграции, когда одно только создание работающей сборки требовало нескольких дней упорного труда. В результате полученные бизнесом преимущества удивляют:
• доля времени разработчиков, затраченного на инновации и написание новых функциональностей, возросла с 5 до 40 %;
• общая стоимость разработки была сокращена примерно на 40 %;
• количество программ, находящихся в разработке, увеличилось примерно на 140 %;
• расходы на разработку одной программы сократились на 78 %.
Опыт, полученный Грювером, показывает: после полномасштабного использования системы контроля версий непрерывная интеграция — один из наиболее важных методов, обеспечивающих быстрый поток работы в нашем потоке создания ценности, позволяя многим командам разработчиков самостоятельно разрабатывать, тестировать и доставлять продукт. Тем не менее непрерывная интеграция остается противоречивым методом. В оставшейся части главы описываются приемы, необходимые для внедрения непрерывной интеграции, а также то, как преодолеть обычно возникающие возражения.
Мелкосерийная разработка, и что происходит, когда мы редко фиксируем код в основной ветке
Как описано в предыдущих главах, когда в систему контроля версий вносятся изменения, нарушающие работу конвейера развертывания, мы быстро набрасываемся на проблему, чтобы ее исправить, в результате чего конвейер развертывания возвращается в «зеленое» состояние. Вместе с тем значительные проблемы появляются, если разработчики трудятся над долго существующими частными ветками (их еще называют «ветки функциональности»). Их обратное слияние с основной веткой выполняется нерегулярно, в результате чего список изменений оказывается весьма велик. Как показано на примере HP LaserJet, это приводит к хаосу и значительным переделкам, требующимся для приведения кода в состояние готовности к релизу.
Джефф Этвуд, основатель сайта Stack Overflow («Переполнение стека») и автор блога Coding Horror («Ужас кодирования»), отмечает, что, в то время как существует множество стратегий ветвления, все они могут быть поделены на две группы.
• Оптимизация для индивидуальной производительности.Каждый участник проекта работает над своей собственной личной веткой. Все работают независимо, и никто не может повредить чужую работу, однако слияние веток становится кошмаром. Совместная работа становится крайне трудной — чтобы увидеть даже мельчайшую часть полной системы, надо работу каждого человека аккуратно объединить с работой остальных.
Читать дальше