Рис. 7.12. Все практики взаимосвязаны и усиливают друг друга
Мы отложили рассмотрение целостных практик до главы 7, потому что в сочетании с другими основными ХР-практиками они становятся понятнее, и команда не сможет освоить их по-настоящему, не имея образа мыслей, совместимого с ХР-ценностями и принципами. Если рассматривать практики по отдельности и реализовывать их одну за другой, то, скорее всего, получится результат «лучше-чем-ничего», который не окажет глубокого влияния на архитектуру продукта.
В хорошей XP-команде каждый усвоил принципы и ценности, поэтому практики формируют экосистему, в которой они усиливают друг друга. Создание тестов до написания кода помогает писать небольшие, независимые, несвязанные модули, благодаря которым проблемы в коде и антипаттерны проще исправлять.
Члены команды работают в паре, что помогает им стать единой командой, заставляет быть более собранными, создает энергичную рабочую среду и дает время обдумать архитектуру. Они постоянно занимаются интеграцией, поэтому, когда одна пара вносит в код дефект, влияющий на другую часть программы, модульное тестирование быстро выявляет ошибку. Это позволяет команде быстро устранить проблемы еще до того, как они окажутся погребены под дополнительными слоями кода, и сделать его чище. Непрерывная интеграция не представляет сложности, потому что существует 10-минутная сборка, которая, помимо прочего, облегчает команде возможность сосредоточиться и позволяет меньше отвлекаться.
Разработчику с правильным ХР-мышлением все это кажется очень простым. Он считает, что просто создает ПО именно так, как надо, а все эти принципы реализуются автоматически.
Команда, не обладающая таким мышлением, будет натыкаться на барьеры при внедрении ХР. Она застрянет в реализации отдельных практик и почувствует, что внедрила ХР, как только все начнут писать тесты, кодировать в парах, работать недельными циклами или использовать сервер непрерывной интеграции. Это простые, осязаемые вещи, выполнение которых можно отметить галочками в списке. Но существует разрыв между применением практик и получением эффекта «экосистемы», о котором XP-команда, возможно, читала, но так и не достигла. Так же как scrum-команды, реализовавшие все практики, но так и не добившиеся поразительных результатов или гиперпродуктивности.
Как команде сформировать правильное мышление? Как совершить переход от реализации практик к коренным образом измененным способам проектирования и создания программного обеспечения?
Эндрю. Итак, проектирование для повторного использования – это не всегда здорово?
Ауке. Это не очень удобный подход к разработке программного обеспечения. Обычно, работая с открытым кодом, ты сначала создаешь нечто для одного пользователя.
Затем кто-то другой берет это решение и начинает приспосабливать к своим нуждам. И он может использовать его повторно. И как только вы сделали это – применили, а затем использовали повторно, – вы знаете, что в этих решениях общего, и можете выполнить рефакторинг общей функциональности.
Вы видите довольно много таких случаев. Проектирование для повторного использования слишком статично. Оно не работает таким образом и очень плохо для этого подходит. Его можно пытаться к чему-то применить, но это не имеет особого смысла в обозримом будущем. Повторное использование подходит для разработки гораздо лучше.
Мы уже говорили о том, как команда может начать формировать правильное мышление для XP, внедряя его практики.
Во-первых, она могла бы получить результаты «лучше-чем-ничего». Но в ходе использования практик люди начинают понимать, как те взаимодействуют, и представление каждого члена команды о том, как создавать ПО, начинает меняться. Это справедливо не только для Scrum, но и для ХР. И ключ к обладанию ХР лежит в инкрементальной архитектуре.
Набор утилит Unix – классический пример инкрементальной архитектуры, и это то, что позволило тысячам разработчиков добавлять к нему маленькие и большие части на протяжении многих лет. Утилиты Unix состоят из множества маленьких кусочков[69], разработанных различными программистами, намеревавшимися решить свои конкретные проблемы (они не старались создать огромную, монолитную операционную систему). На примере набора утилит Unix мы рассмотрим, как инкрементальная архитектура может помочь множеству людей (большинство из которых никогда не встречались) внести свой вклад в создание большой, устойчивой, высококачественной системы, продолжающей развиваться на протяжении десятилетий.
Вот пример того, как работают утилиты Unix. Допустим, у вас есть масса больших текстовых файлов с множеством данных – может быть, это адреса для списка рассылки, конфигурационные файлы операционной системы или числа, которые должны быть обработаны. Нужно быстро придумать способ их преобразовать (например, вытащить только имена и номера телефонов, изменить определенные разделы конфигурации нужным образом или найти значения данных, соответствующих шаблону). Как бы вы это сделали?
Можно написать программу, которая считывает данные из файла, обрабатывает строки, разделы, последовательности и формирует выходные данные. Но если вы знакомы с Unix-утилитами (такими как cat, ls, cut, awk, sed и т. д.), то знаете, что они созданы для решения подобных проблем. Вы пишете скрипт (или даже просто команду, которую выполняете в командной строке), считывающий данные из файла, выполняющий преобразования и записывающий результаты в другой файл.
Например, у вас есть большая, разделенная запятыми адресная книга в файле addr.txt. В ней восемь столбцов: имя, должность, адрес электронной почты, номер телефона, почтовый адрес, город, штат и почтовый индекс. Вы хотите найти имя, должность и номер телефона адресатов, проживающих в Миннесоте. Если полное имя и должность записаны в первых двух столбцах, номер телефона – в четвертом, а штат и почтовый индекс в последних столбцах, то эта команда в Unix будет производить правильный вывод и записывать его в файл output.txt[70]:
Разве Unix-утилиты создавали люди, желавшие упростить процесс обработки адресных книг? Конечно, нет. Утилиты для Unix основаны на философии простоты: каждая выдает результат, который любая другая утилита может использовать в качестве входных данных, и каждая делает одну конкретную работу. Утилита cut последовательно читает строки ввода, вырезает определенные поля или столбцы и вставляет их в выходные данные. Утилита командной строки grep проверяет каждую строку на входе и передает на выход только те, которые соответствуют заданному шаблону. Но эти две утилиты, особенно в сочетании с другими средствами Unix, могут выполнять практически неограниченное количество задач. Например, системный администратор может комбинировать их с такими утилитами, как find (она ищет файлы в файловой системе) или last (называет пользователей, последними работавших на этом компьютере), чтобы выполнить множество сложных задач системного администрирования.
Эти утилиты – основа того, благодаря чему Unix стала наиболее популярной операционной системой для интернет-сервисов и бизнес-применений. И в течение многих лет общая архитектура системы утилит развивалась среди людей, использующих эти утилиты каждый день, вместе с культурой системного администрирования. Со временем появлялись новые утилиты и возможности их использования. Так, сжатие файлов получило широкое распространение в 1980–1990-х годах. Когда в 1992 году gzip добавилась в набор утилит для Unix, для этого уже существовал простой шаблон. Еще до написания первой строки было ясно, как она будет получать данные и отдавать их (конвейер), выполняться (из командной строки) и даже в каком виде появится документация (страницы man). Утилиты могут легко расширяться, а поскольку все инструменты не связаны друг с другом, возможность сжатия и распаковки файлов легко добавляется, не требуя каких-либо изменений в других частях системы.
Уже более сорока лет расширение набора утилит Unix происходит именно таким способом. Тысячи разработчиков внесли свой вклад в отдельные утилиты или улучшили существующие. Unix-система росла естественным образом. А вместе с ней развивались культура и база знаний. Это позволило людям использовать Unix для выполнения все более сложных задач, что, в свою очередь, помогло найти новые способы ее совершенствования.
Все утилиты Unix придерживаются очень строгого шаблона входных и выходных данных.
Символ «|» в командной строке обозначает конвейер, он передает выходные данные одной утилиты и вход другой. Символ «<» выполняет ввод из файла, а символ «>» – вывод в файл. Все стандартные утилиты Unix создают выходы, работающие с этими конвейерами, – такова часть договора, которого каждая из них придерживается. Договор между утилитами Unix очень простой, что позволяет легко расширить всю систему. Пока каждая дополнительная утилита придерживается того же договора, она будет соответствовать другим утилитам.
Важный инструмент для оказания помощи команде в создании системы, использующей поэтапный подход, – это очень простой договор между ее модулями. Как они взаимодействуют друг с другом, передают ли сообщения? Вызывают функции или методы? Предоставляют ли услуги доступа в сети? Чем проще и последовательней коммуникационный механизм между модулями, тем легче команде добавить новые.