Чистый Agile. Основы гибкости Мартин Роберт
А теперь за работу! Позже я расскажу в подробностях о том, как ведется разработка по историям. А сейчас просто представим, что есть какой-то способ превратить истории в работающий код. Представьте этот способ как перекладывание карточек с историями из кучи запланированное в кучу сделанное.
К середине итерации должно быть выполнено много историй. А сколько единиц должно оставаться? Правильно, столько же, сколько и сделано. В нашем случае это 15. Чтобы провести промежуточную проверку, нужно уметь делить на два.
Поэтому давайте созовем всех на промежуточное совещание. Пускай это будет понедельник, первый день второй недели итерации. На встречу должны прийти члены команды и заинтересованные стороны — последние оценивают ход работ.
Ой-ей, да истории выполнены только на 10 единиц! Осталась всего неделя, вряд ли за нее выполнят больше двадцати! Поэтому заинтересованные стороны вычитают некоторое количество историй из плана, чтобы осталось только 10 единиц до конца итерации.
В пятницу после обеда подводится итог итерации с демонстрацией. Оказывается, выполнено лишь 18 единиц. Итерация провальна?
Нет! Итерации не бывают провальными. Целью итерации является сбор данных для менеджеров. Конечно, было бы здорово, если бы во время каждой итерации появлялся код, но даже когда этого не происходит, главное — собрать данные.
Вчерашняя погода
Теперь мы знаем, сколько единиц сложности историй мы можем выполнить за неделю — около 18. Как думаете, сколько единиц запланируют заинтересованные стороны в понедельник, в начале следующей итерации? Без сомнений, восемнадцать. Это называют вчерашней погодой. Вчерашняя погода лучше всего предскажет, какая погода будет сегодня. А вернее всего ход текущей итерации может предсказать то, что получено в ходе предыдущей.
Поэтому на встрече по планированию заинтересованные стороны добавляют столько историй, чтобы получилось 18 единиц. Но на этот раз на таком же промежуточном совещании происходит кое-что, что не укладывается в голове. Выполнено 12 единиц. Надо ли говорить об этом партнерам?
Нет, не стоит. Они сами все увидят. Поэтому заинтересованные стороны добавляют шесть дополнительных единиц, чтобы по плану вышло 24.
Конечно, выходит так, что команда успевает выполнить только 22. Поэтому на следующую итерацию планируется 22 единицы.
Окончание проекта
И на этой итерации происходит то же самое. По завершении итерации скорость, с которой выполняются истории, наносится на соответствующий график, чтобы все могли видеть, как быстро команда выполняет свою работу.
Предположим, что так продолжается итерация за итерацией, месяц за месяцем. Что происходит с кипой карточек с историями? Представьте, что кругооборот итераций — это насос, перекачивающий ROI из этой кипы. И представьте, что непрерывное появление требований в ходе проекта — это насос, перекачивающий окупаемость обратно в кипу. Пока входящие вложения превосходят их отток, проект будет продолжаться.
Однако может случиться и так, что количество новых функций для реализации постепенно сведется к нулю. Когда это произойдет, денежные средства, которые содержат карточки, будут исчерпаны через несколько итераций. Придет день, когда на встрече по планированию заинтересованные стороны посмотрят на карточки с историями и поймут, что работы больше не осталось. Проект завершен.
Проект заканчивается не тогда, когда выполнены все истории. Проект можно считать завершенным, когда больше не остается карточек с историями, которые стоит выполнять.
Иногда удивляет, какие карточки с историями остаются в кипе, когда проект заканчивается. Однажды я работал над проектом, который длился год. Самая первая история, написанная для проекта и давшая ему название, так и не была реализована. Эта история была важна в свое время, но позже появились более срочные истории, за которые брались разработчики. И к тому времени, когда срочные истории были выполнены, оказалось, что важность первоначальной истории испарилась.
Истории
Пользовательские истории — это простые инструкции, которые напоминают нам о свойствах функций. Мы стараемся не записывать слишком много подробностей, истории мы пишем как раз потому, что знаем, что подробности наверняка не раз изменятся. Подробности нужно записывать позже, в качестве приемочных тестов.
Истории должны соответствовать следующим атрибутам качества (критерии INVEST).
• Независимость (Independent). Пользовательские истории независимы друг от друга. Это значит, что истории необязательно выполнять в каком-то определенном порядке. Например, выход не нужно выполнять после входа.
Это гибкое требование: бывает и так, что какие-то истории будут зависеть от других, которые нужно реализовать в первую очередь. К примеру, если мы реализуем вход без функции восстановления забытого пароля, то однозначно восстановление пароля в какой-то мере зависит от входа. И все же мы стараемся отделить истории так, чтобы они были зависимы друг от друга в наименьшей мере. Это позволяет выполнять истории в нужном клиенту порядке.
• Обсуждаемость (Negotiable). А это еще одна причина, по которой мы не записываем все подробности. Нужно, чтобы разработчики и клиенты могли обсуждать эти подробности.
Например, клиенты могут запросить классный интерфейс с возможностью перетаскивания в него чего-либо. Разработчики могут посоветовать интерфейс попроще, с флажками, объяснив, что разработка в таком случае будет проще и дешевле. Обсуждения важны, поскольку это один из немногих способов, с помощью которых клиенты получают представление о том, как управлять стоимостью разработки ПО.
• Ценность (Valuable). Клиенты хотят видеть, что у истории есть определенная измеримая ценность.
Рефакторинг нельзя считать историей. Архитектуру тоже нельзя. И чистка кода никакая не история. История — это всегда что-то, имеющее ценность для бизнеса. Не переживайте, нам доведется иметь дело с рефакторингом, архитектурой и чисткой кода, но не с самими историями.
Это значит, что история будет проходить через все уровни разработки программы. То есть она может частично затрагивать часть реализации графического интерфейса, промежуточного программного обеспечения, баз данных и так далее. Представьте, что история — это тонкий вертикальный срез, проходящий сквозь горизонтальные слои проекта.
Количественное измерение ценности можно проводить без формальностей. Некоторым командам может вполне подойти шкала приоритетов «высокий — средний — низкий», кому-то больше понравится десятибалльная шкала. Не имеет значения, какую шкалу использовать, главное — уметь чувствовать разницу между историями, ценность которых значительно отличается.
• Поддаваемость оценке (Estimable). Пользовательская история должна быть достаточно конкретной, чтобы разработчики могли сделать прогноз.
История «программа должна быть быстрой» не поддается оценке, потому что быстрота не имеет предела. Это сопутствующее требование, которое относится ко всем историям.
• Компактность (Small). Пользовательская история должна быть небольшой, чтобы один-два разработчика смогли с ней справиться за одну итерацию.
Не нужно, чтобы одна история огромным одеялом накрывала всю команду в течение целой итерации. Желательно, чтобы в итерации было примерно то же количество историй, что и разработчиков в команде. Если в команде 8 разработчиков, нужно подбирать от 6 до 12 историй для каждой итерации. Вы ведь не хотите завязнуть на одном этапе, верно? Это скорее совет, чем правило.
• Тестируемость (Testable). Клиенты должны четко назвать тесты, позволяющие подтвердить, что история выполнена.
Как правило, эти тесты пишут QA-специалисты. Тесты должны быть автоматизированы, с их помощью выполняется проверка историй на завершенность. Подробнее об этом вы узнаете позже. А пока что не забывайте, что каждая история должна быть достаточно конкретной, чтобы можно было подобрать необходимые тесты.
Но, может, это идет вразрез с принципом обсуждаемости? Нет, потому что когда мы пишем историю, то не обязаны знать, какие тесты нужны. Когда возникнет необходимость, тогда и будет написан тест. Хотя я еще не знаю всех нюансов истории вход, я уверен, что ее можно протестировать, потому что вход — это конкретное действие. А теперь посмотрим на историю пригодный к эксплуатации. Ее никак не протестируешь. И даже никак не оценишь. Действительно, прогнозируемость и тестируемость идут рука об руку.
Оценка историй
Существует множество способов оценки историй. Большинство из них — разновидности подхода Wideband Delphi[38].
Один из простейших способов — оценка на пальцах. Разработчики сидят вокруг стола, вчитываются в историю и, если необходимо, обсуждают ее с заинтересованной стороной. Потом разработчики прячут одну руку за спину так, чтобы никто не мог ее увидеть, сгибают пальцы так, чтобы по пальцам можно было понять, сколько единиц сложности требуется, по их мнению, затратить на выполнение истории. После этого кто-нибудь начинает счет, и на счет «три» все одновременно достают руки с зажатыми пальцами из-за спины.
Если все показали одинаковое число пальцев либо отклонение невелико и в среднем все показывают одно и то же, то число записывается на карточке с историей, а команда переходит к следующей истории. Но если между участниками встречи есть значительные разногласия, то разработчики обсуждают причину и повторяют действия до тех пор, пока согласие не будет достигнуто.
Можно оценивать истории по размерам одежды: большие, средние и маленькие. Если хочется задействовать все пять пальцев, продолжайте. С другой стороны, использование шкалы с большим количеством баллов почти всегда абсурдно. Помните, мы хотим дать достоверную оценку, а не наиболее точную.
Покер планирования[39] — похожий способ, но там нужны карты. Существует много популярных колод карт для такого покера. В большинстве из них применяется что-то вроде рядов Фибоначчи. В одной распространенной колоде содержатся такие карты:? 0, , 1, 2, 3, 5, 8, 13, 20, 40, 100 и . Если у вас такая колода, мой совет — уберите оттуда большую часть карт.
Преимущество рядов Фибоначчи в том, что с помощью них можно провести оценку больших историй. Например, вы можете взять 1, 2, 3, 5 и 8, благодаря чему получите восьмикратный размер.
Можно еще взять карты 0, и?. В способе с паьцами можно вместо этих знаков показать палец вниз, палец вверх и открытую ладонь. Ноль означает «слишком незначительно, чтобы оценивать». С такими историями нужно быть осторожными! Возможно, стоит объединить несколько таких историй в одну побольше. Бесконечность () означает, что история слишком большая, чтобы провести ее оценку, а это значит, что ее нужно разделить. И знак вопроса (?) означает неизвестность — нам не от чего отталкиваться.
Разбиение, слияние и костыли
В слиянии историй нет ничего сложного. Можно просто скрепить карточки вместе и рассматривать эти истории как одну. Просто посчитайте сумму единиц сложности. Если есть истории, оцененные в ноль единиц, хорошо подумайте, как оценить их при сложении. В этом случае пять нолей не могут дать ноль в сумме.
Разбивка историй будет сложнее: нужно сделать так, чтобы истории соответствовали правилам их написания. В качестве простого примера разбиения истории рассмотрим вход. Если мы захотим разбить эту историю на несколько историй поменьше, то можем создать на ее месте вход без пароля, вход с одной попытки, вход с нескольких попыток и забыли пароль?.
История, которую нельзя разбить на несколько, — большая редкость. Особенно это касается тех крупных историй, которые не можно, а нужно разбивать. Помните, что работа программиста заключается в том, чтобы разбивать истории вплоть до отдельных строк кода. Поэтому разбиение возможно почти всегда. Самое сложное — соблюдать правила написания историй.
Костыль — это метаистория, точнее история для оценки истории. Ее называют костылем, так как она напоминает длинный тонкий клин, который пронизывает всю разработку программы насквозь, подпирая другую историю.
Допустим, есть история, которую у вас не получается оценить. Пускай это будет печать PDF. Почему вы не знаете, как ее оценить? Потому что до этого вы никогда не использовали библиотеку PDF и у вас нет точного представления, как она работает. Поэтому вы пишете новую историю, которую называете оценка печати в PDF. Теперь оцениваем уже ее, и ее-то оценить проще. Вы уже знаете, что нужно сделать, чтобы выяснить, как работает библиотека PDF. Обе истории отправляются в кипу с карточками.
На одной из последующих встреч по планированию заинтересованные стороны могут попробовать выбрать карточку печать PDF. Но не тут-то было — костыль встанет им поперек горла. Поэтому придется выбрать карточку с костылем. Это позволит разработчикам выполнить работу, необходимую для оценки первоначальной истории, которую можно провести в одной из следующих итераций.
Управление итерацией
Цель каждой итерации — получение данных посредством выполнения историй. Команде скорее следует сосредоточиться на историях, чем на задачах, которые под ними скрываются. Предпочтительнее выполнить 80 % историй, чем выполнить все истории на 80 %. Сосредоточьтесь на доведении историй до их выполнения.
Как только заканчивается совещание по планированию, программисты должны выбрать истории, за которые будут нести личную ответственность. В некоторых командах выбирают истории, находящиеся сверху, а остальные откладывают на потом, чтобы взяться за них по выполнении выбранных ранее. Как бы то ни было, программисты сами выбирают и выполняют истории.
Менеджеры и лидеры могут поддаться искушению назначать истории программистам. Но этого нужно избегать. Пускай программисты договорятся между собой.
Например:
Джерри (опытный спец):
— Если не возражаете, я возьму на себя вход и выход. Думаю, есть смысл выполнить их вместе.
Жасмин (опытный спец):
— Не вопрос, только почему бы вам вдвоем с Альбертом не взяться за базы данных? Он постоянно расспрашивает, как мы используем источники событий. Мне кажется, что вход даст ему некоторую ясность. Что скажешь, Альберт?
Альберт (стажер):
— О! Звучит круто! Я видел, как это делается. Я даже за выдачу наличных взяться могу!
Алексис (ведущий программист):
— А почему бы мне не взять выдачу наличных? Альберт! Давай ты поработаешь над ней вместе со мной. Потом возьмешь перевод.
Альберт:
— Угу, ладно. Наверное, так будет лучше. Кто сначала перекладывает камешки, потом передвигает горы, ведь так?
Жасмин:
— Ну конечно, Альберт! И остается внесение наличных. Я возьму это на себя. Алексис, мы с тобой в одной упряжке, нам нужно поработать над пользовательским интерфейсом, ведь они у нас, вероятно, мало отличаются. И мы должны иметь возможность делиться кодом.
В этом примере можно увидеть, как ведущий программист направляет новичка, амбициозного стажера, не давая ему взять на себя больше, чем он может, а еще то, как члены команды сообща выбирают себе истории в работу.
Контроль качества и приемочное тестирование
Если QA-специалисты еще не начали писать автоматизированные приемочные тесты, то нужно это делать как можно скорее, прямо после встречи по планированию. Тесты для историй, рассчитанные на быстрое выполнение, должны быть готовы как можно раньше. Не нужно ждать, пока приемочные тесты созреют для выполненных историй.
Приемочные тесты должны появляться быстро. Мы ожидаем, что их полностью напишут еще до середины итерации. Если к середине итерации готовы не все приемочные тесты, кто-то из разработчиков должен бросить работу над историей и помогать писать тесты.
Это означает, что за эту итерацию не удастся выполнить все запланированные истории, однако в любом случае без приемочного тестирования ни о каком выполнении не может идти речи. Только убедитесь, что программисты, работающие над какой-либо историей, не пишут приемочные тесты для нее же. Если специалисты по качеству каждый раз не успевают написать тесты к середине итерации, это, вероятнее всего, означает, что соотношение разработчиков и QA-специалистов выбрано неверно.
Если по достижении середины итерации все приемочные тесты написаны, то QA-специалисты должны приняться за тесты к следующей итерации. Это рискованно, поскольку встреча по планированию еще не состоялась, однако заинтересованные стороны могут дать рекомендации насчет историй, которые они выберут с наибольшей вероятностью.
Разработчики и QA-инженеры должны обсудить такие тесты. Мы не хотим, чтобы разработчики молча брали то, что им бросают QA-специалисты. Необходимо обсуждать структуру тестов и писать их сообща, вплоть до парного программирования.
До середины итерации команде нужно постараться выполнить истории, чтобы успеть к промежуточному совещанию. А до конца итерации разработчикам нужно постараться успеть подвергнуть оставшиеся истории соответствующим приемочным тестам.
Если мы говорим, что история выполнена, то подразумеваем, что она прошла приемочное тестирование.
В последний день итерации разработчикам предстоят муки выбора: какие истории стоит завершить, а от каких придется отказаться? Мы предпочитаем перераспределять усилия. Так получается выполнить столько историй, сколько возможно.
Опять же ни к чему заканчивать итерацию с историями, выполненными лишь наполовину, ведь есть возможность пожертвовать какой-то историей, чтобы полностью выполнить другую.
Здесь не нужна спешка. Нужны конкретные результаты и измеримость хода работ. Нужны надежные данные. Историю можно считать выполненной, когда пройдено приемочное тестирование. Когда программист говорит, что выполнил историю на 90 %, в реальности непонятно, насколько она готова. Поэтому на графике скорости работ стоит отмечать лишь истории, прошедшие приемочное тестирование.
Демонстрация
Каждая итерация заканчивается короткой демонстрацией новых функций заинтересованным сторонам. Такая встреча должна длиться не больше часа или двух, в зависимости от размера итерации. В демо-версии должно быть видно, что истории, в том числе все предшествующие, полностью прошли приемочное и модульное тестирование. Следует также показать свежийфункционал, добавленный к программе. Лучше всего, если заинтересованные стороны сами проверяют работоспособность программы, чтобы у разработчиков не было соблазна скрыть то, что не работает.
Скорость
Завершающий аккорд итерации — обновление графика скорости и диаграммы сгорания задач. На графике нужно отмечать единицы только тех историй, которые прошли соответствующие приемочные испытания. После нескольких итераций графики пойдут на спад. Спад диаграммы сгорания задач помогает выявить, какого числа будет достигнута следующая важная веха. График скорости же показывает нам, насколько хорошо организована работа команды.
Во время начальных итераций график скорости будет довольно неточным, так как команда еще толком не разобралась в основах проекта. Но после первых нескольких итераций точность графика возрастет и достигнет уровня, позволяющего четче определить истинную скорость выполнения работ.
Мы ожидаем, что после нескольких первых итераций угол наклона приблизится к нулю, то есть график примет горизонтальный вид. Мы не ждем от команды ускорения или замедления в долгосрочной перспективе.
Возрастание скорости
Если мы видим, что график пополз вверх, это вряд ли означает, что команда действительно стала работать быстрее. Скорее всего, это означает, что менеджер проекта выжимает из разработчиков все соки, подгоняя их. Как только руководство прибегает к давлению, то команда бессознательно начинает мухлевать с оценками, чтобы создать впечатление, будто стала работать быстрее.
Это обычное надувательство. Единицы сложности можно представить как своеобразную валюту, которая обесценивается командой из-за напряжения, создаваемого извне. Вернемся к такой команде через год, и что мы увидим? Этих единиц за итерацию у них будет больше 9000! Мораль такова, что не нужно гнаться за скоростью — это всего лишь способ измерения. Зарубите себе на носу: нельзя подгонять то, что измеряешь.
Оценка итерации на встрече по планированию проводится лишь затем, чтобы дать знать заинтересованным сторонам, сколько историй возможно выполнить. Она помогает выбирать истории и планировать их выполнение. Однако такая оценка не является обязаловкой — ни о какой провальной итерации не идет речи, если скорость вдруг оказалась ниже. Помните, что только та итерация провальна, из которой не удалось получить данных.
Снижение скорости
Если график скорости неуклонно ползет вниз, то, скорее всего, причина в качестве кода. Команда, вероятнее всего, проводит мало рефакторинга, и код начинает портиться. Одна из причин нехватки рефакторинга — команда пишет недостаточно модульных тестов и боится, что из-за рефакторинга перестанет работать то, что работало прежде. Борьба с этим страхом — важнейшая задача лидеров команды разработчиков, она полностью зависит от дисциплины при тестировании. Подробнее об этом дальше.
Когда скорость падает, усиливается давление на команду. Это ведет к обесцениванию единиц сложности. И за таким раздуванием единиц может скрываться падение скорости выполнения проекта.
Золотая история
Один из способов избежать обесценивания единиц сложности историй — постоянно сравнивать истории с первоначальным «золотым эталоном», историей, с которой сравнивают все остальные. Вспомните, нашей первоначальной «золотой» историей был вход, который мы оценивали в 3 единицы. Если новая история, например исправить опечатку в меню, оценена в 10 единиц, то очевидно, что оценка этой истории раздута.
Небольшие частые релизы
Согласно методу «небольшие и частые релизы», команде разработчиков нужно выпускать релизы своего программного обеспечения как можно чаще. В конце девяностых, когда Agile только появился, мы думали, что норма — это выпуск релиза раз в месяц-два. Но сейчас этот срок стал гораздо короче. Сокращать срок можно до бесконечности. Увеличение частоты релизов происходит благодаря непрерывной доставке (continuous delivery). Этот метод заключается в том, что релиз происходит после каждого изменения кода.
Это толкование может ввести в заблуждение, потому что выражение «непрерывная доставка» создает впечатление, что мы хотим сделать короче только цикл доставки. Это не так, мы хотим сократить каждый цикл.
К сожалению, в силу исторических обстоятельств мы имеем препятствие в виде некой значительной инерции. Эта инерция — рудимент тех способов, к которым мы прибегали при работе с исходным кодом, когда трава была зеленее, а деревья выше.
Краткая история управления исходным кодом
История управления исходным кодом — это повесть о циклах и их размерах. Она берет начало в 1950–1960-х годах, когда исходный код хранили в отверстиях, пробитых на кусочках бумаги (рис. 3.3).
Рис. 3.3. Перфокарта
Многие из нас тогда пользовались перфокартами. Карта вмещала на себе 80 символов и представляла собой одну строку программного кода. Сама программа занимала целую колоду таких карт. Обычно их перетягивали резинкой и хранили в коробке (рис. 3.4).
Рис. 3.4. Колоды перфокарт в коробке
Владелец программы хранил колоду перфокарт в выдвижном ящике или шкафчике. Если кому-то нужно было проработать исходный код, это приходилось делать прямо из ящика или шкафчика с позволения владельца.
Если у вас получилось проверить исходный код, то вы были единственным, кто мог внести в него изменения, поскольку имели возможность физически посмотреть перфокарты. Больше никто не мог их касаться. Когда дело было сделано, нужно было отдать колоду владельцу, который клал ее в ящик или шкафчик.
Цикл для этой программы составлял столько, сколько времени у программиста был к ней физический доступ. Счет мог идти на дни, недели, месяцы.
Ленты
В 1970-х мы плавно перешли к тому, что стали хранить образы перфокарт с исходным кодом на магнитной ленте. На магнитной ленте можно держать большое количество программных модулей, а еще ее было проще копировать. Редактирование модулей выглядело так:
1. Достать главную ленту с оригиналом из главной стойки.
2. Скопировать модули, которые нужно отредактировать, с главной ленты на ленту с рабочей копией.
3. Положить главную ленту на место, чтобы другие могли получить доступ к прочим модулям.
4. Воткнуть цветную кнопку на доску выдачи рядом с названием модулей, которые нужно отредактировать. (У меня была синяя, у начальника красная, у остальных программистов из моей команды были желтые. Да-да, в конце концов у нас закончились цвета.)
5. Редактировать, компилировать и тестировать на ленте с рабочей копией.
6. Снова достать главную ленту.
7. Скопировать измененные модули с ленты с рабочей копией на главную ленту.
8. Положить обновленную главную ленту в стойку.
9. Вынуть кнопку из доски.
И снова цикл составлял ровно столько, сколько кнопка находилась на доске выдачи. Это могло занимать часы, могло дни и даже месяцы. Покуда кнопка находилась на доске выдачи, никто больше не мог обращаться к модулям, которые вы закрепили за собой.
Разумеется, эти модули были и на главной ленте, и в крайнем случае кто-нибудь мог в обход правил отредактировать модули непосредственно на ней. Кнопки были условным соглашением, но никак не физической преградой.
Диски и системы управления исходным кодом
В 1980-х годах исходные коды переместились на диски. Поначалу еще использовалась доска выдачи и кнопки, но потом начали появляться настоящие средства управления исходным кодом. Первое из того, что я помню, — это Система управления исходным кодом (SCCS). Она работала по тому же принципу, что и доска выдачи. Происходила блокировка модуля на диске, из-за чего никто не мог получить доступ к его редактированию. Такое блокирование называется пессимистическим. И снова то же самое. Длительность цикла зависела от длительности блокирования. Блокировка могла длиться часами, днями, а то и месяцами.
На смену Системе управления исходным кодом пришла Система управления редакциями (RCS), которая, в свою очередь, уступила место Системе одновременных версий (CVS). Во всех так или иначе применялась пессимистическая блокировка. Поэтому цикл по-прежнему длился долго. Тем не менее хранение данных на диске было куда удобнее, чем на магнитной ленте. При копировании модулей с оригинала на ленту с рабочей копией очень соблазнительно было сделать модули крупными сами по себе.
С другой стороны, диски позволяли нам стремительно уменьшать размер модулей. Множество маленьких модулей просто-напросто не вело к таким издержкам, как несколько крупных. За счет уменьшения модулей продолжительность цикла стала короче, поскольку количество времени, затрачиваемое на проверку модуля, также относительно уменьшилось.
Проблема состояла в том, что изменения в программе влекли за собой изменения во многих модулях. В случаях, когда модули были тесно связаны, на их проверку все еще уходило много полезного времени. Некоторые из нас научились отделять связанные модули, чтобы уменьшить эти сроки. А кто-то так и не научился.
Subversion
Потом пришло время системы Subversion (SVN). В ней появилось оптимистическое блокирование. Оптимистическое блокирование по сути никакое и не блокирование. Разработчик мог проверять модуль одновременно с другим разработчиком. Система позволяла отслеживать действия разработчиков и автоматически объединять изменения в модулях. Если система обнаруживала конфликт, когда два разработчика работали над одними и теми же строками кода, то вынуждала программиста сперва разрешить этот конфликт, прежде чем подтвердить принятые изменения.
Это значительно сократило продолжительность цикла до времени, требуемого на редактирование, компиляцию и тестирование последовательности небольших изменений. Связанность модулей еще представляла собой проблему. Тесно связанные модули замедляли цикл, потому что приходилось вносить изменения во много модулей одновременно. Однако программы, где модули не были так тесно связаны, проходили цикл гораздо быстрее. Сам срок проверки больше не служил ограничивающим фактором.
Git и тесты
В наши дни мы используем Git. Сроки проверки при использовании Git сошли на нет. Это понятие больше не существует. Напротив, любое изменение в модуль можно внести когда угодно. Программисты разрешают конфликты между этими изменениями, как и когда этого пожелают. Маленькие несвязанные модули позволяют молниеносно и часто вносить изменения. Поэтому циклы составляют считаные минуты. Добавьте к этому возможность создавать всеобъемлющие и быстрые тестовые наборы, которыми можно протестировать практически все. Вот вам и все нужное для непрерывной доставки.
Историческая инерция
К сожалению, организации с трудом отказываются от унаследованных подходов. Циклы продолжительностью в дни, недели и месяцы глубоко укоренились в культуре многих команд, откуда затем перешли к QA-специалистам, менеджерам и даже заинтересованным сторонам. С колокольни такой «культуры» мысль о непрерывной доставке может показаться бредом сумасшедшего.
Небольшие и частые релизы
Agile пытается порвать шаблоны исторической инерции, предлагая как можно сильнее сократить циклы релизов. Если у вас релиз происходит раз в полгода, постарайтесь выпускать раз в три месяца, раз в месяц, раз в неделю, в конце концов. Продолжайте уменьшать циклы релизов, асимптотически стремясь к нулю.
Чтобы достичь этого, организации требуется разорвать связь между релизом и развертыванием. Понятие «релиз» означает, что программа технически готова к развертыванию. Решение о развертывании полностью ложится на плечи клиентов.
Возможно, вы заметили, что теми же самыми понятиями мы описывали итерации. Итерации с технической точки зрения можно развертывать. Если продолжительность итерации составляет две недели, но мы хотим выпускать релизы чаще, придется итерации сократить.
Можно ли сокращать итерации асимптотически, стремясь к нулю? Да, можно. Но это уже тема отдельного разговора.
Приемочное тестирование
Приемочное тестирование — один из самых непонятных и запутанных методов Agile, который используется реже всего. Это любопытно, потому что основной посыл удивительно прост: требования указывает клиент.
Проблема, несомненно, в понимании слова «указывает». Многие клиенты считают, что могут, поводив руками в воздухе, расплывчато и туманно описать функционал, а потом произойдет чудо — разработчики сами догадаются обо всех мельчайших нюансах. А многие программисты хотят, чтобы клиенты точно объяснили, как должна работать программа, вплоть до координат и цвета каждого пикселя на экране.
Из этих двух крайностей нужно вывести золотую середину.
Что же такое спецификация? Спецификация по своей сути — это функция, которую можно протестировать. Например:
Когда пользователь вводит правильное имя учетной записи и пароль, а потом нажимает «войти», на экране появится страница приветствия.
Вот это и есть указание, то есть спецификация. Очевидно, что это можно протестировать.
Очевидно, что тест для этой функции можно автоматизировать. Нет причин на то, чтобы компьютер не мог удостовериться в выполнении указанного требования.
Вот так работает приемочное тестирование. Опыт подсказывает, что насколько это вообще осуществимо, требования к системе должны представлять собой автоматизированные тесты.
Погодите! А кто же пишет эти тесты? Первый абзац этого раздела отвечает на наш вопрос: требования указывает клиент. Тогда получается, что клиенты должны писать автоматизированные тесты, ведь так?
Погодите! Автоматизированные тесты должны быть написаны на каком-то формальном исполняемом языке. И это работа для программистов, не иначе! Получается, что программистам придется писать эти тесты?
Погодите! Если программисты будут писать тесты, то это будет не то, чего хочет клиент. Это будут технические тесты с множеством нюансов, понятных только программистам. Они не отражают ценность тестируемого элемента для клиента. Тогда автоматизированные тесты будет писать клиент? Ведь так?
Погодите! Если автоматизированные тесты будет писать клиент, то они не будут соответствовать технологии, которую мы используем. И программистам тогда придется их переписывать, не так ли?
Вот тут-то и становится понятно, почему этот метод вводит многих людей в заблуждение.
Инструменты и методологии
А еще хуже то, что наш метод погряз в инструментах и методологиях.
Пытаясь облегчить клиентам написание автоматизированных тестов, программисты написали целое изобилие инструментов, оказывающих медвежью услугу. Имеются в виду поделки вроде FitNesse, JBehave, SpecFlow и Cucumber. Каждый из этих инструментов предоставляет формы, призванные отделять техническую сторону автоматизированного теста от пользовательской. Гипотеза состоит в том, что клиент может написать пользовательскую часть автоматизированного теста, в то время как программисты могут написать код, связывающий эти тесты с тестируемой программой.
Звучит круто, и инструменты вроде как неплохо способствуют разделению труда. Тем не менее клиенты неохотно берутся за подобное. Представители компаний, ответственные за указание требований в спецификациях, побаиваются формальных языков. Они в своем большинстве предпочитают человеческие языки вроде русского или английского для написания спецификаций с требованиями.
Увидев, что клиенты ни в какую не хотят писать приемочные тесты, программисты решили исправить положение, и в надежде на то, что клиенты хотя бы прочитают документы, написанные формальным языком, уже сами написали для них тесты. Но и это не сработало, ведь клиенты терпеть не могут формальные языки.
Они предпочтут посмотреть на работающую программу или в лучшем случае доверят тестирование QA-специалистам.
Разработка через поведение
С наступлением нового тысячелетия Дэн Норт начал работу по пересмотру разработки через тестирование. Получившуюся методологию он назвал разработкой через поведение. Он поставил себе цель избавиться от жаргона технарей в тестах, чтобы тесты больше напоминали спецификации с требованиями, которые так любят клиенты.
Сначала это было еще одной попыткой формализации языка тестирования, в этом случае применялось три ключевых слова: «дано», «когда» и «тогда». Было создано или модифицировано несколько инструментов для поддержки этого языка. В их числе JBehave, Cucumber и FitNesse. Но с течением времени упор стал делаться не на инструменты и тесты, а на требования и спецификации.
Сторонники разработки через поведение предполагают, что клиенты могут извлечь большую пользу, указывая требования к своим программам на формальном, основанном на сценариях языке вроде того, который использует ключевые слова «дано», «когда» и «тогда», независимо от того, автоматизированы ли в действительности такие требования в виде тестов.
Это избавляет клиентов от соответствия техническим требованиям, которые налагает написание действительно исполняемых тестов, в то же время позволяя быть тестам формальными и точными.
Как показывает практика…
Несмотря на всю противоречивость и запутанность, которые мы увидели выше, в приемочном тестировании нет ничего сложного. Клиенты пишут формальные тесты, которые содержат описание каждой пользовательской истории, а разработчики эти тесты автоматизируют.
Эти тесты пишутся бизнес-аналитиками и QA-специалистами до завершения первой половины итерации, когда должны разрабатываться истории, которые впоследствии будут проходить эти тесты. Разработчики объединяют эти тесты в непрерывную сборку. Такие тесты соответствуют критериям готовности для историй, разработанных во время итерации. Требования к истории не считаются указанными, пока для нее не написан приемочный тест. История не считается завершенной, пока не пройдено приемочное тестирование.
Бизнес-анализ и контроль качества
Приемочные тесты — результат совместной работы бизнес-аналитиков, QA-специалистов и разработчиков. Бизнес-аналитики указывают, что должно происходить в лучшем случае. Они являются связующим звеном между заинтересованными сторонами и программистами и выражают их желание получить хороший продукт.
Специалисты по контролю качества, напротив, обрисовывают наихудший исход. Сейчас способов получить такой исход намного больше, чем раньше. QA-специалисты зарабатывают на хлеб тем, что вычисляют уязвимости программы. Технари до мозга костей, они способны предвидеть все финты и фокусы, которые может выкинуть программа. Они также знают, как мыслят программисты и как определить халтуру.
И, конечно, не обойтись без самих разработчиков. Они, работая со специалистами по качеству и бизнес-аналитиками, обеспечивают гарантии того, что тесты будут иметь смысл с технической точки зрения.
Контроль качества
Здесь, конечно, QA-специалисты играют уже более важную роль. Если сначала они выполняют тыловую работу тестировщиков, то теперь выходят на первый план, находясь на передовой линии проекта. В начале работ они дают обратную связь после написания кода, указывая на ошибки и упущения, а потом занимаются предотвращением этих ошибок, заблаговременно предоставляя необходимые данные разработчикам.
Нагрузка на специалистов по качеству многократно возрастает. Контроль качества теперь должен проходить в начале каждой итерации, а не выявлять недостатки и несоответствие на финальной стадии. Однако в любом случае важность контроля качества не преуменьшается — именно инженеры по качеству определяют, можно ли развернуть систему.
Тесты в конце бесполезны
Проведение контроля качества и автоматизация тестирования решают еще одну важную задачу. Когда специалист по контролю качества проводит тестирование в конце, да еще и вручную, он попадает в щекотливое положение.
Работа должны быть выполнена до развертывания программного обеспечения. Но менеджерам и заинтересованным сторонам просто не терпится скорее начать развертывание, и они наседают на QA-инженеров.
Когда контроль качества переносится на финал проекта, весь удар берут на себя специалисты по качеству. А если разработчики отдадут продукт на проверку с опозданием, будет тогда перенос сроков? Часто сроки определяются очень важными деловыми причинами, поэтому перенос будет дорого стоить или даже в корне расстроит планы клиента. В итоге козлом отпущения остаются QA-специалисты.
Как специалистам по качеству тестировать программу, если на это не остается времени в графике работ? Как им выполнить работу быстрее? Очень просто: пропустить некоторые тесты. Протестировать только то, что изменилось. Провести анализ воздействия на функции, которые появились недавно или претерпели изменения, а потом протестировать уязвимости. Не тратить время на тестирование того, что не изменилось.
Так пропускаются необходимые тесты. Под давлением сроков QA-специалисты пропускают все регрессионные тесты, надеясь, что проведут их в следующий раз. Но чаще всего «следующий раз» не наступает.
Болезнь контроля качества
Все перечисленное выше не самое худшее, что происходит с контролем качества на финише. Если контроль качества проводится в конце, как клиенты узнают, хорошо ли выполняется работа? Естественно, по количеству выявленных недочетов. Если специалисты обнаруживают тонну ошибок, то они явно не просто так получают зарплату. QA-специалисты могут завышать количество таких ошибок, чтобы показать, как замечательно справляются со своими задачами.
И считается, что раз что-то найдено, значит, все хорошо.
Кому еще выгодны недочеты? Среди старых программистов есть такая присказка: «Уложусь-то я в любые сроки, а как оно будет работать — это уже другой разговор». Так кто еще выигрывает от найденных недочетов?
Разработчики, которым нужно уложиться в сроки.
И здесь не нужны слова. Не требуется никаких договоренностей. Обе стороны понимают, что только выиграют. Начинается черная торговля недочетами. Эта болезнь свойственна многим компаниям, она не то чтобы смертельна, но весьма изматывает.
Разработчики в роли тестировщиков
Эти проблемы лечатся методом приемочного тестирования. QA-специалисты пишут приемочные тесты для историй, выполненных за итерацию. Но само тестирование проводят не они. Не QA-специалисты должны проводить тестирование программы. Тогда кто? Программисты, конечно!
Это работа программистов. Разработчики ответственны за подтверждение того, что их код проходит все тесты. Поэтому проведение тестирования, безусловно, работа программистов. Проведение тестов — единственный способ проверить, выполнены ли истории.
Непрерывная сборка
А будет так, что программисты автоматизируют тестирование[40], установив сервер непрерывной сборки. Каждый раз, когда программист запускает проверку какого-нибудь модуля, сервер будет запускать необходимые программы, с помощью которых будут проводиться тесты, в том числе все модульные и приемочные. Подробнее о непрерывной интеграции далее в этой книге.
Одна команда
В экстремальном программировании практика «одна команда» изначально называлась «заказчик всегда рядом» (On-Site Customer). Идея заключалась в том, что чем меньше дистанция между пользователями и программистами, тем лучше коммуникация, и разработка таким образом становится быстрее и точнее. Заказчик был метафорой кого-то или группы людей, которые понимали потребности пользователей и были рядом с командой разработчиков. В идеале клиент сидел в одной комнате с командой.
В Scrum заказчик называется «владелец продукта». Это человек или группа, которые выбирают истории, ставят приоритеты и дают своевременную обратную связь.
Метод переименовали в «одна команда», чтобы стало ясно, что команда разработчиков — это не дуэт «заказчик — программист». В команде разработчиков вклад вносят все, в том числе руководители, тестировщики, разработчики технической документации и т. д. Цель этого метода — в наибольшей степени улучшить контакт между всеми участниками. В идеале все участники должны сидеть в одной комнате.
Вряд ли вызывает сомнения, что нахождение всей команды в одном пространстве увеличивает ее эффективность. Команда может общаться быстро и с минимум формальностей. Ответ на вопрос можно получить за несколько секунд. Рядом всегда есть опытные товарищи, которые могут подсказать.
Более того, это повышает вероятность непреднамеренных интуитивных открытий. Представитель заказчика всегда может посмотреть в экран программиста или тестировщика и заметить, что происходит что-то не так. Тестировщик может случайно услышать, например, что программисты, работающие в паре, обсуждают требования, и понять, что они пришли к неверному выводу. Такой синергетический эффект нельзя недооценивать. Когда одна команда работает в одном пространстве, происходят чудеса.
Обратите внимание, что этот метод относится к методам взаимодействия с клиентом, а не к методам взаимодействия внутри команды. Основные выгоды от метода «одна команда» получает клиент.
Когда команда находится в одном пространстве, дело идет слаженнее.
В одном пространстве
В начале 2000-х я помогал некоторым организациям внедрить методы Agile. Во время предварительных визитов, до того как начинать активный коучинг, мы попросили наших клиентов подготовить пространство и расположить в нем всех членов команды. Заказчик неоднократно сообщал, что эффективность команд заметно возросла просто потому, что ее члены находились в одном пространстве.
Размещение другим способом
В 1990-х интернет открыл широкие возможности использования труда программистов в странах с очень низкой стоимостью рабочей силы. Искушение воспользоваться этой возможностью было бешеным. Бухгалтеры делали расчеты и с горящими глазами представляли, сколько средств можно было сэкономить.