Scrum и xp заметки с передовой
Скачать 3.07 Mb.
|
Усвоенный урок: Если от ручного регрессионного тестирования отказаться нельзя, но очень хочется его автоматизировать – лучше не надо (ну разве что это действительно очень просто). Вместо этого сделайте всё для облегчения процесса ручного тестирования. А уже после этого можно подумать и про автоматизацию. Эволюционный дизайн Это значит начать с простого дизайна и постоянно улучшать его, а не пытаться сделать всё идеально с первого раза и больше ничего и никогда не трогать. С этим мы справляемся достаточно хорошо. Мы уделяем достаточно времени рефакторингу и улучшению существующего дизайна, и очень редко занимаемся детальным проектированием на годы вперёд. Иногда мы, конечно, можем напортачить, в результате чего плохой дизайн настолько врастает в систему, что рефакторинг превращается в действительно большую задачу. Но, в целом, мы полностью довольны этим подходом. Если практиковать TDD, то, по большому счёту, постоянное улучшение дизайна получается само собой. Непрерывная интеграция (Continuous integration) Чтобы внедрить непрерывную интеграцию нам пришлось для большинства наших продуктов создать достаточно сложное решение, построенное на Maven'е и QuickBuild'е. Это архиполезно и экономит массу времени. К тому же это позволило нам раз и навсегда избавится от классической фразы: "но у меня же это работает!". Наш сервер непрерывной интеграции является "судьёй" или эталоном, по которому определяется работоспособность всего исходного кода. Каждый раз, когда кто-то сохраняет свои изменения в системе контроля версий, сервер непрерывной интеграции начинает собирать заново все доступные ему проекты и прогоняет все тесты на сервере. Если хоть что-то пойдёт не так, то сервер обязательно разошлёт всем участникам команды уведомления. Такие электронные письма содержат в себе информацию про то, какие именно изменения поломали сборку, ссылку на отчёты по тестам и т.д. Каждую ночь сервер непрерывной интеграции пересобирает каждый проект заново и публикует на наш внутренний портал последние версии бинарников (EAR'ы, WAR'ы и т.д. [пр. переводчика – формат файлов, который используется в J2EE для компоновки модулей]), документации, отчётов по тестам, по покрытию тестами, по зависимостям между модулями и библиотеками и ещё много чего полезного. Некоторые проекты также автоматически устанавливаются на тестовых серверах. Scrum и XP: заметки с передовой 63 Чтобы это всё заработало, пришлось потратить уйму времени, но, поверьте мне, это того стоило. Совместное владение кодом (Collective code ownership) Мы выступаем за совместное владение кодом, хотя ещё не все наши команды внедрили у себя эту практику. На собственном опыте мы убедились, что парное программирование и постоянная смена пар автоматически увеличивают уровень совместного владения кодом. А команды, у которых совместное владение кодом на высоком уровне, доказали свою высочайшую надёжность. К примеру, они никогда не проваливают спринты из-за того, что у них кто-то заболел. Информативное рабочее пространство У всех команд есть доступ к доскам и незанятым стенам, которыми они действительно пользуются. Во многих комнатах стены завешаны всякого рода информацией о продукте и проекте. Самая большая проблема у нас – постоянно накапливающееся старье на стенах. Уже подумываем завести роль "домработницы" в каждой команде. Хотя мы и поощряем использование доски задач, еще не все команды внедрили их (см. стр. 43"Как мы обустроили комнату команды") Стандарты кодирования Недавно мы начали определять стандарты кодирования. Очень полезно – жаль не сделали этого раньше. Это не займёт много времени, начни с простого и постепенно дополняй. Записывай только то, что может быть понятно не всем, при этом, по возможности, не забудь сослаться на существующие материалы. У большинства программистов есть свой индивидуальный стиль кодирования. Мелкие детали: как они обрабатывают исключения, как комментируют код, в каких случаях возвращают null и так далее. В одних случаях эти различия не играют особой роли, в других могут привести к серьёзному несоответствию дизайна системы и трудно читаемому коду. Стандарты кодирования – идеальное решение этой проблемы, если они, конечно, регламентируют важные моменты. Вот небольшая выдержка из наших стандартов кодирования: • Вы можете нарушить любое из этих правил, но на то должна быть веская причина и это должно быть задокументировано. • По умолчанию используйте стандарты кодирования Sun: http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html • Ни при каких обстоятельствах не перехватывайте исключения без сохранения полного стека вызовов программы (stack trace), либо повторной генерации исключения (rethrow). Допустимо использование log.debug(), только не потеряйте стек вызовов. • Для устранения тесного связывания между классами применяйте внедрение зависимостей на основе сеттеров (Setter Based Injection) (разумеется, за исключением случаев, когда такое связывание просто необходимо). • Избегайте аббревиатур. Общеизвестные аббревиатуры, такие как DAO, допустимы. • Методы, которые возвращают коллекции или массивы, не должны возвращать null. Возвращайте пустые коллекции и массивы вместо null. Устойчивый темп / энергичная работа Множество книг по Agile-разработке программного обеспечения утверждают, что затянувшаяся переработка ведёт к падению продуктивности. После некоторых не вполне добровольных экспериментов на эту тему, я могу только согласиться с этим всем сердцем! Scrum и XP: заметки с передовой 64 Около года назад одна из наших команд (самая большая) очень-очень много работала сверхурочно. Качество кода было ужасное, и большую часть времени команда "тушила пожары". У тестировщиков (которые тоже работали сверхурочно) не было ни малейшей возможности нормально протестировать систему. Наши пользователи были в ярости, а газетчики готовы были нас растерзать. Спустя несколько месяцев мы смогли уменьшить количество переработки до приемлемого уровня. Люди перестали работать сверхурочно (кроме редких кризисов в проекте), и – вот сюрприз! – и производительность, и качество кода заметно улучшились. Конечно, уменьшение количества рабочих часов не было единственной причиной повышения производительности и улучшения кода. Но мы уверены, что это сыграло существенную роль. Scrum и XP: заметки с передовой 65 14 Как мы тестируем Это самая сложная часть. Вот только я не уверен, то ли это самая сложная часть Scrum'а, то ли разработки программного обеспечения в целом. Организация тестирования может достаточно сильно отличаться в различных компаниях. Всё зависит от количества тестировщиков, уровня автоматизации тестирования, типа системы (просто сервер + интернет приложение или, возможно, вы выпускаете «коробочные» версии программ?), частоты релизов, критичности ПО (блог-сервер или система управления полётами) и т.д. Мы довольно много экспериментировали, чтобы понять, как организовать процесс тестирования в Scrum'е. Сейчас я попытаюсь рассказать о том, что мы делали и чему мы успели научиться за это время. Скорее всего, вам не избежать фазы приёмочного тестирования В идеальном мире Scrum'а результатом каждого спринта должна быть система, потенциально готовая к использованию. Бери и устанавливай, да? Scrum команда релиз Пользователи 1.0.0 А вот и нет! По нашему опыту, такой подход обычно не работает. Там будет куча противных багов. Если "качество" для вас хоть что-нибудь значит, тогда придётся позаботиться о ручном приёмочном тестировании. Это когда специально выделенные тестировщики, которые не являются частью команды, бомбят систему теми видами тестов, о которых Scrum-команда не могла даже и подумать, или на которые у неё не было времени, или соответствующего оборудования. Тестировщики работают с системой точно так же, как и пользователи, а значит, что это нужно делать вручную (если, конечно же, ваша система разработана для людей). Scrum и XP: заметки с передовой 66 Scrum команда релиз 1.0.0 релиз релиз 1.0.1 1.0.1 Пользователи Команда тестировщиков Команда тестировщиков найдёт баги, Scrum-команде придётся подготовить версию с исправленными багами, и рано или поздно (будем надеяться что рано) вы сможете выпустить для своих пользователей версию 1.0.1 с необходимыми исправлениями. Она будет куда более стабильной, чем глючная 1.0.0. Когда я говорю "фаза приёмочного тестирования", я имею в виду весь период тестирования, отладки и перевыпуска, пока версия не будет достаточно хороша для выхода в свет готового продукта. Минимизируйте фазу приёмочного тестирования Приёмочное тестирование, мягко говоря, приносит некоторые неудобства. Оно определённо не имеет ничего общего с гибкими методологиями. И, несмотря на то, что мы не можем избавиться от этой фазы, мы можем попробовать свести её к минимуму. А если точнее, уменьшить количество времени, которое для него необходимо. Этого можно достигнуть следующими способами: • Максимально улучшить качество исходного кода, создаваемого Scrum-командой. • Максимально увеличить эффективность ручного тестирования (т.е. найти лучших тестировщиков, обеспечить их лучшими инструментарием и убедиться, что они сообщают о тех задачах, которые отнимают много времени и могут быть автоматизированы). Так как же мы можем поднять качество кода команды? Ну, вообще-то способов существует очень много. Я остановлюсь на тех, которые, как нам показалось, действуют лучше всего: • Включите тестировщиков в Scrum-команду. • Делайте меньше за спринт. Повышайте качество, включив тестировщиков в Scrum-команду О, я уже слышу эти возражения: • "Но это же очевидно! Scrum-команды должны быть кросс- функциональными!" • "В Scrum-команде не должно быть выделенных ролей! У нас не может быть человека, занимающегося только тестированием!" Я бы хотел кое-что разъяснить. В данном случае, под "тестировщиком" я подразумеваю человека, главная специализация которого тестирование, а не человека, чья роль – это исключительно тестирование. Разработчики достаточно часто бывают отвратительными тестировщиками. Особенно, когда они тестируют свой собственный код. Тестировщик – это "последняя инстанция". Кроме того, что тестировщик – обычный член команды, он ещё и выполняет очень важную функцию. Он – человек, за которым всегда "последнее слово". Ничто не может считаться готовым в спринте, пока он не подтвердит, что это действительно так. Я обнаружил, что разработчики часто говорят, что что-то готово, хотя в действительности это не так. Даже, если у вас имеется очень чёткое определение критерия готовности (которое у вас должно быть, см. стр. 29 "Критерий готовности"), в большинстве случаев, разработчики будут Scrum и XP: заметки с передовой 67 часто его забывать. Мы, программисты, люди очень нетерпеливые и стремимся перейти к следующему пункту списка как можно быстрее. Так как же мистер Т (наш тестировщик) узнает, что что-то действительно готово? Ну, прежде всего, он может это (сюрприз!) протестировать! В большинстве случаев оказывается, что то, что разработчик считает готовым, на самом деле даже невозможно протестировать! Либо по причине того, что оно не было зафиксировано в репозитории, либо не установлено на сервер, либо не может быть запущено, или ещё что- нибудь. Как только мистер Т завершил тестирование, первым делом он должен пройтись по критериям готовности (если таковые у вас имеются) и, желательно, вместе с разработчиком. К примеру, если критерий готовности требует наличия заметок к релизу, то мистер Т проверяет их наличие. Если же необходима более формальная спецификация функционала (хотя это бывает редко), мистер Т проверяет и это тоже. И так далее. Приятный дополнительный эффект этого подхода состоит в том, что у команды появляется человек, который отлично подходит на роль организатора демонстрации результатов спринта. Чем занимается тестировщик, когда нечего тестировать? Этот вопрос никогда не теряет своей актуальности. Мистер Т: "Scrum-мастер, сейчас нечего тестировать. Чем я могу заняться?". Может пройти неделя, пока команда закончит первую историю, так чем же будет заниматься тестировщик всё это время? Для начала, ему следует заняться подготовкой к тестированию. А именно: написанием спецификаций тестов, подготовкой тестового окружения и так далее. Таким образом, когда у разработчика появится что- нибудь готовое к тестированию, мистер Т должен быть готов начать тестирование. Если команда практикует TDD, люди с первого дня заняты написанием тестирующего кода. В этом случае, тестировщик может заняться парным программированием с разработчиками, пишущими тестирующий код. Если же тестировщик вообще не умеет программировать, ему следует работать в паре с разработчиком в роли "штурмана", дав напарнику возможность печатать. У хорошего тестировщика обычно получается выдумать больше разных тестов, чем у хорошего разработчика, поэтому они друг друга дополняют. Если же команда не занимается TDD или же количества подлежащих написанию тестов недостаточно, чтобы полностью загрузить тестировщика, он просто может делать всё что угодно, чтобы помочь команде достичь цели спринта. Как и любой другой член команды. Если тестировщик умеет программировать – отлично. Если нет, команде придется выявить все задания, не требующие навыков программирования, но которые необходимо выполнить за спринт. Когда на планировании спринта истории разбиваются на задачи, то в первую очередь команда старается сфокусироваться на задачах, требующих программирования. Однако, как правило, существует множество задач, которые не требуют программирования, но, тем не менее, подлежат выполнению по ходу спринта. Если вы в течение планирования спринта потратите немного времени на определение задач, которые не требуют программирования, то мистер Т получит возможность сделать для достижения цели спринта очень много. Даже если он не умеет программировать или в этот момент нечего тестировать. Вот некоторые примеры задач, которые не требуют программирования, но которые часто должны быть закончены до конца спринта: • Установить и настроить тестовое окружение. • Уточнить требования. • Детально обсудить процесс установки. • Написать документы по установке (заметки к релизу, readme.txt или что там требуется в вашей компании). • Пообщаться с подрядчиками (например, с дизайнерами пользовательского интерфейса). • Улучшить скрипты автоматизированной сборки. • Последующее разбиение историй на задачи. • Собрать ключевые вопросы от разработчиков и проследить, чтобы они не остались без ответов. Scrum и XP: заметки с передовой 68 С другой стороны, что мы делаем в случае, когда мистер Т оказывается "узким местом"? Скажем, сейчас последний день спринта и большая часть функционала уже реализована, а мистер Т не имеет возможности протестировать всё необходимое. Что мы делаем в этом случае? Ну, мы можем сделать всех членов команды помощниками мистера Т. Он решает, что он может сделать самостоятельно, а простые задачи поручает остальным членам команды. Вот, что такое кросс-функциональная команда! Итак, мистер Т действительно играет особую роль в команде, но он, кроме того, может выполнять работу других членов команды, так же, как и другие члены команды могут делать его работу. Повышайте качество – делайте меньше за спринт! Это решается ещё на планировании спринта. Проще говоря, не пытайтесь сделать как можно больше историй за спринт. Если у вас существуют проблемы с качеством или вам приходится тратить слишком много времени на приёмочное тестирование, просто делайте меньше за спринт! Это автоматически приведёт к повышению качества, уменьшит продолжительность приёмочного тестирования и количество багов, которые вылезут у конечного пользователя. В конце концов, это должно поднять производительность всей команды, ведь она сможет сконцентрироваться на новых задачах, вместо того, чтобы постоянно тратить время на исправление старого кода, который постоянно ломается. Почти всегда получается дешевле сделать меньше, но качественнее, чем больше, но потом в панике латать дыры. Стоит ли делать приёмочное тестирование частью спринта? Тут у нас полный «разброд и шатание». Некоторые наши команды включают приёмочное тестирование в спринт, однако, большинство – нет. И вот почему: • Спринт ограничен во времени. Приёмочное тестирование (которое, если брать во внимание моё определение, включает отладку и повторный выпуск продукта), довольно сложно втиснуть в чёткие временные рамки. Что если время уже вышло, а в системе остался критический баг? Что тогда? Собираетесь ждать завершения ещё одного спринта? Или выпустите готовый продукт с этим багом? В большинстве случаев оба вариант неприемлемы. Именно поэтому мы выносим ручное приёмочное тестирование за пределы спринта. • Если две Scrum-команды работают над одним продуктом, тогда ручное приёмочное тестирование необходимо проводить, собрав результаты работы обеих команд. Если обе команды включают в спринт ручное приёмочное тестирование, тогда всё равно нужна команда, которой придётся протестировать финальный релиз, который получается после интеграции результатов работы обеих команд. Scrum команда A T релиз 1.0.0 Scrum команда B T Команда тестировщиков Это далеко не идеальное решение, но в большинстве случаев оно нас устраивает. Scrum и XP: заметки с передовой 69 Соотношение спринтов и фаз приёмочного тестирования В идеальном Scrum-мире фаза приёмочного тестирования не нужна, так как каждая Scrum-команда после каждого спринта выдаёт новую, готовую к реальному использованию версию системы Спринт №1 Время 1.0.0 Спринт №2 1.1.0 Пользователи Ну, а на самом деле, всё выглядит чуть-чуть по-другому: После первого спринта выпускается глючная версия 1.0.0. Во время второго спринта начинают поступать сообщения об ошибках, и команда большую часть времени занимается отладкой, а потом выпускает версию с исправлениями 1.0.1 в середине спринта. Потом, в конце второго спринта выходит версия 1.1.0 с новым функционалом, которая, естественно, оказывается ещё более глючной, так как у команды просто не хватило времени довести её до ума из-за того, что приходилось подчищать хвосты, оставшиеся с прошлого спринта. И так по кругу. Наклонная красная штриховка второго спринта символизирует хаос. Неприглядная картина, да? А самое грустное в том, что эта проблема остаётся даже при наличии команды приёмочного тестирования. Единственная разница состоит в том, что основная масса сообщений об ошибках поступает от команды тестирования, а не от негодующих пользователей. Но эта разница просто огромна с точки зрения бизнеса, хотя для разработчиков ничего и не меняется. Ну, кроме только того, что тестировщики обычно менее агрессивны, чем конечные пользователи. Обычно. Scrum и XP: заметки с передовой 70 Простого решения этой проблемы мы так и не нашли. Но наэкспериментировались с разными подходами вдоволь. Перво-наперво, опять же, необходимо обеспечить наивысшее качество кода, который создаёт Scrum- команда. Стоимость раннего обнаружения и исправления ошибки (в пределах спринта) несравнимо ниже стоимости обнаружения и исправления ошибки после окончания спринта. Но факт остаётся фактом: как бы мы не уменьшали количество ошибок, они обязательно найдутся и после завершения спринта. Так что же с этим делать? Подход №1: "Не начинать новые истории, пока старые не будут готовы к реальному использованию" Звучит классно, не так ли? Вас тоже греет эта мысль? :) Несколько раз мы уже было решались на этот подход, и даже рисовали теоретические модели того, как бы могли это сделать. Но каждый раз мы останавливались, когда рассматривали обратную сторону медали. Нам бы пришлось ввести неограниченную по времени итерацию между спринтами, в которую бы мы только тестировали и исправляли ошибки до тех пор, пока у нас на руках не было бы готового к использованию релиза. Спринт №1 Время Спринт №2 Релиз Спринт №3 Релиз Наличие неограниченной во времени итерации между спринтами нам не нравилось в основном из-за того, что она бы нарушила регулярность спринтов. Мы не смогли бы больше заявлять: "Каждые три недели мы начинаем новый спринт". А кроме того она не решает проблему. Даже если мы введём такую итерацию, всё равно время от времени будут появляться ошибки, срочно требующие внимания, и нам надо быть к этому готовыми. Подход №2: "Начинать реализовывать новые истории, но наивысшим приоритетом ставить доведение старых до ума" Мы предпочитаем этот подход. По крайней мере, до сих пор так и было. По сути, он состоит в следующем: когда мы заканчиваем спринт, мы переходим к следующему, но учитываем, что в следующем спринте нам потребуется время на исправление багов прошлого спринта. Если следующий спринт оказывается перегружен работой над исправлением дефектов прошлого, то мы пытаемся понять причину такого количества дефектов и выработать способ поднять качество. И мы выбираем длину спринта достаточной, чтобы успеть справиться с приличным объёмом работы по исправлению багов прошлого спринта. Постепенно, за несколько месяцев, количество работы по устранению дефектов прошлых спринтов уменьшилось. Кроме того, мы смогли добиться, чтобы на устранение дефекта требовалось отвлекать меньше людей, то есть, нет нужды беспокоить всю команду по поводу каждого бага. Теперь хаос в наших спринтах снизился до приемлемого уровня. Scrum и XP: заметки с передовой 71 При планировании спринта, чтобы учесть то время, которое мы планируем потратить на устранение дефектов, мы устанавливаем уменьшенное значение фокус-фактора. Со временем команды начинают очень хорошо определять нужное значение фокус-фактора. В этом также очень помогает статистика реальной производительности (см. стр. 23 "Как команда принимает решение о том, какие истории включать в спринт?") Неправильный подход: "Клепать новые истории" Если перефразировать, то это значит: "реализовывать новые истории, вместо того, чтобы довести старые до ума". Казалось бы – да кому такое может прийти в голову? А, тем не менее, мы частенько допускали эту ошибку в начале и, я уверен, что и многие другие компании тоже. Эта неадекватность связана со стрессом. На самом деле многие менеджеры не понимают, что когда кодирование закончено, то, мы, как правило, всё ещё далеки от релиза, готового к использованию. Поэтому менеджер (или product owner) просит команду наращивать функционал продукта в то время, как груз почти-готового-к-выпуску кода становится всё тяжелее и тяжелее, замедляя весь процесс. Не забывайте об ограничении системы Допустим приемочное тестирование – это ваше самое узкое место. Например, у вас слишком мало тестировщиков или фаза приемочного тестирования забирает много времени из-за ужасного качества кода. Допустим, команда, которая выполняет приемочное тестирование, успевает проверить 3 фичи в неделю (не подумайте, мы не используем "фичи в неделю" как метрику; я взял это для примера). Также допустим, что ваши разработчики могут сделать 6 новых фич в неделю. Для менеджера или product owner'а (даже для самой команды) будет искушением запланировать разработку 6-ти новых фич в неделю. Только не это! Витать в облаках долго не получится, а когда упадёте – будет больно. Лучше при планировании рассчитывать на 3 фичи в неделю, а оставшееся время направить на устранение узкого места. Например: • Заставить разработчиков потестировать (приготовьтесь к "благодарности" за это...). • Внедрить новые инструменты и скрипты, которые упростят тестирование. • Добавить больше автоматизации. • Сделать длиннее спринт и включить приемочное тестирование в спринт. • Выделить несколько "тестовых спринтов", где вся команда будет работать над приемочным тестированием. • Нанять больше тестировщиков (даже если это означает уволить некоторых разработчиков). Мы пробовали все эти решения (кроме последнего). С точки зрения долгосрочной перспективы лучшими являются пункты 2 и 3, а именно: более эффективные инструменты, скрипты и автоматизация тестирования. А для выявления узких мест лучше всего подходят ретроспективы. Возвращаясь к реальности У вас, наверное, сложилось впечатление, что у нас есть тестеры во всех Scrum-командах, и что мы обзавелись ещё одной огромной командой тестеров, которые после каждого спринта проводят приёмочное тестирование всех готовых продуктов. Ну ... это не так совсем. У нас всего несколько раз получилось выделить время на все эти процедуры, но тогда мы на собственном опыте убедились насколько это полезно. Могу сказать, что на данный момент мы всё ещё далеки от желаемого процесса обеспечения качества, и нам по-прежнему есть чему учиться. |