Разработка через тестирование позволяет убедиться лишь в том, что каждый отдельно взятый модуль работает. Это также помогает командам предотвратить некоторые из самых распространенных и серьезных проблем, которые могут создать трудности в ходе поддержки кода. Можно внести изменения в одну часть кода и обнаружить, что неожиданно появились ошибки в другой части, которая, казалось бы, никак не связана с первой. И это только потому, что разработчик не подозревал о существовании зависимости между ними. Когда программист пишет модульные тесты, использующиеся каждый раз при сборке кода, эти автоматизированные тесты помогают избежать появления зависимостей, которые, оставшись невыявленными, приведут к немедленному сбою. Тесты помогают программисту заметить проблему, прежде чем она окажется встроенной в общий код и ее будет трудно удалить. Кроме того, модульные тесты помогают разработчикам писать код, который легче использовать повторно. Например, программист может создать класс Java, работа с которым чрезмерно усложнена: использовать в нем сбивающие с толку имена, неудобную инициализацию или другие структурные проблемы, которые смогут легко пробраться в код любого разработчика. Когда программист создает модульный тест, использующий такой класс, изъян может стать очевидным и, так как код класса еще не написан, разработчику несложно устранить этот изъян. Затем модульный тест становится частью исходного кода, так что другие программисты могут опираться на него, чтобы увидеть, как этот метод предполагалось использовать.
Вторая основная ХР-практика, ориентированная на написание кода, – это парное программирование. В команде, занимающейся парным программированием, два разработчика пишут код, сидя за одним компьютером. В большинстве случаев один программист сидит за клавиатурой, а другой наблюдает и они постоянно обсуждают, что делают. Команды, работающие в парах, вносят гораздо меньше ошибок, потому что за процессом внимательно следят две пары глаз. Парная работа также помогает уменьшить усталость, потому что один программист может печатать, когда другой устал. Эффективные команды парного программирования обсуждают идеи и подходы, постоянно устраивают мозговые штурмы и поэтому более инновационны. Они также несут ответственность друг перед другом за правильное применение практики: одному проще словчить, а под наблюдением партнера сделать это гораздо сложнее. Многие разработчики рассказывали нам, что программисты в парах пишут код быстрее и лучшего качества, чем работая по отдельности.
Существуют две основные XP-практики, которые мы относим к категории «интеграция». Первая – это десятиминутная сборка. Команда создает автоматизированную сборку для всей кодовой базы, которая длится 10 минут. Эта сборка включает в себя автоматический запуск всех модульных тестов и генерацию отчетов с положительными и отрицательными результатами.
«Десять минут» может звучать как произвольная величина, но с точки зрения команды она имеет смысл. Если сборка проекта занимает более 10 минут, то члены команды запускают ее гораздо реже. Но для команды очень важно использовать ее часто, потому что это быстро выявляет проблемы. Например, в сборке запускаются модульные тесты, поэтому после ее завершения команда знает, достигла ли она нужного качества.
Иными словами, она позволяет быстро получить ответ на вопрос «Работает ли еще наш код?». И благодаря тому, что сборка довольно короткая, команда будет запускать ее часто, а каждый член команды получит постоянно обновляемую картину качества кода.
Другая интеграционная практика – это непрерывная интеграция. Она реализуется благодаря набору инструментов, позволяющих нескольким членам команды одновременно работать с одной версией исходного кода. Если несколько членов вашей команды должны одновременно работать с одними и теми же файлами, то они не могут использовать одну физическую копию файла на всех. Иначе они бы постоянно затирали изменения друг друга. Поэтому команда будет применять специальную систему контроля версий, которая имеет централизованный (или децентрализованный) основной набор файлов. Отдельные разработчики будут выписывать из него, или копировать, нужные файлы, которые будут использоваться только этим программистом (что часто именуют помещением в так называемую песочницу). Разработчик будет трудиться над копией кода в своей «песочнице» и периодически отправлять изменения обратно в общее хранилище.
Проблема в том, что слишком часто разработчик, внося измененную версию программы, обнаруживает конфликт своего кода с изменениями, незадолго до этого внесенными другим программистом. В большинстве случаев этот конфликт выявляется сразу же при попытке выгрузить личную копию в общую базу кода, которая перестает компилироваться. Но временами проблемы оказываются более серьезными и скомпилированная программа ведет себя неправильно. Причина в том, что один код был заменен конфликтующим с ним кодом, взятым из другой «песочницы». Все эти проблемы проявятся, когда будет проведена интеграция системы из различных частей, написанных разными программистами.
И вот тут вступает в дело практика непрерывной интеграции: команда должна достаточно часто собирать код и отслеживать ошибки компиляции или сбои модульных тестов. Многие команды настроят сборочный сервер для периодической компиляции кода из хранилища, запуска полученной сборки и уведомления команды, если обнаружатся ошибки. Но создание сервера непрерывной сборки – это лишь одна часть непрерывной интеграции. Непрерывная интеграция означает, что каждый член команды постоянно содержит свою копию исходного кода в актуальном состоянии. Члены команды периодически интегрируют последнюю версию кода из хранилища обратно в свои «песочницы». Команды, применяющие парное программирование, порой пользуются физическим маркером сборки. Это может быть плюшевая или резиновая игрушка (забавная и заметная, чтобы сразу было видно, у кого она находится). Она переходит от пары к паре. Когда пара получает этот символ, наступает ее очередь интегрировать последнюю версию кода из хранилища к себе, исправить все найденные проблемы интеграции, а затем передать маркер сборки следующей паре. Это гарантирует, что проблемы интеграции будут обнаружены на ранних стадиях и исправлены.
Для управления своими проектами XP-команды используют итеративную разработку. Как и в Scrum, практики XP-планирования основаны на большом цикле долгосрочного планирования, который разбит на короткие итерации. В практике недельного цикла XP-команды используют итерации длиной в одну неделю совместно с практикой историй (это то же самое, что и пользовательские истории, о которых вы уже знаете). Каждый цикл начинается с совещания, посвященного планированию, где члены команды проводят обзор достигнутых результатов, работают с клиентами, чтобы выбрать истории для итерации, а затем разбивают их на задачи, которые оцениваются и передаются разработчикам.
Это вам уже знакомо, потому что это очень похоже на scrum-планирование. Действительно, многие XP-команды в точности перенимают практики scrum-планирования (которые считаются популярными гибридами Scrum и ХР, описанными в отчете VersionOne State of Agile, упоминавшемся в главе 2). Закончив планирование, команда посвящает первую часть итерации написанию автоматических тестов для историй и задач, а остальное время итерации пишет код, способный пройти эти тесты. Но вместо процесса самоорганизации некоторые XP-команды помещают все задачи на данную итерацию в очередь. Каждый разработчик берет следующую задачу из этой очереди после завершения текущей. Это гарантирует, что разработчики не выбирают свои любимые задачи и они распределяются равномерно между всеми.
Для долгосрочного планирования XP-команды используют практику квартального цикла. Раз в квартал команда собирается, чтобы проанализировать ход работ над проектом. На этих встречах XP-команды обсуждают темы, то есть те идеи из реального мира, которые они могут использовать, чтобы связать воедино истории своего проекта. Такие дискуссии помогают выяснить, какие истории необходимо добавить в проект, и поддерживают связь команды с реальными проблемами бизнеса, которые должно решить программное обеспечение. Также обсуждаются внутренние и внешние трудности, которые испытывает команда, повторяющиеся ошибки и еще не внесенные исправления. Оцениваются результаты, которых добилась команда, насколько хорошо удалось удовлетворить потребности клиентов и как в целом продвигается проект. Некоторые XP-команды склонны использовать ту же практику ретроспектив, что и scrum-команды.
И последняя из ХР-практик, посвященных планированию, – временной запас. Она позволяет команде добавлять мелкие, менее приоритетные истории в каждый недельный цикл. Во время планирования эти истории разбивают на задачи, но к ним не приступают до конца итерации. И тогда, если команда сталкивается с неожиданными проблемами, она исключает из итерации эти «запасные истории» и все-таки поставляет работающее ПО в конце цикла. Как и в других методах итеративной разработки, XP-команды поставляют в конце итерации только полностью законченный функционал. Это означает, что он работает, все тесты пройдены и его можно продемонстрировать пользователям.
XP затрагивает как программирование, так и совместную работу команд. Эта методика включает в себя две основные практики, помогающие командам сплотиться, – «командные практики». Первая – коаллокация, когда команда располагается в одном помещении. Люди в команде работают лучше, когда сидят рядом друг с другом и могут легко коммуницировать. Многие люди не осознают, что, хотя программирование – это индивидуальный труд и зачастую выполняется в одиночку (за исключением парного программирования), работа в команде разработчиков требует высокой социальной активности.