какие действия с ветками потенциально приводит к багам
# Работа с багами
# Описание
Это процесс наполнения, приоритизации и разбора багов. Процесс позволяет сделать работу с багами понятной и прозрачной для команды.
# Почему ветка важна?
Баги в системе – это неправильное поведение системы. Слишком большое число критичных багов приводит к сильной деградации системы и в конечном счёте отражается на продуктовых метриках.
Что процесс даёт для разных ролей. Для лида: Контроль за состоянием системы. По динамике наполнения и разбора багов можно делать выводы как о проблемах в продукте, так и о работе команды разработчиков.
Для продукта и пользователей:
# Что будет, если её не делать?
# На кого может быть делегирована?
На старших QA-специалистов в команде или компании. На Scrum-мастеров, продактов.
# Примеры поведения
# Примеры плохого поведения
# Примеры хорошего поведения
# Способы прокачки
# Практика
У любой системы работы с багами есть несколько составляющих:
Конфигурации могут быть разными и отличаться в зависимости от структуры команд. Например, в случае с матричной структурой компании, за систему сбора багов и наполнение отвечает отдел QA. Разбор может проходить за счёт периодического разбора багов силами разработчиков. Бывают интересные варианты с багодельней
, когда баги разбираются в формате хакатона с элементами геймификации.
# Zero Bug Policy
Основная критика многих стандартных подходов с наполнением и периодическим разбором багов в том, что он всё равно пухнет. Обычно так происходит из-за того, что создание новых фичей видится более перспективным для продактов, чем разбор старых багов. Просто выкидывать баги в силу специфики работы тестировщикам тоже не хочется. В итоге бэклог багов превращается в «антресоль», куда баги попадают, но из которой уже не достаются.
Идея Zero Bug Policy в том, что баги не хранить. Фактически предлагается баги раскладывать на две группы:
Опять же, это не означает, что мы снижаем планку качества. Мы признаем, что в нашем продукте есть сколько-то багов и это нормально.
Критика данного метода основывается на следующем:
Локализация дефектов и оформление баг-репортов
В этой статье мы расскажем о том, что делает QA-специалист, когда он находит тот или иной баг. Также рассмотрим, какие бывают дефекты и с чем их «едят».
Основные термины:
Дефект (или баг) — изъян в компоненте или системе, который может привести компонент или систему к невозможности выполнить требуемую функцию. Дефект, обнаруженный во время выполнения, может привести к отказам компонента или системы. Например: невозможно сохранить данные после заполнения анкеты.
Ошибка — действие человека, которое может привести к неправильному результату. Например: ввод букв в поле для ввода даты (должны приниматься только цифры) с последующим сохранением данных.
Отказ (failure) — отклонение компонента или системы от ожидаемого результата, выполнения, эксплуатации. Например: при переходе на следующую страницу анкеты данные предыдущей страницы затираются.
— не сохраняются изменения данных в профиле;
— не работает добавление комментария;
— не работает удаление товара из корзины;
— не работает поиск.
— текст вылезает за границы поля;
— элемент управления сайтом наслаивается на нижестоящий элемент;
— не отображается картинка.
— можно поставить дату рождения «из будущего», а также несуществующие даты — 31 февраля, 32 декабря и т.п.;
— можно сделать заказ, не указав адрес доставки;
— логика поиска работает неправильно.
— орфографические и пунктуационные ошибки;
— картинка товара не соответствует карточке товара;
— конвертация валют идет по неправильному курсу (калькулятор считает правильно, но при программировании указан неверный курс).
— отсутствие подсветки или уведомления об ошибке при некорректно заполненных полях формы;
— сброс всех данных при ошибке заполнения регистрационной формы;
— перегруженный интерфейс.
— можно получить логин, пароль в результате использования SQL-инъекций;
— неограниченное время жизни сессии после авторизации.
Итак, мы рассмотрели типы и виды дефектов. Теперь расскажем о том, как их документировать.
Документирование ошибок
Отчет об ошибке (Bug Report) — это документ, описывающий ситуацию или последовательность действий, приведшую к некорректной работе объекта тестирования, с указанием причин и ожидаемого результата.
«Следующий этап заключается в документировании ошибок», — могли бы подумать вы, но оказались бы неправы.
Нельзя просто взять и задокументировать найденный дефект. Прежде всего, важно выполнить локализацию.
Перепроверка дефекта
Нужно проверить себя еще раз: воспроизвести баг снова при тех же условиях. Если ошибка не повторилась при очередном тестировании, то нужно разобраться, точно ли были соблюдены все действия и шаги воспроизведения, приведшие к этому результату. Возможно, дефект появляется только при первоначальной загрузке системы (при первом использовании). Для того, чтобы более точно определить условия воспроизведения ошибки, необходимо эту ошибку локализовать.
Локализация дефекта
Чтобы локализовать баг, необходимо собрать максимальное количество информации о его воспроизведении:
Например, не проходит восстановление пароля. Необходимо выявить, откуда приходит запрос на сервер в неверном формате — от backend либо frontend.
Например, в одной из форм, которую редко используют, возникает ошибка при нажатии на кнопку «Редактировать». Если в качестве временного варианта решения проблемы скрыть кнопку, это может повлиять на аналогичную форму в другом окне/вкладке, к которой пользователи обращаются чаще. Для качественного анализа необходимо знать, как работает приложение и какие зависимости могут быть между его частями.
Нужно проверить, соответствует ли результат тестирования ожидаемому результату. Справиться с этой задачей помогает техническое задание (в частности, требования к продукту), где должна быть задокументирована информация о тестируемом ПО и его функционировании. Пропускать этот шаг при тестировании не следует: если специалист недостаточно опытен, не зная требований, он может ошибаться и неправильно понимать, что относится к багам, а что нет. Внимательное изучение документации позволяет избежать таких ошибок.
Необходимо воспроизвести баг в разных операционных системах (iOS, Android, Windows и т.д.) и браузерах (Google Chrome, Mozilla, Internet Explorer и др.). При этом нужно проверить требования к продукту, чтобы выяснить, какие системы должны поддерживаться. Некоторые приложения работают только в определенных ОС или браузерах, поэтому проверять другие варианты не нужно.
Например, desktop-приложение предназначено для использования на компьютерах, поэтому зачастую нет необходимости тестировать его на мобильных устройствах. Для смартфонов в идеале должна быть разработана отдельная мобильная версия, которую, в свою очередь, нет смысла тестировать на компьютерах. Кроме того, есть web-приложения, которые могут работать и на компьютерах, и на мобильных устройствах, а тестировать их нужно на всех типах устройств. Для тестирования можно использовать эмулятор той или иной среды, но в рамках статьи мы не будем затрагивать этот вопрос.
Мобильную версию приложения нужно тестировать на нескольких устройствах с разной диагональю экрана. При этом можно руководствоваться требованиями к ПО, в которых должно быть указано, с какими устройствами это ПО совместимо.
Для того, чтобы не запутаться в реализованных задачах, в разработке используют версионность ПО. Иногда тот или иной баг воспроизводится в одной версии продукта, но не воспроизводится в другой. Этот атрибут обязательно необходимо указывать в баг-репорте, чтобы программист понимал, в какой ветке нужно искать проблему.
Возможно, дефект был найден при нехватке внутренней или оперативной памяти устройства. В таком случае баг может воспроизводиться на идентичных устройствах по-разному.
Для того, чтобы оптимизировать сроки тестирования, мы рекомендуем использовать техники тест-дизайна.
Фиксирование доказательств
Доказательства воспроизведения бага нужно фиксировать при помощи логов, скринов или записи экрана.
Live-логирование – это снятие системных логов в режиме реального времени. Для этого можно использовать следующие программы: Fiddler, Visual Studio для Windows, iTools, Xcode для iOS, Android Debug Monitor, Android Studio для Android и др.
Оформление баг-репорта
Все найденные дефекты обязательно нужно документировать, чтобы каждый задействованный на проекте специалист мог получить инструкции по воспроизведению обнаруженного дефекта и понять, насколько это критично. Если в команде принято устно передавать разработчику информацию о найденных дефектах, есть риск упустить что-то из вида.
Дефект, который не задокументирован – не найден!
Когда вся необходимая информация собрана, а баг локализован, можно приступать к оформлению баг-репорта в таск-трекере. Чем точнее описание бага, тем меньше времени нужно для его исправления. Список атрибутов для каждого проекта индивидуален, но некоторые из них – например, шаги воспроизведения, ожидаемый результат, фактический результат – присутствуют практически всегда.
Баг должен быть описан кратко и ёмко, иметь понятное название. Это поможет разработчику разобраться в сути ошибки и в том, может ли он взять этот случай в работу, если занимается соответствующим разделом системы. Также это позволяет упростить подключение новых специалистов на проект, особенно если разработка ведется много лет подряд, а запоминать баги и отслеживать их в таск-трекере становится все сложнее. Название проекта можно составлять по принципу «Где? Что? Когда?» или «Что? Где? Когда?», в зависимости от внутренних правил команды.
Например:
Где происходит? — В карточке клиента (в каком разделе системы).
Что именно происходит? — Не сохраняются данные.
Когда происходит? — При сохранении изменений.
В какой части функциональности тестируемого продукта найден баг.
Версия продукта, ветка разработки, в которой воспроизводится ошибка.
Этот атрибут показывает влияние дефекта на функциональность системы, например:
· Blocker — дефект, блокирующий использование системы.
· Critical — ошибка, которая нарушает основную бизнес-логику работы системы.
· Major — ошибка, которая нарушает работу определенной функции, но не всей системы.
· Minor — незначительная ошибка, не нарушающая бизнес-логику приложения, например, ошибка пользовательского интерфейса.
· Trivial — малозаметная, неочевидная ошибка. Это может быть опечатка, неправильная иконка и т.п.
Приоритет определяет, насколько срочно нужно исправить ошибку. Обычно выделяют следующие виды приоритетов:
Статус указывает стадию жизненного цикла бага, взят он в работу или уже решен. Примеры: to do, in progress, in testing (on QA), done. В зависимости от особенностей проекта возможны дополнительные статусы (например, аналитика).
Баг-репорт отправляют тимлиду проекта или разработчику, который будет заниматься исправлением дефекта, в зависимости от принятых в команде договоренностей.
Где найден баг: операционная система, наименование и версия браузера.
Необходимо для описания действий, которые предшествовали воспроизведению бага. Например, клиент авторизован в системе, создана заявка с параметрами ABC и т.д. Баг-репорт может не содержать предусловие, но иногда оно бывает необходимо для того, чтобы проще описать шаги воспроизведения.
Один из самых важных атрибутов — описание шагов, которые привели к нахождению бага. Оптимально использовать 5-7 понятных и кратких шагов для описания бага, например:
1. Открыть (. )
2. Кликнуть (…)
3. Ввести в поле А значение N1
4. Ввести в поле B значение N2
5. Кликнуть кнопку «Calculate»
Что произошло после воспроизведения указанных выше шагов.
Что должно произойти после воспроизведения шагов тестирования, согласно требованиям.
Логи, скриншоты, видеозапись экрана — всё, что поможет разработчику понять суть ошибки и исправить ее.
После составления баг-репорта обязательно нужно проверить его, чтобы избежать ошибок или опечаток.
Локализация и оформление багов — необходимые составляющие работы QA-специалиста с программным продуктом. Приглашаем подробнее ознакомиться с услугами тестирования и обеспечения качества в SimbirSoft.
Noveo
Подходы к управлению ветками в системах контроля версий (часть 3)
Третья часть статьи Мартина Фаулера посвящена различным способам осуществления релиза в продакшн.
Путь от mainline до релиза в прод
Mainline — это активная ветка, с регулярно появляющимися фрагментами нового и измененного кода. Поддержание ее в рабочем состоянии важно для того, чтобы люди, начиная работу над новой задачей, начинали ее со стабильной базы. Если код достаточно стабилен (прим.: Фаулер называет такой код «здоровым»), вы также можете релизить код непосредственно с mainline в прод.
Эта философия поддержания mainline в постоянно доступном состоянии является центральным принципом Continuous Delivery, которая требует решимости и навыков поддержания mainline в «здоровом» состоянии, как правило, с помощью Deployment Pipelines для поддержки необходимого интенсивного тестирования.
Команды, работающие таким образом, обычно могут отслеживать свои релизы, используя теги на каждой выпущенной версии. Но командам, не практикующим непрерывное развертывание, нужен другой подход.
Релизная ветка
В эту ветку допускаются только те коммиты, которые стабилизируют готовую к релизу версию.
Типичная релизная ветка будет копироваться с текущей mainline, но не позволит добавлять в неё новые фичи. Основная команда разработчиков продолжает добавлять фичи в mainline, и они будут включены в следующий релиз. А разработчики, занимающиеся релизом, ориентированы исключительно на устранение дефектов, которые мешают релизу быть готовым к выпуску на прод. Все исправления этих дефектов делаются в релизной ветке и мержатся с mainline. Когда все дефекты устранены, ветка готова к релизу в прод.
Хотя объем работ по фиксам в релизной ветке (будем надеяться) меньше, чем по новой функциональности, с течением времени становится всё сложнее их слить обратно в mainline. Ветки неизбежно расходятся, и чем больше коммитов изменяют mainline, тем труднее смержить с ней релизную ветку.
Проблема применения коммитов к релизной ветке, таким образом, заключается в том, что слишком легко пренебречь их копированием в mainline, особенно когда это становится все труднее из-за расхождений. В результате появляется регрессия, за которую очень стыдно. Как следствие, некоторые люди предпочитают создавать коммиты на mainline, и только убедившись, что там они работают, выборочно копировать их в релизную ветку.
Выборочное копирование (cherry-pick) — это когда коммит копируется из одной ветки в другую, но ветки не сливаются. То есть копируется только один коммит, а не все предыдущие коммиты, начиная с точки ответвления. В этом примере, если бы я мержил F1 с релизной веткой, то включил бы M4 и M5. Но при выборочном копировании я взял бы только F1. Выборочное копирование не всегда может быть корректно применено к релизной ветке, так как может основываться на изменениях, сделанных в M4 и M5.
Недостаток написания релизных фиксов на mainline в том, что для многих команд это сложнее, к тому же не вдохновляет фиксать что-то на mainline и затем делать то же самое на релизной ветке, прежде чем можно будет релизить. Это особенно актуально, когда релиз должен выйти в сжатые сроки.
Командам с одной версией на проде понадобится только одна релизная ветка, но некоторые продукты будут использовать на проде много релизов. ПО, которым владеет заказчик, будет обновляться только по его желанию. Многие клиенты, обжегшись на неудачных обновлениях, неохотно идут на апгрейд, если не нужно добавлять мощные новые фичи. Такие клиенты, однако, все равно хотят исправлять баги, особенно если они связаны с проблемами безопасности. В подобной ситуации команда разработчиков держит релизные ветки открытыми для каждого используемого релиза и применяет к ним исправления по мере необходимости.
Поскольку разработка продолжается, становится все труднее применять фиксы к более старым релизам, но это входит в издержки ведения бизнеса. Можно только минимизировать их, поощряя клиентов к частым обновлениям до последней версии. Для этого важно поддерживать стабильность продукта — если какое-то обновление пройдет неудачно, клиент не захочет это повторять.
(Другие термины, которые я слышал для релизной ветки, включают в себя такие, как «ветка для подготовки релизов» («release-preparation branch»), «ветка стабилизации» («stabilization branch»), «ветка кандидата» («candidate branch») и «защитная ветка» («hardening branch»). Но «релизная ветка», похоже, самая распространенная).
Когда это использовать
Релизные ветки являются ценным инструментом, когда команда не в состоянии поддерживать свою mainline в здоровом состоянии. Это позволяет части команды сосредоточиться на исправлении багов, которые необходимо устранить до выпуска в продакшн. Тестировщики могут работать с самой стабильной последней версией из конца ветки. Каждый может увидеть, что сделано для стабилизации продукта.
Однако при всей своей ценности релизные ветки не используются в сильных командах для продуктов с одной продакшн-версией, поскольку в этом нет необходимости. Если основная ветка поддерживается достаточно здоровой, то любой коммит на mainline может выйти в релиз напрямую. В этом случае у релизов должны быть теги с общедоступной версией и номером сборки.
Возможно, вы обратили внимание на неуклюжее выражение «одна продакшн-версия» в предыдущем абзаце. Оно использовано потому, что этот паттерн становится критичным, когда командам нужно управлять несколькими версиями на проде.
Релизные ветки также могут быть удобными, когда процесс релиза организован через замедляющие факторы — например, релизный комитет, который должен дать аппрув на все релизы в прод. Как говорит Крис Олдвуд (Chris Oldwood), «в этих случаях релизная ветка выступает скорее как карантинная зона, пока медленно вращаются корпоративные шестеренки». Вообще нужно стараться по максимум избавляться от факторов, замедляющих релиз, так же как и устранять проблемы при интеграции. Однако в некоторых обстоятельствах, например, если речь о магазинах мобильных приложений, это может оказаться невозможным. Во многих из этих случаев часто достаточно тега, и ветка открывается только в том случае, если требуется какое-либо существенное изменение в исходном коде.
Релизная ветка может также быть и веткой окружения («Environment Branch»), что влечет за собой все опасения, связанные с использованием этой схемы. Возможен и вариант долгоживущей релизной ветки, который я вскоре вкратце опишу.
Maturity Branch («Ветка готовности»)
Ветка, последний коммит которой указывает на последнюю версию уровня зрелости кодовой базы.
Команды часто хотят знать, какая версия исходного кода самая актуальная; это может быть сложно, если код в системе разного уровня зрелости. Тестировщик может захотеть взглянуть на последнюю версию продукта в работе, а кто-то, кто дебажит сбой в проде, захочет взглянуть на последнюю production-версию.
Maturity branches предоставляют возможность отслеживать это. Идея в том, что как только версия кодовой базы достигает определенного уровня готовности, она копируется в отдельную ветку.
Рассмотрим maturity branche для проды. Когда мы готовим релиз на продакшн, мы открываем релизную ветку для стабилизации продукта. Как только релиз готов, мы копируем его в долгосрочную ветку для продакшн. Я думаю об этом скорее как о копировании, а не о мерже, поскольку мы хотим, чтобы production-код был точно таким же, как тот, что был протестирован в отслеживаемых ветках («upstream branches»).
Одно из преимуществ Maturity branche заключается в том, что она четко показывает каждую версию кода, которая достигает этой стадии в рабочем процессе релиза. Поэтому в приведённом выше примере нам нужен только один коммит в production-ветке, который объединяет коммиты M1-3 и F1-2. Нужно будет немного пошаманить с SCM, чтобы сделать это, но в любом случае это ослабляет зависимость от мелкомодульных коммитов на mainline. Эти коммиты должны быть записаны в сообщении о коммите, чтобы помочь людям отследить их позже.
Maturity branches обычно называются в честь соответствующей стадии в процессе разработки. Отсюда и такие термины, как «production-ветка», «staging-ветка» и «ветка QA». Иногда я слышал, что люди называют production maturity branch «релизной веткой».
Когда это использовать
Системы управления исходным кодом поддерживают совместную работу и отслеживание истории кодовой базы. Использование maturity branch позволяет получить важные кусочки информации, показывая историю версий отдельных этапов в процессе работы над релизом.
Я могу найти последнюю версию, например, действующий production-код, взглянув на последний коммит соответствующей ветки. Если появится баг, которого, я уверен, не было раньше, я смогу посмотреть, какие предыдущие версии находятся в этой ветке, и увидеть конкретные изменения в кодовой базе проды.
В изменения конкретных веток можно интегрировать автоматизацию — например, автоматизированный процесс может деплоить версию в прод каждый раз, когда на production-ветку делается коммит.
Как альтернативу maturity branches можно использовать схему тегирования. Как только версия готова для тестирования, ей можно присвоить соответствующий тег — обычно он включает в себя и номер билда. Таким образом, когда билд 762 готов для тестирования, он может получить тег «qa-762», а когда он готов к выпуску в прод, то получает «prod-762». Поискав в репозитории кода теги, соответствующие нашей схеме, мы можем увидеть историю. Аналогичным образом к присвоению тегов может быть прикручена и автоматизация.
Итак, maturity branch может сделать рабочий процесс более удобным, но многие компании считают, что и теги работают превосходно. Поэтому я смотрю на это как на один из тех паттернов, которые не имеют ни ярко выраженных преимуществ, ни недостатков. Часто, однако, необходимость использования системы управления исходным кодом для такого отслеживания является признаком бедного инструментария Deployment Pipeline команды.
Вариация: долгоживущая релизная ветка
Я могу думать об этом как о разновидности паттерна релизной ветки, которая сочетает его с maturity branch для кандидата в релиз. Когда мы хотим сделать релиз, мы копируем mainline в эту релизную ветку. Как и в случае с пре-релизными ветками, коммиты делаются в релизную ветку только для улучшения стабильности. Эти фиксы также мержатся с mainline. Когда это происходит, мы вешаем тег на релиз и можем снова скопировать mainline, когда хотим сделать другой релиз.
Коммиты могут быть скопированы, что более типично для maturity branches, или смержены. При мерже мы должны внимательно следить, чтобы последний коммит релизной ветки точно совпадал с последним коммитом mainline. Один из способов сделать это — отменить все фиксы, которые были применены к mainline перед мержем. Некоторые команды также добавляют коммиты после слияния, чтобы убедиться, что каждый коммит представляет собой полноценный кандидат в релиз. (У людей, которые считают это сложным, есть веские причины предпочитать новую ветку для каждого релиза).
Такой подход применим только для продуктов с одним выпуском в продакшн за раз.
Одна из причин, по которой командам нравится этот подход, заключается в том, что при нем последний коммит релизной ветки всегда указывает на следующего релиз-кандидата, и не приходится последнюю релизную ветку, чтобы найти на ней последний коммит. Однако, по крайней мере, в git’е мы достигаем того же эффекта с помощью слова «релиз» в имени ветки, которое переходит к следующей вместе с hard reset, когда команда создает новую релизную ветку, оставляя тег на старой.
Ветка для окружения
Сконфигурируйте возможность запустить продукт в новом окружении, сделав коммит в исходный код.
Софт, как правило, запускается в различных средах, таких как рабочая машина разработчика, продакшн-сервер и, возможно, разные среды тестирования и staging’а. Обычно запуск в этих различных средах требует внесения некоторых изменений в конфигурацию, таких как URL, используемый для доступа к базе данных, расположение системы обмена сообщениями и URL для ключевых ресурсов.
Ветка для окружения — это ветка с коммитами, которые применяются к исходному коду для перенастройки продукта для работы в другой среде. Например, у нас есть версия 2.4, работающая на mainline, и теперь мы хотим запустить её на нашем staging-сервере. Мы делаем это, создавая новую ветку, начиная с версии 2.4, применяем соответствующие изменения окружения, пересобираем продукт и деплоим его в staging-окружение.
Изменения обычно вносятся вручную, хотя, если ответственные за это люди не возражают против git’а, они могут выбрать (cherry pick) изменения из более ранней ветки.
Паттерн ветки для окружения часто сочетается с Maturity Branch. Долгоживущая QA maturity branch может включать в себя настройки конфигурации для QA-окружения. Мержи в эту ветку будут подхватывать изменения в конфигурации. Аналогично долгоживущая релизная ветка может включать в себя эти изменения настроек.
Когда это использовать
Ветка для окружения — это привлекательный подход. Она позволяет нам настроить приложение таким образом, чтобы оно было готово к работе в любой новой среде. Мы можем держать эти изменения в diff’е, который можем избирательно включать в будущие версии продукта. Это, однако, классический пример антипаттерна — то, что выглядит привлекательно сначала, но вскоре приводит вас в мир страданий, драконов и коронавирусов.
В любом изменении окружения кроется опасность, что поведение приложения изменится при его перемещении из одной среды в другую. Если мы не можем взять версию, запущенную в прод, и отладить ее на рабочей машине разработчика, это значительно усложняет решение проблем. Мы можем внедрить баги, которые проявляются только в определенных средах, и что самое опасное — на проде. Из-за этой опасности мы хотим быть уверены, что, насколько это возможно, один и тот же код будет запущен как на проде, так и в других средах.
Проблема с ветками для окружения заключается в той самой гибкости, которая делает их такими привлекательными. Так как мы можем изменить любой аспект кода в этих diff’ах, мы легко можем добавить патчи с настройками, которые приводят к различному поведению и сопутствующим этому багам.
В результате многие организации мудро настаивают на железном правиле: после компиляции файла именно эта версия должна запускаться во всех окружениях. Если требуются изменения в конфигурации, они должны быть изолированы с помощью таких механизмов, как явные конфигурационные файлы или переменные окружения. Таким образом они могут быть сведены к простой установке констант, которые не будут изменяться во время выполнения, оставляя меньше возможностей для появления багов.
Простое разграничение между исполняемым файлом и конфигурацией может легко размыться, если софт исполняет свой исходник напрямую (например, JavaScript, Python, Ruby), но здесь применимы те же самые принципы. Старайтесь менять окружение маленькими шажками и не используйте ветвление исходного кода для их применения. Общее правило заключается в том, что вы должны иметь возможность проверять любую версию продукта и запускать его в любом окружении, так что всё, что изменяется исключительно из-за различных сред деплоймента, не должно быть в системах контроля версий. По поводу хранения комбинаций дефолтных параметров в системах контроля версий ведутся споры, но каждая версия приложения должна иметь возможность переключаться между этими различными конфигурациями по мере необходимости, основываясь на динамическом факторе, а именно — на переменных окружения.
Ветки для окружения являются примером использования ветвления исходного кода как модульной архитектуры бедного человека. Если приложение должно работать в разных средах, возможность переключения между разными средами должна входить в пакет первого класса дизайна приложения. Для приложения, у которого дизайн только второго класса, ветки окружения могут быть полезны как средство для выживания, сделанное на скорую рук, но как только это будет возможно, их следует удалить, заменив более надежной альтернативой.
Ветка для хотфикса
Ветка для работы над исправлением критичной ошибки на проде.
Если на проде появляется серьезный баг, его необходимо исправить как можно скорее. Работа над ним будет иметь более высокий приоритет, чем любая другая работа команды, и никакая другая работа не должна замедлять работу над этим исправлением.
Работа по хотфиксам должна быть выполнена в системе контроля версий, чтобы команда могла правильно их записать и работать над ними вместе. Это можно сделать, открыв ветку в последней релизной версии и применив любые изменения для исправлений в этой ветке.
Как только фикс будет применен на проде и у каждого будет возможность хорошенько выспаться, хотфикс можно будет применить к mainline, чтобы не допустить регресса со следующей версией. Если для следующей версии открыта релизная ветка, хотфикс должен быть применен и к ней тоже. Если время между релизами долгое, то, скорее всего, хотфикс будет сделан поверх изменённого кода, а значит, будет более проблемным для мержа. В этом случае очень полезны хорошие тесты, которые выявляют ошибку.
Если команда использует релизные ветки, то можно сделать хотфикс там, а новый релиз — по завершении работы над ним. По сути, это превращает старую релизную ветку в ветку для хотфикса.
Как и в релизных ветках, можно делать хотфиксы на mainline и выборочно забирать их в ветку для релизов. Но это довольно редкий случай, поскольку хотфиксы обычно делаются в очень сжатые сроки.
Если команда делает непрерывное развертывание (Continuous Delivery), она может релизить хотфиксы непосредственно из mainline. И хотя разработчики могут использовать ветку для хотфиксов, начинать они будут скорее с последнего коммита вообще, чем с последнего, выпущенного в релиз.
Я повесил на новый релиз тег 2.2.1, так как если команда работает таким образом, то, скорее всего, M4 и M5 не содержат новых фич. Если содержат, то хотфикс, наиболее вероятно, просто войдет в релиз 2.3. Это, конечно, иллюстрирует, что при Continuous Delivery хотфиксам нет необходимости обходить нормальный процесс релиза. Если в команде налажен достаточно эффективный процесс релиза, хотфикс может быть обработан так же, как и обычный релиз — и это является существенным преимуществом мышления Continuous Delivery.
Командам Continuous Delivery присущ специфический подход не допускать до mainline никаких коммитов до тех пор, пока хотфикс не будет завершен. Это согласуется с мантрой, что ни у кого нет более важной задачи, чем исправление mainline — и на самом деле это относится к любым дефектам, найденным на ней, даже к тем, которые ещё не отправлены в прод (так что я полагаю, что этот подход не очень-то специфичен).
Когда это использовать
Хотфиксы, как правило, делаются в достаточно стрессовой обстановке, а чем больше стресс, тем больше у команды шанс допустить ошибку. В таких условиях еще более ценным, чем обычно, является использование систем контроля версий и совершение коммитов чаще, чем это кажется разумным. Продолжение этой работы на ветке позволяет всем знать, что делается для решения проблемы. Единственным исключением может быть простой фикс, который может быть применен напрямую к mainline.
Более интересный вопрос здесь — решить, что является критичным багом и подлежит хотфиксу, а что может быть оставлено в обычном рабочем процессе разработки. Чем чаще команда выпускает релизы, тем больше ошибок на проде она может обрабатывать в обычном ритме разработки. В большинстве случаев решение будет зависеть в первую очередь от влияния бага на бизнес и от того, как это вписывается в частоту релизов команды.
Релиз-поезд (Release Train)
Выпускайте релизы с заданным интервалом времени, как поезда, отправляющиеся по регулярному расписанию. Закончив работу над своей фичей, разработчики сами выберут, на какой поезд сесть.
Команда, использующая релиз-поезда, будет устанавливать периодичность релизов, например, каждые две недели или каждые шесть месяцев. Даты устанавливаются на то время, когда команда будет создавать ветку для каждого релиза, следуя метафоре расписания поездов. Люди решают, на какой поезд хотят посадить свою фичу, и нацеливаются в работе на этот поезд, помещая свою коммит на соответствующую ветку во время загрузки поезда. Когда поезд отправляется, эта ветка становится релизной и будет принимать только фиксы.
Команда, использующая ежемесячные поезда, начнет ветку в марте, основываясь на февральском релизе. В течение месяца они будут добавлять новые фичи. В установленную дату, возможно, в третью среду месяца, поезд отправляется — и добавление новых фич в эту ветку становится невозможным. Они запускают новую ветку для апрельского поезда и новые фичи добавляют на нее. Тем временем кто-то из разработчиков стабилизирует мартовский поезд, выпуская его в прод, когда он будет готов. Любые исправления, применяемые к мартовскому поезду, переносятся и на апрельский поезд.
Релиз-поезда обычно используются вместе с ветвлением по фичам. Когда Алла почувствует, что скоро допилит свою фичу, она решит, на какой поезд садиться. Если она думает, что успеет до мартовского релиза, она интегрируется в мартовский поезд, но если нет, она подождет следующего и интегрируется там.
Некоторые команды используют мягкую заморозку за несколько дней до отправления поезда (которое само по себе является жесткой заморозкой). Когда релиз-поезд переведен в состояние мягкой заморозки, разработчикам не следует выгружать туда плоды своих трудов, если только они не уверены, что их фичи стабильны и готовы к релизу. Любая фича, где будет найдена ошибка, будет скорее возвращена (вытолкнута из поезда), а не исправлена прямо в поезде, если она добавлена после мягкой заморозки.
В наши дни, когда люди слышат «релиз-поезд», то часто это в контексте концепции Agile Release Train от SAFe. Agile Release Train от SAFe — это организационная структура команды, рассчитанная на крупномасштабную команду из нескольких групп, у которых общее расписание поездов для релизов. Но, хотя здесь и используется схема релиз-поезда, она не совпадает с той, которую я описываю здесь.
Когда это использовать
Центральным понятием паттерна релиз-поезда является регулярность релизов. Если вы заранее знаете, когда должен отправиться релиз-поезд, вы можете планировать свою работу над фичами так, чтобы успеть к конкретному поезду. Если вы думаете, что не закончите работу к мартовскому поезду, вы знаете, что сядете на следующий.
Релиз-поезда особенно полезны, когда есть факторы, существенно замедляющие процесс релиза, — например, внешняя группа тестирования, проверяющая релиз пару недель, или релизный совет, который должен прийти к соглашению для того, чтобы новая версия продукта могла появиться. В таких случаях часто более мудрым будет попытаться избавиться от этих факторов и сделать возможными более частые релизы. Конечно, бывают ситуации, когда это совершенно невозможно, например, процесс верификации, используемый магазинами приложений на мобильных устройствах. Тогда подстройка релиз-поездов под такие факторы может сделать ситуацию оптимальной.
Механизм релиз-поезда помогает сконцентрировать внимание каждого на том, в какой момент должны появляться какие фичи, и таким образом помогает предсказать, когда они будут завершены.
Явным недостатком такого подхода является то, что фичи, завершенные в начале отправного периода поезда, будут сидеть в поезде и читать книгу в ожидании отправления. Если это крупные фичи, то это означает, что продукт упускает важные возможности в течение нескольких недель или месяцев.
Релиз-поезд может стать значимым этапом в улучшении процесса релиза, используемого командой. Если команда испытывает трудности с выпуском стабильных релизов, то прыжок сразу в Continuous Delivery может быть слишком сложным. Выбор подходящего периода выпуска релизов, который трудно, но возможно соблюдать, может стать хорошим первым шагом. По мере того, как команда прокачивает свои скиллы, она может увеличить частоту отправки поездов, в конечном счете отказываясь от них в пользу Continuous Delivery, когда дорастет до этого.
Вариация: загрузка будущих поездов
В базовом примере поезда с фичами на платформу прибывает новый поезд, который забирает фичи одновременно с отъездом предыдущего. Но другой подход заключается в том, чтобы фичи одновременно принимали несколько поездов. Если Алла думает, что не закончит свою фичу к мартовскому поезду, она все равно может запушить свою почти доделанную фичу на апрельский поезд и затем добавлять коммиты, чтобы закончить ее до отъезда.
Через регулярные промежутки времени мы выгружаем коммиты из мартовского поезда в апрельский. Некоторые команды предпочитают делать это только тогда, когда мартовский поезд отправляется, чтобы делать только один мерж, но те из нас, кто знает, что небольшие мержи существенно проще, предпочли бы выгружать каждый мартовский коммит при первой возможности.
Загрузка будущего поезда позволяет разработчикам, работающим над апрельскими фичами, участвовать в работе, не мешая работе в мартовском поезде. Недостатком является то, что если апрельская команда вносит изменения, конфликтующие с работой, выполненной в марте, мартовские рабочие не получают обратной связи, что усложняет будущие мержи.
В сравнении с регулярными релизами из mainline
Одно из главных преимуществ релиз-поезда — регулярный выпуск релизов в прод. Но наличие нескольких ветвей для новых разработок усложняет процесс. Если наша цель — регулярные релизы, то мы можем достичь этого также и с помощью mainline. Решите, каков график выпуска релизов, а затем создавайте релизную ветку по этому графику из того, что находится в последнем коммите mainline.
Если mainline готова к релизу (Release-Ready Mainline), то в релизной ветке нет необходимости. При регулярных релизах, подобных этому, у разработчиков есть возможность придержать почти готовую фичу для следующего релиза, просто не заливая ее на mainline, если до даты следующего релиза осталось совсем мало времени. При Continuous Integration разработчики всегда могут отложить реализацию графического интерфейса для фичи или держать ее неактивной, если хотят, чтобы фича ждала следующего запланированного релиза.
Mainline, готовая к релизу (Release-Ready Mainline)
Поддерживайте mainline достаточно здоровой, чтобы ее последний коммит всегда мог быть запущен непосредственно в прод.
Когда я начинал этот раздел, я сказал, что если вы сделаете mainline здоровой веткой с надежной проверкой должного уровня кода, то вы сможете выпускать релизы непосредственно из mainline, отмечая релиз тегом, когда захотите.
Я уже достаточно описал альтернативные этому простому механизму паттерны, поэтому думаю, что пришло время осветить и сам этот механизм, потому что, если команда может это сделать, то это отличный выбор.
Тот факт, что каждый коммит, сделанный на mainline, может идти в релиз, еще не означает, что так и должно случиться. Это тонкое различие между Continuous Delivery и Continuous Deployment. Команда, использующая Continuous Deployment, релизит каждое изменение, принятое на mainline, но в случае с Continuous Delivery каждое изменение готово к релизу, а вот выпускать его или нет — это уже бизнес-решение (таким образом, Continuous Deployment представляет собой подвид непрерывной доставки). Мы можем рассматривать Continuous Delivery как возможность делать релизы в любое время, а наше решение о реализации этой опции зависит от других факторов.
Когда это использовать
В сочетании с Continuous Integration как частью Continuous Delivery готовая к релизу mainline является общей особенностью высокопроизводительных команд. Учитывая это, а также мой хорошо известный энтузиазм по поводу Continuous Delivery, вы, наверное, ожидаете от меня слов о том, что готовая к релизу mainline — это всегда лучший выбор по сравнению с альтернативами, о которых я рассказывал в этом разделе.
Однако паттерны всегда связаны с контекстом. Паттерн, превосходный в одном контексте, может оказаться ловушкой в другом. Эффективность готовой к релизу mainline зависит от частоты интеграции команды. Если команда использует ветвление по фичам и интегрирует новую фичу, как правило, только раз в месяц, то команда, скорее всего, не в самом лучшем состоянии, и работа по модели готовой к релизу mainline может стать препятствием на пути их развития. Не лучшее состояние — это состояние, в котором они не могут реагировать на меняющиеся потребности продукта, потому что время цикла от идеи до производства слишком велико. Кроме того, у них, скорее всего, будут сложные мержи и проверки, так как каждая фича — крупная, что приводит к многочисленным конфликтам. Они могут проявляться во время интеграции или быть постоянной проблемой для разработчиков, когда они копируют mainline в свои ветки для фич. Такие сложности препятствуют рефакторингу, что снижает модульность, а это, в свою очередь, еще усугубляет проблему.
Ключом к выходу из этой ловушки является более частая интеграция, но во многих случаях этого может быть трудно достичь, сохраняя при этом готовую к выпуску mainline. В таком случае часто лучше отказаться от готовой к выпуску mainline, поддерживать более частую интеграцию и использовать релизную ветку для стабилизации mainline для продакшн. Со временем, конечно, мы будем надеяться устранить необходимость релизной ветки, улучшив deployment pipeline.
В контексте частых интеграций готовая к выпуску mainline имеет очевидное преимущество простоты. Нет необходимости беспокоиться о сложностях различных описанных мною типов веток. Даже хотфиксы могут быть применены к mainline, а затем на проде, что делает их уже не настолько особенными, чтобы заслуживать отдельного имени.
Кроме того, поддержание mainline готовой к выпуску повышает дисциплину. Это удерживает готовность к выходу в продакшн в центре внимания разработчиков, гарантируя, что в систему не будут то и дело просачиваться проблемы — ни баги, ни сложности в процессах, замедляющие время цикла продукта. Полная дисциплина Continuous Delivery — с интеграциями в mainline по несколько раз в день без вреда для нее — многим кажется пугающе трудной. Однако, достигнув ее и привыкнув, команды обнаруживают, что это значительно снижает стресс и сравнительно легко поддерживается. Именно поэтому она является ключевой delivering zone в модели Agile Fluency®.
Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.