Как организовать тестирование, чтобы ускорить и стабилизировать релизы продукта. Часть 1
Если командная работа не согласована, между отдельными участниками процесса и целыми командами постоянно будут происходить столкновения, а продукты компании или микросервисы внутри одного продукта будут мешать друг другу при использовании общих ресурсов и инфраструктуры. Результатом будут постоянные поломки, конфликты и снижение темпов работы. Быстрые и предсказуемые релизы в таких условиях будут недостижимы.
Меня зовут Виктория Дежкина, я занимаюсь тестированием в Департаменте разработки и сопровождения продуктов больших данных X5 Retail Group. Я расскажу, как мы изменили процесс тестирования в одной из наших продуктовых команд, чтобы ускорить подготовку релизов практически вдвое и избавить команду от стресса. Теперь этот подход к тестированию мы внедряем и в других продуктах компании.
В Дирекции больших данных X5 Retail Group сегодня ведется развитие 13 продуктов, из них 4 – в департаменте монетизации, где продукты ориентированы на внешний рынок, и любая ошибка, будь то дефект на проде или запоздало выпущенная фича, имеет экономический эффект и ведет к потере клиентов. По сути, это внутренние команды, которые зарабатывают на внешнем рынке и играют по правилам малого бизнеса в рамках большой компании.
Все наши продукты значительно различаются по своим целям и потому требуют разных подходов к разработке и тестированию, но у них много и общего: они пользуются одной инфраструктурой (Kubernetes, Greenplum, тестовые серверы), а разработчики из разных продуктовых команд иногда подменяют друг друга на время отпусков.
В такой ситуации резко возрастает роль договоренностей: если одна часть команды не знает, что делает другая, и в каждой команде действуют свои правила, неизбежно будут возникать проблемы.
Например, два продукта пользуются одной инфраструктурой нагрузочного тестирования, и обоим срочно нужно провести тесты. Не оповещая друг друга, они делают это одновременно, в итоге получают ложные результаты, потому что СУБД «легла», а из-за кого – непонятно. Хотели сэкономить время на переговорах, в итоге потеряли массу времени без всякого результата.
Не исключены и потери данных. Допустим, один из продуктов занимает свободный тестовый сервер, никого об этом не предупреждая. Официально «железо» считается свободным, поэтому туда заходит другой продукт и случайно стирает все тестовые данные первого. Это, конечно, не пользовательские данные, а всего лишь тестовые, но все равно довольно неприятный кейс.
Может и просто не хватить рабочих рук, если работа не будет планироваться заранее. Например, сейчас нагрузочное тестирование у нас в Дирекции работает в формате сервиса, то есть мы выделяем соответствующего специалиста командам по запросу. Если несколько команд, не предупредив заранее, придут требовать нагрузочное тестирование в один день, тестировщиков на всех может не хватить.
Кажется, что самый простой выход в такой ситуации – установить для всех продуктов единые правила взаимодействия. Но проблема в том, что все продукты разные. Часть из них предназначена для внутренних пользователей, то есть специалистов из других департаментов компании, – например, сервисы для ценообразования или изучения спроса. Другая часть разрабатывается для внешних пользователей – например, для поставщиков. У этих продуктов совершенно разная логика архитектурных решений и критерии качества.
Продукты на разных стадиях готовности также не приемлют одинакового подхода: на ранней стадии, когда продукт тестирует идеи, в приоритете – понятность функционала для пользователя и отсутствие угроз корпоративной безопасности. Когда продукт выходит на поддержку, на первое место выходят другие требования: удобство пользователей, стабильность существующего функционала, скорость реакции на дефекты и полное соблюдение корпоративной политики безопасности.
Два этих фактора – совместная работа на одной территории и уникальные особенности продуктов – и поставили перед нами задачу: выработать правила, которые позволят командам не мешать друг другу, но при этом не будут связывать тестировщиков и разработчиков по рукам и ногам и не сведут разработку разных продуктов к одному шаблону, зарубив все преимущества agile и продуктового подхода на корню.
Скажу пару слов о том, почему ведущую роль в создании стандартов взаимодействия в продуктовых командах у нас играют тестировщики. Дело в том, что в силу специфики нашей работы мы видим продукт целиком, тогда как разработчики обычно сосредоточены на одном небольшом участке. Мы первые замечаем проблемы и предлагаем варианты их устранения, но окончательное решение вырабатывается вместе с разработчиками.
Мы уже писали, как происходит эта коллективная работа: на начальном этапе нам приходится буквально составлять единый словарь, чтобы не путаться в терминах. Но это только начало. Дальше предстоит договориться о массе самых разных нюансов.
Расскажу, как это происходило на примере одного из наших продуктов – системы автоматизации закупок торговой сети. Ее задача – обеспечить работу всех процессов от момента, как у магазина возникает потребность в определенных товарах, до момента, как он их получает.
Какие процессы менялись в нашем продукте
Когда мы пришли в продукт, релизы должны были выходить раз в 2 недели, но почти всегда опаздывали на несколько дней, и день релиза всегда означал, что мы обязательно задержимся на работе и до последнего будем стабилизировать существующую версию. Необходимо было что-то менять.
Важно заметить, что перемены – это общее дело. Кто бы ни был их инициатором, тестировщик или сами разработчики, без согласия всей команды и сильных союзников не обойтись. Любые изменения нужно обсуждать всей командой, чтобы собрать как можно больше идей и не упустить возможные риски. Деливери и продакт-менеджер нашего продукта и до меня системно работали над улучшением процессов как с технической, так и с продуктовой стороны. Я, придя в команду, рассмотрела процесс со стороны тестирования, и вместе мы продумали согласованную «стратегию перемен». Первым пунктом в ней стало изменение в выкладке кода.
Порядок выкладки кода
Порядок выкладки – одна из главных «болей» командной разработки, и проблемы тут бывают самые разные. Например, у команды только одна ветка с кодом, и там не прекращается исправление ошибок. Или веток несколько, но на тестовой среде могут одновременно оказаться задачи с пересекающимся функционалом, в итоге тестировщики не смогут локализовать дефект или проверить часть новых функций, заблокированных дефектом одной из задач. А уж про отчаянных разработчиков, которые не считают чем-то плохим по-быстрому править мелочи на проде, не предупреждая других, я вообще промолчу.
Чтобы всего этого избежать, нам нужно было определить количество веток и сред, а также согласовать порядок выкладывания кода на тест.
Проще всего было бы разделить процесс на две ветки с «чистым» и «грязным» кодом. Но нам нужно было соблюсти множество условий:
- Избежать исправления одного и того же дефекта в разных задачах
- Исключить попадание на прод непроверенных задач при выкатке уже проверенных
- Получить возможность проверять задачи как «по одной», так и всей «релизной сборкой»
- Наладить параллельный процесс тестирования и разработки
- Иметь возможность в любой момент видеть, какие задачи готовы к выкатке и какие задачи лежат на разных тестовых средах
- Организовать для разработчиков бэкенда и фронтенда доступ к совместной разработке.
Мы потратили 2 часа на обсуждение новой схемы работы и пришли к следующему: заводим три ветки, две из них (release и master) будут с чистым кодом. В «мастере» лежит текущая продуктовая версия, в «релизе» – фичи, готовые к выкатке. В Dev происходит тестирование, тут располагаются задачи, готовые к тесту, разработка проходит локально. Выкатка на каждую ветку происходит по согласованию с тестировщиком. Вот так:
Что нам это дало в плане тестирования:
- Время на тестирование каждой задачи по отдельности сократилось на 20%. Больше не требовалось начинать проверку заново, если на тест выкатилась новая задача без предупреждения, новые задачи не блокировали проверку уже сделанных, ускорилось время локализации дефектов.
- К планируемому дню фиксации релиза непроверенными оставались 1-2 задачи вместо 4 (то есть время на их проверку сократилось вдвое).
- Сократилось время интеграционного тестирования и тестирования релиз-кандидата с 6 часов до 2 (считая ретест).
- Снизилось количество дефектов, находимых на этапе релиза. Раньше на первой релизной версии мы находили их более 10, а теперь не более 4. Время на ретест снизилось на 2 часа.
- Появилась возможность продолжать разработку параллельно с тестированием.
Общее время, которое мы тратили на тестирование, задерживая выкатку на прод, сократилось на 8 часов. Всего 2 часа обсуждения новой схемы работы с командой – и удалось сберечь целый рабочий день, который раньше приходилось тратить каждые две недели. Неплохо?
Но и проблемы остались.
- Разработчики тратили время на оповещение тестировщика о готовности задачи, отвлекались и теряли контекст задачи при необходимости выкатки на тест.
- На релиз-кандидате продолжали появляться ошибки, связанные с неодинаковой выкаткой на тест, релиз и прод.
- Хотфиксы на проде при таком подходе были возможны только сразу после выкатки.
- Не было понимания, когда и где проводить нагрузочное тестирование.
Итог: мы продолжали задерживаться на работе в день релиза.
Мы собрались снова. Часть проблем решилась уточнением процесса разработки и добавлением CI:
Автоматическую выкатку на все среды мы настраивали почти месяц, но это дало ускорение по времени почти в 2 рабочих дня. Команда давно к этому шла, этим занимались деливери-менеджер и сами разработчики, но им пока не удавалось добиться двух вещей: чтобы выкатка на все среды была единообразной и чтобы при этом сохранялась возможность контроля версий. Автоматическая выкатка или нарушала главный принцип тестирования «в любой момент времени тестировщик должен знать, что находится в каждой среде», или тормозила разработчиков, которые не могли завершить разработку задачи без разрешения выкатки на тест.
Это сложная дилемма. Проигнорируешь первый принцип и вместо ускорения получишь резкое снижение предсказуемости релизов и ошибочно выкаченные задачи. Например, если дефект будет исправляться уже в связке с «чистой задачей», то при выкатке исправленной задачи она непременно зацепит дефектную. Поэтому придется либо ждать исправления дефекта связанной задачи, откладывая дату релиза, либо повторно исправлять уже исправленный дефект.
Автоматическая выкатка – не человек, с ней не договоришься. Поэтому проблему с оставшимися ошибками мы решали другим путем – особым подходом к планированию, а затем и добавлением еще одного стенда.
Планирование для разработки и тестирования
Изначально при планировании мы с командой обсуждали, понятна ли разработчикам задача, сколько времени она займет и уложимся ли мы в спринт. Тестировщик оценивал, сколько времени займет тестирование.
При этом мы совсем не обсуждали ошибки, которые могут произойти: не предвидели их наперед и не включали в список возможных задач. В итоге когда эти кейсы вылезали в процессе тестирования, они добавлялись уже отдельной задачей, требовали время на проработку и увеличивали объем релиза, а иногда вообще обнаруживались только в релиз-кандидате, на этапе совместного тестирования задач, перенося выкатку на неопределенный срок. Мы собрались на троих – тестировщик, деливери и продакт и продумали новую схему взаимодействия.
Теперь мы проговариваем все возможные ошибки до начала разработки. Потребовалось стать занудным отличником, который на этапе планирования спрашивает обо всем и вся: «А что будет, если сторонний сервис отвалится?», «А если нам вернется null, а не 0?», «А что если нам придут не все данные?», «А если печенеги нападут?» и так далее, но зато теперь мы были готовы ко всему.
Также мы начали проговаривать, как именно будем реализовывать ту или иную задачу и как будем ее тестировать. Это позволило сократить самодеятельность (изобретение всяких велосипедов, например) и дало каждому участнику команды понимание, что делают его коллеги. Это, к слову, позволило нам отказаться от критериев приемки (AC).
Чтобы обсуждение в новом формате не получалось слишком громоздким, мы стали делать это не на 2 недели вперед, а на неделю.
Новый порядок выкладки кода и планирования задач были только первыми шагами. В следующей части статьи, которая выйдет завтра, я подробно расскажу о том, как мы изменили ещё целый ряд процессов в команде:
- Формат постановки и приемки задач и дефектов: ушли от user stories к более удобному для нас гибриду “use case + техзадача”.
- Момент тестирования: установили в релизном цикле 5 точек, в которых тестировщики активно контролируют процесс создания продукта.
- Правила взаимодействия связки «бэкенд – тестирование – фронтенд»: выработали схему, которая позволила проверять все данные, которые передаются между бэкендом и фронтендом.
- Ускорение работы по исправлению дефектов: установили правила, как приоритизировать задачи по отладке, чтобы не отвлекать разработчиков лишний раз.
Эти меры позволили нам сократить релизный цикл с 2,5 недель до 1. Прирост по скорости может показаться небольшим, но главное достижение было в том, что наши релизы стали более стабильными и предсказуемыми – мы получили возможность выкатывать релизы «по команде»: можем собраться в любой день, выкатить задачи и к вечеру все будет готово и отлажено.