Practices of an Agile Developer Working in the Real World Venkat Subramaniam Endy Hunt
Этюды на тему быстрой разработ...
347 downloads
1727 Views
10MB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
Practices of an Agile Developer Working in the Real World Venkat Subramaniam Endy Hunt
Этюды на тему быстрой разработки программного обеспечения Работа в реальном мире Венкат Субраманиам Энди Хант
Venkat Subramaniam, Endy Hunt Practices of an Agile Developer Working in the Real World ISBN 0-9745140-8-X © Венкат Субраманпам, Энди Хант Этюды на тему быстрой разработки программного обеспечения Работа в реальном мире
Переводчик Е. Лукач Корректор Л, Белая Верстка Ю. Кунашовой © Издательство “Лори”, 2009 Изд. ХЬ ; OAJ (03) ЛР № : 07612 30.09.97 г. ISBN 978-5-85582-299-1 Подписано в печать 01.01.2009 Формат 70x100/16 Бумага о ф с е т «N? ] . Гарни тура Баскервиль. Печать офсетная Печ. л. 13. Тираж 1000. Заказ № 45 Цена договорная
Издательство “Лори” Москва, 123100, Шмитовский пр., д. 13/6, стр. 1 (пом. ТАРП ЦАО) Телефон для оптовых покупателей: (495) 256-09-83 Размещение рекламы: (495) 259-01-62 www.lory-press.ru Отпечатано в типографии ООО “Тиль-2004” 107023 Москва, ул. Электрозаводская, д. 21
Отзывы читателей о книге “Этюды на тему быстрой разработки программного обеспечения” Разделы “На что это похоже” просто бесценны: одно дело —рас сказать о том, что нужно делать, и совсем другое —применить все эт о на практике и быть уверенным в том, что вы все делаете пра вильно. Натаниэль Т. Шутта, Один из авторов книги “Принципы Ajax” В этой книге есть все, что читатель ждет от серии Pragmatic Bookshelf (Книжная полка прагматика): она небольшая по объему, легко читается, глубокая, по-настоящему полезная; авторы докапы ваются до самой суги. О на будет важным подспорьем для людей, стремящ ихся освоить гибкие подходы к разработке ПО, Форрест Чанг, Ведущий разработчик ПО С первой страницы книги меня не отпускала мысль: “Вот здоро во, именно такой книги не хватало многим разработчикам". Я сразу понял, что и мне ее не хватало. Я настоятельно рекомендую ее раз работчикам любого уровня знаний и опыта. Гуэрри А. Семонес, Старший инженер ПО, Appisiry В книге авторы, руководствуясь здравым смыслом и собствен ным опытом, показали, почему следует серьезно рассмотреть воз можность применения гибких методик в проектах. Это именно та сугубо практическая, экспериментальная информация, которую наиболее трудно почерпнуть из книг. * Мэттью Джонсон, Ведущий инженер ПО
Н екоторы е из описанны х практик мне были известны и преж де, поскольку я читал и другие книги серии Pragm atic Bookshelf, по эта книга содерж ит много новых идей и представляет их в ясном, компактном, упорядоченном ф орм ате. Я бы настойчиво рекомен довал эту книгу начинающему разработчику или группе разработ чиков, желающ их стать “гибкими”. Скотт Сплавек, Старший инженер ПО
В условиях массового распространения гибких м етодик в отрас ли необходимо понимать, в чем же на самом деле заклю чается “гиб кость”. Эта книга в компактной и практичной ф о р м е указывает путь к ее достиж ению . Марти Хот, Инженер/архитектор ПО, Razorstream Возможно, вы и прежде слышали о гибких м етодиках и даж е спраш ивали себя, что нужно сделать, чтобы с каждым днем работа улучшалась. Мой ответ таков: прочитайте эту книгу и позвольте ан гелам наш ептать вам на ухо наиболее подходящ ие вам практики. Дэвид Лазаро Саз, Разработчик ПО Это, дей стви тельно, всесторон ний и притом верно нацелен ный и компактны й обзор основны х практик гибкой разработки. Ч то мне нравится более всего в этой книге —это то, что она не вы двигает на передний план какую-то одну agile-методологию , а ско р ее увязы вает общ ие для различны х м етодологий при н ц и п ы в единую систему. Э та книга нужна всем тем, кого интересую т б ы ст р ы е и надеж ны е пути создания “ч ер то вски ” хорош их п рограм м ны х продуктов. Мэттью Басс, Консультант в области ПО
Э то превосходное продолж ение T h e Pragm atic Program m er! Билл Клеб Научный сотрудник, NASA
Содержание Глава 1. ГИБКАЯ РАЗРАБОТКА ПРОГРАММНОГО О БЕСП ЕЧЕН И Я
1
Глава 2. НАЧАЛА Г И БК О С Т И .............................................................. 11 1 Работайте на результат.......................................................13 2 Быстрая правка не решает проблем.................................16 3 Критикуйте идеи, а не лю дей............................................19 4 К черту торпеды, идите вперед......................................... 25 Глава 3. ВЗРАЩИВАНИЕ ГИБКОСТИ................................................ 28 5 Всегда будьте в к у р с е ......................................................... 30 6 Повышайте уровень своей команды.................................34 7 Умейте вовремя разучиться.............................................. 37 8 Спрашивайте до тех пор, пока не поймете......................40 9 Почувствуйте р и т м ............................................................43 Глава 4. ДЕЛАТЬ, КАК ХОТЯТ ПОЛЬЗОВАТЕЛИ............................ 47 10 Позвольте заказчику принимать реш ения......................49 11 Дизайн должен направлять работу, а не диктовать 52 12 Обоснованно применяйте технологию........................... 57 13 Держите все наготове...................................... ..................60 14 Интегрируйте сразу, интегрируйте ч а с т о ......................64 15 Автоматизируйте процесс развертывания с р а зу ...........67 16 Наладьте регулярную обратную связь при помощи демонстрационных в е р с и й .............................................. 70 17 Используйте короткие итерации, постепенно добавляйте функциональность.........................................76 18 Фиксированные цены мешают выполнению обещ аний ............................................................................ 81 Глава 5. ГИБКАЯ ОБРАТНАЯ С В Я З Ь ............................................. 85 19 Посадите ангелов себе на п л е ч и ...................................... 87 20 Используйте код до с б о р к и .............................................. 92 21 Различия имеют значение.................................................97
22 23 24
Автоматизируйте приемочные испытания................... 100 Измеряйте фактическое продвиж ение......................... 103 Прислушайтесь к пользователям.................................... 106
Глава 6. ГИБКОЕ ПРОГРАММИРОВАНИЕ..................................... 108 25 Программируйте осмысленно и вы разительно........... 110 26 Говорите самим кодом........................................................116 27 Активно анализируйте все плюсы и м инусы .................122 28 Пишите код инкрементами...............................................126 29 Следите за простотой кода............................................... 128 30 Пишите связный код..........................................................131 31 Говорите, не спрашивайте...............................................135 32 Замените, сохраняя контракт......................................... 138 Глава 7. ГИБКАЯ ОТЛАДКА................................................................. 143 33 Ведите журнал р е ш е н и й ................................................. 145 34 Предупреждения фактически являются ошибками 148 35 Атакуйте проблемы в изоляции...................................... 152 36 Сообщайте о всех исключительных ситуациях........... 155 37 Обеспечьте содержательные сообщения об ошибках 158 Глава 8. ГИБКОЕ СОТРУДНИЧЕСТВО...........................................163 38 Наладьте регулярное очное общ ен и е........................... 165 39 Архитекторы должны писать код....................................169 40 Практикуйте коллективную собственность................... 172 41 Будьте наставником......................................................... 174 42 Позвольте другим найти о т в е т ...................................... 177 43 Регистрируйте лишь готовый к о д .................................179 44 Проводите ревизию кода................................................. 182 45 Информируйте д р у ги х .................................................... 185 Глава 9. ЭПИЛОГ: ДВИЖЕНИЕ К ГИБКОСТИ.............................187 Приложение А. Ресурсы..........................................................................194 А.1 Интернет-ресурсы............................................................ 194 А.2 Библиография.................................................................... 197
Д аж е если ты далеко ушел по неверному пути - поверни назад. Турецкая пословица
Глава 1
Гибкая разработка программного обеспечения Турецкая пословица, взятая в качестве эпиграфа, кажется простой и вполне очевидной —можно даже предположить, что такое прави ло всегда должно выполняться в процессе разработки ПО, По, увы, слишком часто разработчики, включая скромных авторов книги, продолжают идти по ложному пути в надежде, что в конце концов все у них получится неплохо. Возможно, верный путь пролегает где-то рядом. Возможно, избранный путь совсем не так плох, как кажется. Он даже временами удобен — если бы процесс создания кодов был бы столь же линейным и предсказуемым, как дорога, о которой говорится в пословице. К сожалению, это не так. Процесс разработки программного обеспечения больше похож на серф инг — та же динамичная, постоянно меняющаяся среда. Море всегда непредсказуемо, порой опасно, в его водах скрываю т ся акулы. Но серф инг потому и захватывает дух, что все волны раз ные. Каждая волна имеет свою уникальную форму и поведение в за висимости от того, где она находится: например, волна на песчаной отмели совсем не похожа на волны, разбивающ иеся о рифы. В процессе разработки программного обеспечения вы сталки ваетесь с новыми требованиями заказчика и реш аете различные задачи —они бесконечны и непостоянны, как морские волны. П о добно волнам, программные проекты принимаю т различную ф ор му и создают на вашем пути разнообразны е препятствия в зависи мости от области, в которой вы работаете, или типа прилож ения. И акулы различных мастей не заставляют себя долго ждать. Успех программного проекта зависит от профессиональных на выков, опыта и компетентности всей команды разработчиков. Как
и успешный серф ер, разработчик должен бы ть в хорош ей техниче ской форме, быть уравновеш енным и гибким. Гибкос ть (agility) в обоих случаях означает способность бы стро адаптироваться к лю бому повороту собы тий, будь то внезапно разбивш аяся волна или неожиданное изменение дальнейш его плана работы .
Манифест Agile-разработки ПО (The Agile Manifesto) (перевод взят на сайте www.silicontaiga.ru. — Прим. пер.) Мы находим лучшие подходы к разработке ПО, непосредственно участвуя в процессе разработки и помогая другим. В процессе работы мы пришли к тому, что для нас важнее: • Люди и их взаимодействие, чем процессы и средства • Работающее ПО, чем исчерпывающая документация • Сотрудничество с заказчиком, чем обсуждение условий контракта • Реагирование на изменения, чем следование плану То есть мы не ставим под сомнение важность пунктов справа, в то же время для нас гораздо важнее записанное слева. Более подробная информация размещена на сайте agilemanifesto.org.ru.
Дух гибкости Итак, что же такое гибкость на самом деле, и откуда б ерет начало движ ение гибкой разработки программного обеспечения? В ф еврале 2001 г. семнадцать заинтересованны х человек (вклю чая Энди Ханта) собрались на лыжном курорте в горах Ю ты, что бы обсудить новые тенденции в разработке П О, которы е еще не ф ормально называю тся “легковесны м и” процессами. Все мы были свидетелями неудач проектов из-за тяж еловесны х процессов, которы е используют чрезм ерно много артеф актов и приносят слишком мало реальной отдачи. Мало кто сомневался в том, что необходима новая методология, которая делала бы упор на наиболее важных аспектах разработки, а не на второстепенны х, отнимаю щ их слишком много времени и мало влияющ их на конеч ный результат. Эти семнадцать человек придумали терм ин agile (живой, под виж ный, гибкий) и выпустили Agile Manifesto (М анифест гибкой разработки), в котором изложили новый подход к разработке про граммного обеспечения, подход, которы й ставит во главу угла лю дей, сотрудничество, адаптивность и работаю щ ий код (начало Ма ниф еста приводится на врезке). Гибкий подход предполагает бы строе реагирование, взаимодействие людей и их нацеленность на осязаемые результаты
своего труда (работающий программный продукт). Б этом заклю чается суть гибкого подхода. Акцент на практической стороне про цесса разработки ПО предполагает переход от традиционных ме тодик, основанных на долгосрочном планировании и нескольких ключевых этапах работы, к более естественному, непрерывному стилю разработки ПО. Предполагается, что команда разработчиков (и те, кто с ней ра ботает) состоит из профессионалов, заинтересованных в позитив ных результатах своей работы над проектом. Они не обязательно должны быть уже опытными профессионалами, но проф ессио нальный подход является нормой для каждого члена команды, т. е. каждый хочет дать максимум того, на что он способен. Если у вас имеются проблемы, связанные с прогульщиками, без дельниками, откровенными вредителями, то вам, скорее всего, гибкие методики не подойдут. Вам нужны более жесткие средства, более медленные и менее продуктивные. В остальных случаях вы можете начать движение в направлении agile style. Это, прежде всего, значит, что вдо не откладывае те тести рова ние продукта на конечную стадию проекта. Н е откладываете интегрирование вашей системы на конец месяца и не приоста навливаете обратную связь с заказчиком и обработку новых т р е бований в то т самый момент, когда вы приступаете к написанию кода. Непрерывная, а не эпизодическая разработка
Н аоборот, вы непреры вно заним аетесь . этими аспектами на про тяжении всего жизненного цикла вашего проекта. На самом деле, поскольку программны й продукт по определению “доделать” невозможно, если он кем-то использует ся, нет уверенности в том, что в данном случае слово “проект* со храняет свой изначальны й смысл. Разработка программного про дукта — это непреры вны й процесс. О братная связь тож е непреры вна. Не нужно выжидать несколько месяцев, чтобы обна ружить проблему —вы узнаете о ней сразу, когда ее еще достаточ но легко устранить. И вы по ходу дела устраняете их — одну за другой. В этом вся суть. Идея непреры вной, последовательной разработки П о я в л я ет с я основополагающ ей для всех гибких методик. О на распространяет ся не только на жизненный цикл создания программных продук тов, но и на обучение персонала новым технологиям, сбор требова ний заказчика, развертывание продукта, обучение пользователей и многое другое. Эта идея охватывает все виды деятельности на всех уровнях.
Вкладывайте энергию
Почему так? Потому что создание программных систем —это сложный процесс: если вы откладываете что-то сущ ественное на более позднее время, это собы тие может либо вообще не случиться, либо случиться совсем не так, как хотелось бы, или же проблема будет постепенно разрастаться настолько, что вы просто потеряете кон троль над ней. О на неизбежно порож дает трение, и ситуация все более усугубляется, становится все более необратим ой. Единствен ный способ скомпенсировать трение в системе независимо от его природы —постоянно инж ектировать добавочную энергию в сис тему (см. раздел "Энтропия в программах” в книге “Программистпрагматик: Путь от подмастерья к мастеру” [НТОО J; изд-во “Л о р и ”, 2007). Есть мнение, что гибкие методики разрабо тки ПО —это своего рода маскировка для кризисного управления (crisis m anagem ent). Н о это не гак. Н еобходимость применять кризисное управление возникает там, где проблемы оказались настолько запущены и раз рослись до таких размеров, что необходимо бросить все остальное и немедленно искать пути выхода из кризиса. Это влечет за собой дальнейшую цепочку кризисов, и вот вы оказы ваетесь в порочном круге нескончаемого кризиса и паники. Как раз этого вам и нужно избежать. Вы, напротив, стрем итесь пресекать проблемы, пока они ещ е малы, исследовать новые подходы еще до того, как в них вложены серьезны е средства, и с готовностью признать свою неправоту, как только узнаете правду. Вам придется изм енить свое мышле ние, пересм отреть имею щиеся практики разработки кодов и всю работу вашей команды. Э то не так трудно, но поначалу весьма не привы чно.
Практика гибкости Можно определить гибкость (agility) следующим образом: Agileразработка программного обеспечение предполагает использова ние обратной связи с заказчиком в целях постоянной корректи ровки кода в условиях наиболее тесного сотрудничества между чле нами коллектива. К оротко поясним, что это значит на практике, и как прим ерно выглядит в ж изни гибкая команда разработчиков. Это сплоченная команда. Гибкая команда обы чно немногочис ленна или же состоит из нескольких небольших групп (порядка де сяти человек). Если есть возможность, они сидят вместе в одном подвале (камере для арестантов), пишут общий код и реш аю т об
щие задачи, связанные с его разработкой. Они тесно сотруднича ют с клиентом или заказчиком, который платит за данный про граммный продукт, и регулярно поставляют им обновленные версии системы по мере их готовности. Вы поддерживаете постоянную обратную связь с пользователя ми продукта и применяете автоматизированную непрерывную сборку и тестирование для своей системы. Работая над кодом, вы замечаете, что код требует постоянного совершенствования: его функциональность остается прежней, но вам приходится непре рывно перепроектировать различные его куски. Это называется рефакторинг (refactoring) и является частью текущей работы про граммиста —код невозможно по-настоящему “доделать”. Процесс работы над проектом во времени представляет собой итерации (iterations): небольшие временные интервалы (порядка одной недели), в течение каждого из которых определяю тся и за тем реализуются в коде новые свойства системы. Демо-версия про дукта после каждой итерации показывается заказчику для получе ния обратной связи (и для проверки правильности избранного вами пуги), после чего в полном масштабе поставляется всем поль зователям системы; чем короче итерации, тем лучше. С учетом вышесказанного остановимся более подробно на при менении гибких практик к отдельным составляющим процесса разработки ПО:
Глава 2: Начала гибкости. Разработка программного обеспече-' ния происходит в голове разработчика. В этой главе объясняется, что мы имеем в виду и как развить в себе способность к гибкому мышлению и поведению; тем самым закладывается прочный фун дамент для восприятия следующих глав. Глава 3: Взращивание гибкости. Гибкий проект не самоочеви ден. Для него нужны определенные ф оновы е условия, которые сами не являются частью процесса разработки, но ж изненно необ ходимы для здорового климата в коллективе разработчиков. Мы покажем, как обеспечить вашей команде и вам постоянный рост и движение вперед. Глава 4: Делать, как хотят пользователи. Даже хорош о напи санный код будет бесполезен, если он не учитывает нужды пользо вателей. Мы рассмотрим практики и технологии, которы е позво ляю т вовлекать пользователей в процесс разработки, активно использовать их опыт работы с системой и учитывать их реальные нужды в процессе создания кода. Глава 5: Гибкая обратная связь. Использование обратной свя зи для коррекции кода и самого процесса разработки —это то, что
позволяет гибкой команде плыть по течению там, где другие лома ются и тонут. Наиболее эффективна обратная связь от самого кода; в данной главе показано, как наладить эту обратную связь и как сти мулировать высокую производительность команды и постоянное движение вперед. Глава 6: Гибкое программирование. Обеспечение гибкости и высокой приспособляемости исходного кода к любым изменениям в будущем —важнейший фактор успеха гибкой модели. В данной главе приводятся некоторые испытанные на практике приемы, ко торые позволяют коду оставаться “чистым”, не иметь ничего лиш него и притом быть “мягким”, податливым; в конечном счете, у та кого кода будет мало шансов превратиться в монстра. Глава 7: Гибкая отладка. Поиск ошибок и отладка кода могуг растянуться на долгий срок и отнять непозволительно много вре мени. В данной главе приводятся советы, как сделать процесс от ладки более эффективным и сэкономить время. Глава 8: Гибкое сотрудничество. В конце концов, один гибкий разработчик может работать достаточно эффективно, но лишь до определенного предела. Если вы выходите за эти рамки, вам нужна команда гибких программистов. Мы продемонстрируем вам ис ключительно результативную практику, которая, как мы убеди лись на собственном опыте, помогает создать монолитную коман ду, а также другие практики, помогающие наладить каждодневное взаимодействие членов команды друг с другом и нацелить ее на дальнейшее развитие.
Дьявол и всякие мелочи Если вы пролистаете эту книгу, вы заметите фрагменты с подсказ ками, отмеченные картинкой, изображающей дьявола. Его “сове ты” искушают, склоняя к дурным и безответственным поступкам, например: “Иди вперед, выбери этот кратчайший путь. Он сэкономит твое время. Никто никогда не узнает об этом, а ты быстро покончишь с этой задачей и двинешься дальше. Воти все”.
фа
Некоторые его высказывания Moiyr показаться абсур как будто они взяты прямо из дильбертовых комиксов ^жочта Адамса (Scott Adams), изреченные архетипичным боссом “с ежи
ком на голове” (pointy-haired boss). По не забывайте, что мистер Адамс заимствует множество идей у своих преданных читателей. Инструментарий гибкой разработки Далее в тексте книги мы будем ссылаться на базовые инструментальные средства, которые могут использоваться в любых гибких проектах. Коротко охарактеризуем каждое из этих понятий на случай, если вы не знакомы с ними. Более подробную информацию по ним можно найти в литературе, список которой приводится в конце книги. Wiki. Это сайт (полное название — WikiWikiWeb), на котором все пользова тели могут редактировать содержимое (контент) и создавать ссылки на новые ресурсы с помощью Интернет-браузера. Среда Wiki созда ет прекрасные возможности для коллективной работы: каждый член команды разработчиков может динамически добавлять и менять контент по мере необходимости. Более подробно об этих ресурсах — см. книгу The Wiki Way [LC01]. Контроль версий. Все, что имеет отношение к сборке проекта, — исход ные коды, документация, иконки, сценарии, и т.д., — подлежит уче ту и контролю со стороны системы контроля версий. Удивительно, но многие команды до сих пор продолжают просто “сбрасывать” свежие файлы на сетевой диск общего доступа, хотя это сугубо не профессиональный подход. Pragmatic Version Control Using CVS [TH03] или Pragmatic Version Control Using Subversion [Mas05] — это подробные руководства по установке и использованию систем кон троля версий. Модульное тестирование. Использование кода в целях проверки его ра боты — главный источник обратной связи для разработчика. Позже мы поговорим об этом подробнее, но здесь отметим, что в вашем распоряжении всегда есть среды тестирования (frameworks), кото рые возьмут на себя большинство “хозяйственной" работы. Подроб нее о том, как проводится модульное тестирование, см. Pragmatic Unit Testing in Java [HT03] и Pragmatic Unit Testing in C# [HT04]; вы так же найдете полезные советы в JUnit Recipes [Rai04]. Автоматизация сборки. Локальная сборка на вашем компьютере, как и централизованная сборка системы для всей команды полностью ав томатизированы и воспроизводимы в любой момент. Поскольку сборка приложения происходит в постоянном режиме, она еще на зывается непрерывной интеграцией (continuous integration). Как и при модульном тестировании, вам доступно множество бесплатных, open-source или коммерческих продуктов, которые избавят вас от рутины. Подсказки и приемы создания автоматизированных систем сборки, включая применение лавовых ламп (lava lamps), приведены в Pragmatic Project Automation [Cla04]. Наконец, полезную информацию о том, как увязать все эти базовые сред ства и практики друг с другом, вы найдете в Ship It! [RG05].
Независимо от степени их нелепости, все эти “голоса” как опре деленный ход мыслей часто можно наблюдать в жизни или услы
шать —как от других людей, так и внутри себя. Всегда существуют соблазны, и некоторым из них мы все же пом аем ся, напрасно на деясь сэкономить свое время. В противовес шепоту искусителя в конце изложения каждой из практик мы даем слово вашему личному ангелу-хранитслю; по нашему мнению, к его советам, действительно, стоит прислу шаться: tfL -
Начинайте с самого трудного. Принимайтесь за решение наиболее трудных задач, простые задачи можно решить позже.
Поскольку реальность редко представляется черно-белой, для каждой из практик мы включили также разделы, в которых попы тались описать, какие ощущения должна вызывать конкретная практика, а также что нужно делать для ее успешной и сбалансиро ванной реализации. Они выглядят примерно так:
На что это похоже Здесь описывается, как конкретная практика должна “ощущаться” в результате ее применения. Если же, следуя данной практике, вы испытываете совсем другие чувства, то вам, возможно, стоит пере смотреть свой подход к ее реализации.
Сохраняйте равновесие Всегда есть вероятность, что вы либо недостаточно активно бу дете следовать какой-либо практике либо, наоборот, перегнете пал icy —и здесь вы найдете советы, как избежать и того и другого и застави ть данную практику работать на вас с максимальной от дачей. Любая прекрасная идея при неразумном ее использовании или же злоупотреблениях может стать вредной и даже опасной (слиш ком часто так называемые гибкие проекты терпели неудачу из-за того, что команда не сумела сохранить равновесие, применяя чу или иную гибкую практику). Мы постараемся показать вам, как по лучить реальную отдачу от гибких практик. Следуя этим практикам и эффективно применяя их в реальных проектах при сохранении равновесия, вы постепенно начнете замечать позитивные перемены как в проектах, так и в своей команде.
Вы будете следовать практикам гибкой разработки, и, что еще более важно, поймете принципы, лежащие в их основе.
Благодарности Каждую кишу, которую вы читаете, можно считать грандиозным проектом, в котором, помимо скромных авторов, участвует огром ное множество людей. Мам хочется поблагодарить всех тех, чьими стараниями эта книга состоялась. Спасибо Джиму Муру за обложку книги, а Ким Уимпсет —за пре восходную работу но редактированию рукописи (любые ошибки в текс те, очевидно, нужно считать результатом авторской правки в самый последний момент). Особая благодарность Йоханнесу Бродуоллу, Шаду Фаулеру, Стефану Дженкинсу, Билу Клебу и Уесу Рейзу за глубокие и полезные замечания. И, наконец, спасибо всем нашим рецензентам, не жалевшим своего времени и таланта, чтобы помочь сделать эту книгу лучше. Это Маркус Анви, Элдон Аламеда, Сергей Аникин, Мэттью Басс, Дэвид Бок, А. Лестер Бак III, Брэндон Кемпбелл, Форрест Чанг, Майк Кларк, Джон Кук, Эд Гиббс, Дэйв Гудлад, Рамамурти Гопал акришиан, Марти Хот, Джек Херрингтон, Рои Джефриз, Мэттью Джонсон, Джейсон Хилц Лафорг, Тод Литтл, Тед Ньюард, Джеймс Ньюкирк, Джаред Ричардсон, Фредерик Роз, Билл Рашмор, Дэвид Лазаро Саз, Нэйт Шутта, Мэтт Секошкс, Гарри Семонес, Брайан Слеттен, Майк Сток, Стивен Вайле, Лейф Уикланд и Джо Уинтер. Венкат:
Мне бы хотелось поблагодарить Дэйва Томаса за его удивитель ный дар наставника. Без его руководства, поддержки и конструк тивной критики это книга так и осталась бы просто великим замыс лом. Я счастлив, что моим соавтором стал Энди Хант; благодаря его участию я многое узнал. Он не только хорошо соображает в своей профессиональной сфере (факт, известный любому программисту-прагматику), его отличает необычайная выразительность язы ка и особый взгляд. На каждом этапе работы над этой книгой я вос хищался командой Pragmatic Programmers — именно они обозначили и затем заставили работать тот набор инструментов, технологий и, прежде всего, "сам подход к предмету, которые на шли отражение в этом издании. Спасибо Марку Гарби за его поддержку. Он замечательный друг, у него исключительное чувство юмора и гибкости. Огромное спа
сибо всем моим подонкам (я хотел сказать, друзьям), с которыми я имел удовольствие общаться в процессе работы над книгой; эго Бен Галбрайт, Брайан Слеттен, Брюс Тейг, Дэйв Томас, Дэвид Гири, Дион Алмер, Эйтан Сьюз, Эрик Хатчер, Гленн Вандербург, Ховард Льюис Шип, Джейсон Хантер, Джастин Гетланд, Марк Ри чардз, Нил Форд, Рамнивас Ладдад, Скотт Дэвис, Стью Халлоуэй и Тед Пьюард — я восхищаюсь вами! Спасибо Джею Циммерману (“гибкому драйверу”), директору NFGS, за поддержку и предостав ленную мне возможность поделиться своими идеями с его заказчи ками. Спасибо моему отцу, который обучил меня правильному набору ценностей» и маме —моей истинной вдохновительнице. Эта книга не смогла бы появиться, если бы не терпение и поддержка моей жены Кавиты, и моих сыновей Картика и Крупакара. Благодарю вас и люблю. Энди:
Я думаю, мы уже всем сказали спасибо, но мне хочется особо по благодарить Венката за его приглашение принять участие в этой книге. Я бы не дал своего согласия кому попало, но Венкат —один из тех, кто “был и участвовал”. Он знает, как все это работает на практике. Я благодарен тем, кто собрался в Сноуберде. Мы не изобрели гибкий подход, но совместными усилиями мы придали ему рост и могучее влияние в современном мире разработки программного обеспечения. И, разумеется, я благодарен своим родным и близким за под держку и понимание. После книги The Pragmatic Programmer мы проделали немалый путь, но проделали его с удовольствием. А теперь —начнем.
Выбирая начало пути, выбираешь и цель.
Гарри Эмерсон Фосдик
Глава 2
Начала гибкости Традиционно книги по методологии разработки программного обеспечения начинаются с перечисления ролей, общих для любо го проекта, за которым обычно следует список артефактов, кото рые вам придется подготовить к сдаче (документация, контроль ные таблицы, графики Гантта, и т.д.). После этого следуют Правила, примерно в таком формате: “Thou Shalt...” (“Вы не долж ны..Z1)1. Здесь вы не встретите ничего подобного. Agility (гибкость) приветствует вас, а на ее территории все вещи происходят немно го иначе. Например, одна весьма популярная методология разработки ПО предлагает для выполнения проекта примерно тридцать пять (!) различных ролевых позиций, среди которых архитектор, про ектировщик, программист, библиотекарь и т.д. Гибкие методы придерживаются иного подхода. Здесь есть только одна роль —раз работчик ПО. Это вы сами. Вы работаете в команде, тесно сотруд ничаете с заказчиком, создавая программные продукты. Гибкие методологии ставят во главу угла человека, а не графики Гантта или каменные скрижали. Процесс разработки ПО рождается не в схеме, не в интегриро ванной среде разработки (IDE) или в инструментах проектирова ния — он происходит в голове разработчика. Но не только он. Здесь находятся также ваши эмоции, офисная политика, самолю бие, индивидуальный опыт и многое другое. Поскольку все это пе ремешано, такие эфемерные состояния, как ваше отношение к че му-либо или общее настроение, могут играть решающую роль. Вот почему нельзя пренебрегать отношением —вашим и вашей команды — к конкретной работе. Профессиональное отношение 1 Или незабвенное: “The System shall,..” ("Система должна...")
нацелено на достижение позитивных результатов и для заказчиков проекта, и для команды, а также на профессиональный рост всей команды и каждого ее члена, и, в конечном счете, на успех. Всегда легче поставить перед собой гораздо менее возвышенные цели, и в данной главе мы обсудим, каким образом можно сохранять вер ность реальным ценностям. Несмотря на любые соблазны, вы го товы Работать на результат (см. ниже). Нехватка времени—типичная проблема для софтверных проек тов, и в спешке вы наиболее склонны выбрать кратчайший, но неразумный выход. Любой опытный разработчик скажет вам, что Б ы страя правка не реш ает проблем (о том, как избежать это го с самого начала, см. с. 16). У каждого из нас есть самолюбие. Некоторые (не будем назы вать имен) имеют, мягко говоря, “здоровую” порцию самолюбия. Когда нас просят решить какую-либо проблему, мы испытываем вполне определенное удовлетворение, гордость от ее решения. Но та же самая гордость порой мешает нам быть объективными. На верняка вы были свидетелями рабочих дискуссий, которые своди лись к обсуждению личностей, вместо обсуждения насущных про блем и путей их решения. Гораздо эффективнее кри ти ковать идеи, а не людей (см. с. 19). Обратная связь с пользователем —это фундаментальный прин цип всех гибких методологий; любую ситуацию нужно корректиро вать, как только обнаруживается, что она развивается в неверном направлении. Но выявить и указать проблемы не всегда просто, особенно, если это имеет политические последствия для проекта. Иногда нужно иметь смелость, чтобы послать К черту торпе ды , идти вперед (когда именно —мы покажем на с. 25). Гибкость работает только в том случае, если вы практикуете профессиональный подход к проекту, к своей работе, к своей карь ере. Если все это не про вас, то никакая самая гибкая практика вас не спасет. При правильном отношении вы выиграете от гибкости максимально. Далее следуют собственно практики и рекоменда ции по их применению.
1
Работайте на результат “Первый и самый важный шаг в решении проблемы — по иск того, кто был ее причиной. Найди этого идиота! Как только виновник найден, можно считать, что проблема больше не повторится. Никогда”.
Иногда слова дьявола звучат гак убедительно! Неужели вам не хочется первым делом заняться поиском виноватого? Правильный ответ: нет. Па первое место ставится устранение проблемы. Вам э го может показаться странным, но далеко не каждый чело век всегда ставит отдачу от проекта превыше всего остального. Даже вы сами. Вспомните вашу самую первую реакцию “по умолча нию”, когда проблема только обозначилась. Вы можете неумышленно усугубить проблему: высказать такие вещи, которые потом только усложнят ее, например обвинить ко го-то и вынуди ть его защищаться. Вместо этого “возьмите тоном выше” и спросите: “Что я могу сделать для решения данной пробле мы или улучшения ситуации?” Гибкая команда нацелена в первую очередь на результат. Вы нацеливаетесь на решение проблемы, а не па обвинение. Обвинение не исправляет ошибок
Худшее из занятий, которое только можно вообразить (за исключением уборки за елонами в цирке), —это работа среди чрезмер но реактивных людей. Их, похоже, вообще не интересует решение проблем, им доставляет удовольствие лишь обсуждать друг друга за спиной. Вся их энергия уходит на то, чтобы указывать пальцами и промывать косточки. Производитель ность таких команд очень низка. Если вы обнаруживаете себя в такой команде, не уходите оттуда, а убегайте. По меньшей мере, пе реведите тему разговора с негатива на что-нибудь более нейтраль ное —спорт или погоду (Что вы думаете об этих “Янки?”). В гибких командах все совершенно иначе. Если вы пожалуетесь кому-то из членов такой команды, вы услышите примерно следую щее: “Все ясно. Чем я могу помочь вам?” Вместо пустых размышле ний на тему данной проблемы они направляют все свои усилия на ее решение. Причина проста: результат важнее репутации, призна ния ошибок и интеллектуального соперничества. I Гачните с себя самого. Когда к вам приходит разработчик и го ворит о какой-то проблеме, расспросите его о деталях и поинтере суйтесь, чем вы можете помочь. Этот простой жест уже показыва ет, что вы намерены искать решение проблемы, а не смириться с
ней. Это уход от негативизма. Вы здесь для того, чтобы помогать. Люди постепенно поймут, что, если они обращаются к вам, вы ис кренне пытаетесь помочь им решить проблемы. Поэтому они бу дут приходить к вам за помощью в решении проблем; если же хо чется позубоскалить —им придется искать другого собеседника. Если вы сами приходите за помощью и получаете далеко не про фессиональную реакцию, попытайтесь спасти положение. Объяс ните как можно более точно, что вы хотите, и особо подчеркните, что для вас главное —найти решение, а не выяснять, кто прав или виноват. Щлш
АЩ
Обвинение не исправляет ошибок. Вместо того чтобы указывать пальцами, показывайте возможные решения. Только позитивный результат имеет значение.
Соответствие требованиям — еще не результат Активное применение стандартизации в разработке процессов предпола гает измерение и оценку соответствия параметров полученного процесса заданному (идеальному) процессу. Если процесс работает и вы докажете, что он точно соответствует заданным требованиям, значит, все в порядке. В реальности все работает совсем не так. Вы можете иметь сертификат ка чества ISO-9001 и производить превосходные спасательные жилеты из свинца. Вы точно следуете всем предписаниям, но все ваши пользователи упорно тонут! Оценка соответствия процессу не может измерить действительный резуль тат работы. Для гибких команд действительный результат важнее деталей процесса.
На что это похоже Вы не боитесь признаться, что не знаете ответов на какие-то во просы. Ошибки используются для изучения возможностей, а не для охоты за ведьмами. Команда работает сообща, а не ищет вино ватых.
Сохраняйте равновесие • Мнение “Это не моя вина” крайне редко соответствует действитальности. Обычно то же самое относится и к утвержде нию “Эго ты во всем виноват”.
• Если вы никогда не ошибаетесь, значит, вы недостаточно сильно стараетесь. • Споры с разработчиками о качестве продукта не дают особой пользы, особенно если проблема сводится к исправлению ошибки или доработке кода. Часто быстрее сделать, чем спо рить об этом. • Если кто-то из членов команды неправильно понял какое-ли бо требование, обращение к API, или решение, принятое на последнем совещании, он может оказаться далеко не единст венным в команде, кто заблуждается. Убедитесь, что вся команда успевает работать в заданном темпе. • Если кто-то из членов команды регулярно совершает дейст вия, которые отрицательно сказываются на общей работе, значит, поведение такого сотрудника нельзя считать профес сиональным. Оно не способствует движению команды впе ред. В этом случае лучше избавить команду от такого сотруд ника1. • Если большинство команды (в особенности, ведущие разра ботчики) ведут себя непрофессионально и не заинтересова ны в изменении ситуации, тогда вам следует избавить коман ду от себя самого и поискать успеха в другом месте: поверьте, это намного лучше, чем участвовать в заведомо обреченном проекте (см. [You99]).
1 Их совсем не обязательно увольнять, но им не нужно работать в этой ко манде. Помните, что перемещения людей и освобождения от должности тоже отрицательно сказываются на общем равновесии коллектива.
2
Быстрая правка не решает проблем иТебе совсем не нужно вникать в этот фрагмент кода: он, похоже, и так работает неплохо. Чуть-чуть подправим вот здесь, добавим единичку к результату — и все работает! Идем дальше, все хорошо".
*
Нам всем до боли знакома такая ситуация. Где-то вкралась ошиб ка, а время поджимает. Быстрое исправление как будто помогает: добавили единицу или пренебрегли последним элементом спи ска — и теперь код работает вроде нормально. Но именно потом происходит то, что позволяет отличить хорошего программиста от неотесанного хакера. Неотесанный хакер оставит все как есть и переключится на дру гую проблему. Хороший программист перейдет к следующему шагу и постара ется понять, почему нужно прибавлять единицу и — самое глав ное —на что еще влияет эта единица. Этот пример кажется надуманным и даже глупым, но он отража ет происходящее на практике, причем в массовом масштабе. Один из клиентов Энди имел подобную проблему с работающей систе мой. Никто из ее разработчиков и архитекторов не имел детально го представления о структуре данных заказчика, и в течение не скольких лет весь базовый код оброс исправлениями типа +1 и -1. Попытки распугать это безобразие, чтобы добавить новую функ циональность и исправить ошибки, превратились в настоящий кошмар для бедных разработчиков (кстати, многие из них после такого “прочесывания” сами облысели по-настоящему). Понятно, что большинство проблем такого сорта проявляются не сразу, а накапливаются постепенно —из одиночных правок на скорую руку. Каждая такая поверхностная правка, которая не уст раняла реальных проблем, залегающих глубже и пронизывающих весь код, лишь сыпала очередную горсть песка в трясину, которая со временем поглотила целиком весь проект. Помните О наземных минах
Поверхностная правка представляет серьезную опасность для кода: исправления такого рода обычно делаются в спешке —без вника ния в суть проблемы и без учета возможных последствий такой “быстрой фиксации” для кода. Бывает трудно устоять перед соблазном избрать облегченный вариант решения задачи. С близкого расстояния вам кажется, что все работает. Если же посмотреть немного дальше, то с тем же успехом можно перехо
дить минное поле. Можно успеть пройти половину поля или даже больше половины, но рано или поздно... Как только элемент быстрой правки появляется в коде, код “мут неет”, его прозрачность снижается. Когда этих элементов накапли вается много, код становится абсолютно непрозрачным, полным тумана. Вам, возможно, приходилось слышать примерно следую щее: “Что бы ты ни делал, не трогай этот модуль. Парень, который его написал, больше здесь не работает, а, кроме него, никто не зна ет, как все это работает”. Код непрозрачен, начисто лишен ясно сти, и разобраться в нем невозможно. Энди говорит... Хотя мы сейчас говорим о правильном понимании исходного текста кода, что особенно важно сделать до того, как вы вносите изменения, то же са мое относится к методологии командной разработки. Необходимо понимать методологию, которую вы собираетесь использо вать в работе команды. Вам надо представлять себе, как данная методоло гия будет работать на практике и почему она устроена именно так. Только в этом случае изменения, которые вы вносите в код, будут эффек тивными.
С таким багажом даже гибкие методики вряд ли справятся. Но некоторые из них все же помогают предупредить такие ситуации в будущем. Мы рстановимся на них подробнее в следующих главах, здесь лишь скажем о них коротко. Не пишите КОД
Изоляция очень вредна для процесса разработки. Не позволяйте своим разработчикам писать в полной изоляции (см. Практика 40, Практикуйте коллективную собственность, с. 172). Если члены команды часть своего времени посвящают изу чению кода, который написан их коллегами, то вам легче добить ся, чтобы код был читаемым и понятным, без украшений типа “+1” или “-1 ”. Чем чаще вы читаете чужой код, тем лучше. Текущие ре визии кода не только повышают прозрачность кода; это один из наиболее эффективных методов обнаружения ошибок (см. Прак тика 44, Проводите ревизию кода, с. 182). в ИЗОЛЯЦИИ
Используйте модульные тесты
Другой важный прием обеспечения прозрачности кода —это модульное тестирова ние. Применяя его, вы можете наиболее естественным образом разделить код на отдельные элементы, которыми легче управлять, что улучшает ди зайн кода и делает его более понятным. По мере продвижения про екта вы всегда можете вернуться назад и прочитать эти тесты; это,
по сути, исполняемая часть документации (см. П рактика 19, Поса дите ангелов себе на плечи, с. 87). Модульные тесты позволяют проанализировать небольшие и более простые для понимания фрагменты кода и глубже разобраться в работе всей системы с по мощью запуска отдельных ее компонентов.
Не попадайтесь на удочку быстрой правки (quick hack). Не жалейте сил для поддержания чистоты и открытости кода.
На что это похоже Код как будто хорошо освещен: во всем приложении нет ни одного темного угла. Вы можете не знать всех подробностей каждого из фрагментов кода или каждого алгоритма. Но вы достаточно хорошо представляете себе общую картину работы системы. Нигде не встре чается полицейское ограждение или знаки типа “Вход воспрещен”.
Сохраняйте равновесие • Вам нужно понять принцип работы какого-то фрагмента кода, но вовсе не обязательно становиться его экспертом. Нужно ос воить его настолько, чтобы иметь возможность эффективно работать с ним, но не делать это занятие свей профессией. • Если кто-то из разработчиков объявляет, что написанный им код слишком труден для понимания другими людьми, значит, поддерживать этот код будет трудно всем, включая его авто ра. Максимально упростите его. • Никогда не вносите исправления без понимания того, что происходит. Синдром + 1 /-1 начинается с невинной мелочи, но затем стремительно разрастается, покрывая код сплош ной и непрозрачной паутиной. Лечите болезнь, а не ее от дельные симптомы. • Большинство нетривиальных систем представляют собой до вольно трудный объект для всестороннего изучения незави симо от профессионального уровня изучающего. Помимо глубокого понимания конкретных элементов системы, с ко торыми вы непосредственно работаете, вам еще необходимо хорошо представлять работу других элементов системы и их взаимодействие друг с другом. • Если система уже подобна непрозрачной паутине, следуйте рекомендациям Практики 4, К черту торпеды, идите вперед, с. 25.
3
Критикуйте идеи, а не людей “ Ты многое вложил в свой проект дизайна приложения. Ты вложил в него свою душу, свое сердце. Ты знаешь, что твой проект лучше, чем все остальные. Не трать времени на выслушивание идей других людей, они только сбивают с толкуй.
Вам наверняка приходилось наблюдать, как обсуждение проек та системы выходит за рамки деловой дискуссии и уступает место эмоциям; в результате решения принимаются исходя из того, кто автор идеи, а не вследствие преимуществ самой идеи над остальны ми. Мы тоже, к своему сожалению, были свидетелями таких сове щаний. В этом нет ничего необычного. Допустим, некто (назовем его Ли) представляет свой проект. Самая простая ваша реакция —ска зать “Это глупо” (при этом подразумевая тупость самого Ли). Неко торые усилия требуются от вас, чтобы уточнить: “Это глупо—ты за был о потоковой безопасности”. И, наконец, действительно серьезных усилий требует наиболее адекватная реакция: “Спаси бо, Ли. Мне только не совсем понятно, что произойдет при одно временном входе двух пользователей в систему”. Вы видите разницу? Вот различные типы реакции на очевидную ошибку: • Объявить ее автора некомпетентным. • Потопить саму идею, указав на ее очевидный изъян. • Попросить вашего коллегу обсудить то, ч то беспокоит вас. Первый тип реакции не принесет абсолютно никакой пользы. Даже если Ли — безнадежный идиот, от которого пока проку не больше, чем от мусорной корзины, ваше сообщение об этом факте вряд ли прибавит ему компетентности и, скорее всего, заставит его отказаться от подобных попыток предлагать что-либо в будущем. Второй тип реакции, по крайней мере, уже успешнее бьет в цель, но не слишком полезен для самого Ли, к тому же он может вызвать обратный огонь на вас. Ведь Ли, которого вы заподозрили в том, что он забыл про потоковую безопасность, вполне может ответить что-либо умное, например: “A-а, да многопоточность здесь вообще не требуется. Все это исполняется в контексте Фрозбот^модуля, а он запускается в собственном потоке”. Ой! Забудьте о Фрозботе. Теперь вы попали в глупое положение, а Ли явно задет тем, что вы считаете его идиотом.
Остается третий тип реакции. Никаких обвинений, ни поспеш ных суждений, просто внесение ясности. Вы даете Ли возмож ность обнаружить проблему самостоятельно, вместо того, чтобы ткнугь его носом1. Это приглашение к разговору, а не к спору.
Венкат рассказывает...
Будьте профессионалами, а не “индивидуалами” Много лет назад, в первый день моей работы в качестве системного адми нистратора, старший администратор и я устанавливали какое-то про граммное обеспечение. Я случайно нажал на кнопку выключения сервера. Через пару секунд несколько недовольных пользователей колотили в дверь. Поведение моего наставника сразу снискало мое доверие и уважение: вме сто того, чтобы ткнуть в меня пальцем, он просто сказал: “Извините, мы пока не знаем, что произошло. Система вернется в рабочее состояние че рез пару минут”. Я получил важный незабываемый урок.
В тесных рамках коллектива разработчиков совсем небольшая доза вежливости и этикета поможет всей команде сосредоточить внимание на качествах самой идеи, а не на личных разногласиях. Все мы в равной степени способны выдавать как великие передо вые идеи, так и полную чушь. Если есть высокая вероятность того, что вашу идею засме ют, или ваша репутация от такого предложения может постра дать, вы вряд ли захотите предлагать что-либо еще. Вообще когдалибо. И это очень плохо: спроектировать и разработать хороший программный продукт невозможно без творческого подхода и интуиции. Весь проект выигрывает, если люди с различными взглядами и интересами обмениваются идеями и соединяют их в нечто большее, чем каждый из них способен предложить само стоятельно. Негативизм убивает любую передовую идею
Любые негативные замечания и отношения пресекают новаторство. Мы не предлагаем вам и вашей команде во время рабочих сове щаний держаться за руки и петь “Кумбайа". Хотя бы потому, что это заметно снизит общий темп совещания. Но необходимо постоянно фокусироваться на решении проблем, а не на попытках доказать, чья идея лучше. Один-единственный вы сокоодаренный разработчик в команде не слишком эффективен, 1 Вообще, это великолепная тактика: задать наводящий вопрос, который позволит собеседнику понять проблему самостоятельно.
но гораздо хуже иметь несколько сталкивающихся лбов, которые отказываются работать друге другом. Производительность и нова торство быстро сходят на нет в таких командах. У каждого из нас всегда есть и хорошие, и плохие идеи, и каж дый в команде должен чувствовать свободу выражения своих идей. Даже если ваша идея не принята полностью, она может помочь в выработке решения. Не бойтесь критики. Помните: каждый, кто стал мастером, когда-то был новичком. Или, как сказал, Лес Браун (Les Brown): “Ты не должен быть великим, чтобы начать, но дол жен начать, чтобы стать великим”. Коллективный верблюд Коллективное проектирование кода бывает весьма эффективным, но неко торые наиболее сильные новаторские идеи рождаются в одиночных “свет лых головах” — у тех, кто способен на более проницательный взгляд. Если вы считаете себя одним из них, вы просто обязаны крайне почтительно от носиться к чужим идеям, которые могут оказаться полезными. Вы как сто рож: с одной стороны, вы защищаете свою позицию, с другой — вам нужно внимательно присматриваться и давать добро новым ценным идеям, даже если они исходят не от вас. На другом конце спектра заседает “комитет*, в задачи которого входит вы работка согласованного решения по всем и каждому вопросу проектирова ния. Если нужно создавать лошадь, этот комитет, как правило, создает верблюда. Мы не предлагаем вам ограничиваться дизайном, который получился в ре зультате такого консенсуса, но вы и не должны быть в заложниках у главно го архитектора, который не способен воспринять ничего нового. Мы совету ем вам прислушаться к словам Аристотеля: “Лишь по-настоящему образованный ум способен поддержать мысль, не принимая еел.
' Вот некоторые конкретные приемы, которые помогут вам на чать: Установите срок окончания (deadline). Если вы проводите проектное совещание или просто затрудняетесь придти к какомулибо решению, установите для него жесткие временные рамки, на пример, нужно закончить до обеда или до конца рабочего дня. Та кое ограничение способствует продвижению команды вперед и предотвращает топтание на месте в бесконечных идеологических дискуссиях. Будьте прагматиками (не побоимся этого слова): воз можно, будет найден далеко не самый лучший выход, а всего лишь более подходящее решение. Срок завершения помогает делать трудный выбор и двигаться дальше.
Доказывайте обратное. Каждый член команды должен пони мать, что без компромиссов не обойтись. Чтобы сохранять объек тивность в отношении какой-то идеи, нужно сначала страстно за щищать ее, а потом спокойно критиковать1. Задача, в конечном счете, сводится к поиску решения с максимальным количеством ар 1ументов “за” и минимальным “против”, так что вам нужно про сто собрать как можно больше доводов обоих знаков. Этот подход также позволяет сделать процесс обсуждения как можно менее эмоциональным. Используйте медиатора. В начале совещания выберите медиа тора (посредника), который будет отвечать за соблюдение фор мальностей при выработке решений в течение всего собрания. Ка ждый член команды должен иметь возможность высказывать свои мысли и суждения по различным аспектам обсуждаемой пробле мы. Медиатор должен следить за тем, чтобы каждый желающий мог выступить и чтобы обсуждение не топталось на месте. В его власти не давать никаких особых преимуществ кому бы то ни было, в том числе примадоннам; он также может остановить выступле ние, не относящееся к делу. Проще всего оставаться в тени и беспристрастно вести собра ние тогда, когда вы сами не являетесь активным участником обсуж дения. Таким образом, медиатор должен сосредоточиться на ф ор мальной стороне обсуждения, а не предлагать собственные идеи (в идеале, ход выполнения проекта не должен касаться его лично). Само собой разумеется, для этой роли важна не столько техниче ская осведомленность, сколько личные качества и опыт общения. Обеспечьте выполнение реш ения. Как только решение при нято (любым способом), каждый из членов команды “переводит стрелку” и принимается за его реализацию в тесном сотрудничест ве с остальной командой. Каждый должен помнить, что главная цель работы —это создание продукта, удовлетворяющего нужды за казчиков. Для заказчика неважно, кто автор идеи, для них важно, чтобы приложение работало и соответствовало их ожиданиям. Это единственно важный для заказчика результат. Дизайн любой системы, k^lkи сама жизнь, полон компромиссов. Успешные команды —это те, которые считаются с этой реально стью. Работа с командой без эмоций потребует от вас немалых уси лий, но этот более зрелый стиль руководства не останется незаме ченным среди ваших коллег. Ваш личный пример заразителен и может принести высокую практическую отдачу. 1 См. “Debating with Knives” (“Ножевые дискуссии") на странице h ttp :// blogs.pragprog.com/cgi-bin/pragdave.cgi/Random/FishBowl. rdoc
,
Критикуйте идеи а не людей. Приближение к решению дос
тавляет большее удовлетворение, нежели доказательство, чья идея лучше.
На что это похоже Напряженность уходит, когда команда спокойно обсуждает подлин ные достоинства и возможные недостатки каждого из предложен ных путей решения. В этом случае вы, не боясь никого обидеть, мо жете отклонить пути, имеющие слишком много недостатков, и без угрызений совести принять пусть даже не идеальные (но все же бо лее удачные) решения.
Сохраняйте равновесие • Всегда старайтесь предложить хорошую идею, но не рас страивайтесь, если ваши идеи не вошли в проект. Не добав ляйте лишние детали в существующую хорошую идею, только ради того, чтобы быть одним из ее соавторов. • Иногда лучше прервать дискуссию, если высказываются наду манные соображения, которые не имеют под собой реальной почвы. Легко задушить не нравящуюся вам идею, приписы вая ей проблемы, которые могут никогда не возникнуть или вообще невозможны. Если вы замечаете, что это происходит при обсуждении, спросите, возникала ли указанная проблема прежде, и как часто. Иными словами, аргументов типа “Мы не можем принять данную стратегию, поскольку поставщик базы данных можег прекратить работу” или “Пользователи никогда не примут эту идею” явно недостаточно. Необходимо провести оценку, какова действительная вероятность развития ситуации по данному сценарию. Если уже имеется подобный опыт, или проводились соответствующие исследованйя, их результаты должны быть представлены, чтобы подкрепить или опро вергнуть высказанные утверждения. • До того как выбирать наилучший вариант решения всегда по лезно убедиться в том, что все голосующие придерживаются единого мнения по поводу того, что нужно считать лучшим. Лучшее для разработчиков может быть далеко не самым луч шим для пользователей и наоборот.
В реальности не существует “наилучшего” решения —просто одно решение бывает лучше другого. Несмотря на популярность этого термина, не существует и так называемых "наи лучших практик”: есть только практики, более подходящие к конкретной ситуации. Отсутствие эмоционального отношения не означает, что вы слепо принимаете любую высказанную идею. Выбирайте пра вильные слова и аргументы, объясняя, почему вы сомневае тесь в достоинствах конкретной идеи или решения, и зада вайте вопросы для лучшего их понимания.
4
К черту торпеды, идите вперед "Когда обнаруживаешь проблему в чужом коде, не говори об этом никому. Ты же не хочешь обидеть кого-то или вы звать всеобщую панику. Если же автором кода является твой босс, будь предельно осторожен, просто следуй его распоряжениям?'.
В басне “Кто повяжет колокольчик кошке” мыши решили повя зать колокольчик на шею кошке, чтобы он звоном оповещал их о ее приближении. Все мыши единогласно проголосовали за этот план. Тогда старая мышь спросила: “Итак, кто же повяжет коло кольчик?” Понятно, что никто не вышел вперед, а весь план про валился. Часто самые замечательные планы рушатся из-за недостатка смелости. Несмотря на все опасности —буквальные и фигуральные торпеды, — необходимо брать на себя повышенную ответствен ность и делать то, что является единственно правильным. Предположим, вас попросили поправить код, написанный кемто другим. Этот код исключительно труден для понимания и рабо ты. Должны ли вы продолжать работу с ним и, в конце концов, со хранить и приумножить весь этот беспорядок? Или лучше сказать боссу, что код безнадежно плохой и его нужно выбросить на по мойку? Конечно, можно долго топтаться на месте, рассказывая всем, насколько ужасен этот код, но эго всего лишь жалобы, а отнюдь не поиск решения проблемы. Вместо этого, проведите оценку затрат и ожидаемого эффекта, если вы продолжите работу с данным ко дом, и сравните их с затратами и эффектом от его полного перепи сывания. Не только сообщите, но и покажите, что экономически выгоднее будет выбросить этот код и написать новый. Приведите доводы, которые помогут вашему боссу (и коллегам) оценить си туацию и принять верное решение. А теперь представьте, что вы в течение некоторого времени ра ботали над отдельным компонентом системы и неожиданно осоз наете, что все это время вы шли совсем не пуда (и влезли не на то де рево) и вам нужно все переделывать. Вы, естественно, боитесь признаться в этом другим членам команды и попросить их о допол нительном времени или помощи. Вместо того чтобы скрывать эти трудности, встаньте и скажи те: “Я хорошо понимаю: то, что я сделал, не является правильным подходом. Я вижу следующие пути исправления данной ситуации; если у вас есть другие соображения, я бы хотел их услышать. Но
уже сейчас понятно, что эта работа потребует еще некоторото времени”. Вы сняли напряжение и ясно дали понять, что стреми тесь найти выход из создавшегося положения. Вы попросили кол лег совместно поискать решение проблемы, не оставив места для любого противостояния. Ваши коллеги получили стимул для со трудничества с вами, чтобы выйти из кризисной ситуации. Те перь они могут активно включиться и протянуть вам руку помо щи. Что еще важнее, вы показали свою честность и смелость, чем заслужили их доверие. Венкат рассказывает...
Насаждайте хорошие практики Я как-то работал с приложением, которое посылает различные типы фай лов серверному процессу, и меня попросили сделать так, чтобы программа умела записывать файлы еще одного типа. Это не должно было вызвать затруднений. Но, когда я начал копаться в коде, меня поразил тот факт, что фрагменты, которые управляют различными типами файлов, вставлены в код простым копированием. Я пошел “в масть” и тоже просто скопировал сотню строк кода, поправил пару строчек, и буквально через пару минут все заработало — но я чувствовал себя хуже некуда. Я поступил плохо, на рушив предписания хороших практик программирования. Я убедил босса, что поддержка этого кода очень скоро станет невыгодным делом, и настоял на рефакторинге. Уже через неделю мы почувствовали выигрыш от этого подхода, когда нам пришлось слегка изменить процеду ру управления файлами в системе: только в этом случае все изменения уже оказались сосредоточены в одном месте, а не рассредоточены по все му тексту исходного кода, как могло быть раньше.
Если вы знаете правильный путь или, по крайней мере, увере ны, что выбранный путь является тупиковым, наберитесь смело сти и объясните свою позицию другим членам команды, вашему боссу, вашим клиентам. Разумеется, это не всегда легко. Это может отодвинуть сроки выполнения проекта, рассердить руководителя проекта, вызвать раздражение спонсоров. Невзирая ни на что, вам нужно настаивать на движении по правильному пути. Адмирал Дэвид Фаррагот, один из героев гражданской войны, однажды скомандовал “К черту торпеды! Капитан Дрейтон, впе ред! Джоэт, полный ход!” На их пути были мины (в те времена они назывались торпедами), но им необходимо было прорваться —и на полной скорости они все-таки прошли1. Это было верное решение. 1 В действительности, чаще это изречение Фаррагота встречается в сокра щенном виде, как боевой клич: “К черту торпеды, полный вперед!"
Делайте то, что считаете правильным. Будьте честными и имейте смелость сказать правду. Порой это трудно, вот поче му требуется смелость.
На что это похоже Смелос ть обычно не ведет к комфорту, а скорее наоборот, осо бенно попачалу. Но чаще всего это единственный способ изба виться от помех, которые со временем будут лишь усугубляться, и, в конечном счете, вы испытаете облегчение, вместо усиливающе гося ужаса.
Сохраняйте равновесие • Если вам кажется, что небеса рушатся и все настроены про тив вас, то это может означать, что вы правы, но не сумели достаточно хорошо аргументировать свое мнение. • Если вам кажется, что небеса рушатся и все настроены про тив вас, то это может означать, что они правы. • Если проект кода или сам код кажутся вам странными, не жа лейте времени на то, чтобы разобраться в истинных причи нах вашего непонимания. Может оказаться, что само реше ние правильное, но излишне запутанное, так что потребуется рефакторинг, чтобы привести все в порядок. Не отклоняйте и не переписывайте все подряд только из-за своего непони мания. Это не смелость, а элементарное отсутствие терпе ния. • Если ваша смелая позиция встречает сопротивление со сто роны людей, принимающих решения, но которым явно не хватает опыта для правильного понимания проблемы, это значит, что вам придется объяснить им то же самое в терми нах, которые им более понятны. Аргументы типа “более чис тый код” на предпринимателя не подействуют. Экономия де нежных затрат, получение высокой отдачи на капвложения, предотвращение судебных тяжб и рост числа клиентов —вот близкие и понятные для них аргументы. • Если вас пытаются заставить поступиться качеством кода, можете ответить, что вы как разработчик не имеете права на носить вред корпоративному имуществу (к коим нужно отно сить и основание кода в целом).
Даж е если ты на правильной дороге, не стой на месте — затопчут. Уилл Роджерс
Глава 3
Взращивание гибкости Гибкость требует постоянного ухода и заботы. Как подметил Уилл Роджерс, нужно постоянно быть в движении. А то, что верно под мечено для лошадей на скачках, как правило, относится и к нам, программистам. Профессия разработчика программного обеспечения —это сти хия, которая постоянно находится в движении, эволюционирует; хотя некоторые концепции всегда актуальны, подавляющее их большинство быстро устаревает. Поэтому наша профессия напо минает бег на тренажере —нельзя отставать, иначе вылетишь. Кто способен помочь вам не выпадать из этого потока? В корпо ративном мире эго только один человек —вы сами. Это ваше пра во —быть или не быть в курсе изменений. Большинство новых технологий основывается на уже существую щих технологиях и идеях. Они содержат в себе новые элементы, но все изменения, как правило, добавляются постепенно. Если вы все время в курсе, то, чтобы воспринять очередное новшество, вам про сто нужно осознать, в чем заключается шаг вперед. Если же вы не следите постоянно за новинками, любое новшество кажется неожи данным для вас и трудно воспринимается. Это все равно, что вер нуться в родной город после десяти лег отсутствия: вы видите слиш ком много изменений, а некоторые места даже кажутся совсем незнакомыми. Тем не менее, люди, живущие здесь и наблюдающие каждодневные изменения, чувствуют себя абсолютно комфортно. Мы поговорим о возможности Всегда бы ть в курсе на с. 30. Усилия по поддержанию собственной осведомленности —дос тойное занятие, но необходимо также активно П овы ш ать уро вень своей команды; об этом мы поговорим на с. 34. Несомненно, изучать новые технологии и новые подходы очень важно, но необходимо еще уметь вовремя отказываться от устарев
ших взглядов на вещи. Другими словами, нужно Уметь воврем я ра зучиться, а как это сделать —см. с. 37. Если уж говорить об изменениях, то нужно еще всегда помнить о том, что наше понимание ситуации тоже не стоит на месте в тече ние выполнения проекта. Вещи, которые казались простыми и по нятными вначале, оказываются далеко не столь очевидными по том. Вам постоянно нужно добиваться полной ясности там, где вам что-то непонятно. И мы увидим, как и почему необходимо Спра ш ивать до тех пор, пока не поймете; см. с. 40. Наконец, “хорошо смазанную” гибкую команду обычно отлича ет систематический подход к выполнению задач, регулярность все го процесса разработки. Как только все покатится вперед, Почув ствуйте ритм, биение которого мы покажем на с. 43.
5
Всегда будьте в курсе "Технический прогресс идет такими темпами, что любые попытки угнаться за ним вызывают переполнение. Это вполне естественно. Делай все свою работу по-старому, пиши на языке, который давно знаешь; все твои попытки следить за изменениями обречены на прова/ f .
“Н ет ничего более постоянного, нежели перемены”, — сказал Геракли т. Это было верно на протяжении всей истории человече ства, но особенно верно сейчас. Вы находитесь в среде, которая по стоянно меняется, эволюционирует, увлекая вас за собой. Если у вас высшее образование в облас ти компьютерных технологий или в других смежных областях, то большой ошибкой было бы пола гать, что на этом ваше образование закончилось. Допустим, вы окончили вуз в 1995 г., т.е. порядка десяти лет на зад. Что вы тогда могли знать? Скорее всего, вы неплохо знали C++. Вы слышали о новом языке, с красивым и необычным названием Java. Повышенным вниманием начинала пользоваться концепция так называемого шаблонного проектирования (design patterns concept). Были разговоры о чем-то, что обозначалось словом Ин тернет. Если вы “впали в спячку” и проснулись в 2005 г., то все, что вы увидите вокруг себя, неизбежно приведет к “переполнению”. Целого года вам будет мало, чтобы обработать и освоить все новые технологии и восстановить свой профессиональный уровень — даже лишь в одной отдельно взятой области знаний. Темпы технического прогресса просто невероятны. Например, возьмем язьп^ауа. Изначально это был просто язык, возможности которого последовательно расширялись с каждым его обновлени ем. Теперь мы имеем Swing, Servlets, JSP, Struts, Tapestry, JSF, JDBC, JDO, Hibernate, JMS, FJB, Lucene, Spring...; список можно продол жать. Если посмотреть на технологии Microsoft, то там есть VB, Visual C++, MFC, COM, ATL, .NET, С#, VB.NET, ASP.NET. ADO.NET, WinForms, Enterprise Services, Biztalk.... И не забудем про UML, Ruby, XML, DOM, SAX,JAXP,JDOM, XSL, Schema, SOAP, web-сервисы, SO А; и здесь можно еще долго перечислять (и аббре виатур может не хватить). К сожалению, сейчас уже недостаточно просто иметь хорошие знания и навыки. Все это может через пару лет устареть или “уе хать за границу* в виде аутсорсинга, и вы тогда лишитесь работы 1. 1 Например, см. “Му Job Went to India: 52 Ways to Save Your Job” (“Моя ра бота уехала в Индию: 52 способа остат ься на своей работе *) [Fow05].
Предположим, что вы программист па Visual C++ или VB. Гут по являются технологии СОМ. Вы тратите время па их изучение (хотя это непростое занятие) —и, наконец, понимаете, в чем же со стоит весь смысл распределенных объектных вычислений. Когда появляется XML, вы гоже тратите время на его изучение. Потом вы копаетесь в ASP и узнаете, как можно написать wcb-приложение. Вы не стали специалистом в каждой их этих технологий, но вы не остались невежественным в этих областях. Благодаря своей пытливости вы познакомились с MVC и с шаблонами проектирова ния. Немного поиграли сJava —ровно столько, чтобы понять, в чем его “кайф”. Если вы постоянно следили за новой информацией, то каждый следующий шаг, например освоение .NET, вам дается относитель но легко. Вам не приходится карабкаться сразу на десятый этаж: вы уже и так высоко, осталось лишь подняться вверх на одип-два пролета. Если же вы не поднимались до этого момента, тогда подъем на десятый этаж закончится тем, что у вас, по меньшей мере, собье тся дыхание. И к тому же это займе т у вас немало вре мени, в течение которого появятся еще несколько новых техно логий. Каким образом всегда идти в ногу со временем? К счастью, сегодня мы имеем в своем распоряжении достаточно большое количе ство приемов и средств, помогающих нам постоянно повышать свой образовательный уровень. Вот лишь некоторые возмож ности: Учитесь итеративно (регулярно) и инкрементно (постепен но повышая свой уровень). Выделяйте каждый день некоторое время для таких занятий. Можно совсем немного, здесь главное — регулярность. Отслеживайте то, что хотите лучше узнать: если встречаете незнакомый термин или фразу, помечайте их, а в сле дующий раз во время очередного занятия попытайтесь в них разо браться. Будьте в курсе последних “сплетен”. В Интернете есть обшир ные ресурсы для того, чтобы узнать подробнее о любой интересую щей вас технологии. Регулярно читайте дискуссионные форумы и подпишитесь на тематические рассылки, чтобы иметь представле ние о проблемах, с которыми сталкиваются другие, и об их наилуч ших решениях. Выберите несколько хороших блогов технической ориентации и регулярно просматривайте их, обращая особое вни мание на то, какие книги читают их авторы (см. также сайт pragmaticprogrammer.com). Участвуйте в локальных пользовательских группах. Локаль ные группы пользователей существуют практически везде. Их те
матика имеет обширный спектр: Java, Ruby, Delphi, .NET, оптими зация процессов (process improvement), ООП» Linux, Mac и многие другие технологии. Слушайте докладчиков, а потом участ вуйте в обсуждениях, которые проводя тся в форме вопросов и от ветов. Посещайте семинары и конференции. Конференции по ком пьютерной тематике проводятся по всему миру. Многие известные консультанты и авторы книг проводят семинары и аудиторные за нятия. Посещение этих мероприятий — великолепный шанс по учиться непосредственно у настоящих экспертов. Будьте неутомимым читателем. Ищите хорошие книги по раз работке программного обеспечения, а также далекие от специаль ной тематики (мы бы могли вам кое-что порекомендовать). Про сматривайте газеты и отраслевые издания, а также массовую прессу (забавно читать старые новости, преподнесенные как вести “с переднего края”).
4W
Следите за техническим прогрессом. Не нужно быть экспертом во всем, но необходимо быть в курсе основных тенденций развития отрасли и учитывать их в своих профессиональных планах и в работе над проектами.
На что это похоже Вы в курсе всего, что происходит; вы узнаете о новых технологиях по мере их появления и принятия. Если понадобится переклю читься и работать в любой другой смежной области —вы вполне го товы к этому.
Сохраняйте равновесие • Многие новые идеи так и остаются идеями, не превращаясь в сколько-нибудь законченную, полезную технологию. То же самое можно сказать о крупных, популярных, хорошо финан сируемых проектах. Соизмеряйте свои усилия. • Нельзя быть специалистом абсолютно во всем. И не пытай тесь им стать. Но если вы уже в чем-то являетесь экспертом, вам гораздо легче им стать и в некоторых других областях. • Постарайтесь понять причины актуальности конкретной но вой технологии, разобраться в том, какую проблему она при звана решить и в каких случаях ее можно применять.
I Ie поддавайтесь порыву перевести вашу систему на новую технологию, инфраструктуру или язык только ради изуче ния. Нужно сначала оценить преимущества нового подхода и только после этого принимать его. Написание небольшого прототипа может служить эффективным противоядием от чрезмерного энтузиазма.
6
Повышайте уровень своей команды "Не делись своим знанием ни с ке м — держи его при себе. Быть самым знающим в команде — это большое преиму щество. Пока ты самый умный, тебе нет дела до каких-то бездельников’'.
У разработчиков вашей команды разные способности, опыт и знания. У каждого есть свои сильные стороны, и в чем-то он разби рается лучше других. Это многообразие талантов и опыта пред ставляет собой идеальную почву для повышения общего образова тельного уровня команды. Для успеха вашей команды недостаточно только вашей образо ванности и знаний. Если другие члены команды не столь хорошо осведомлены, как вы сами, вся команда работает менее эффектив но, чем могла бы: хорошо образованная команда во всех отношени ях лучше невежественной. В процессе работы над проектом вы часто вынуждены прибе гать к специальным терминам и метафорам для более точного вы ражения своих мыслей и целей. Если многие ваши коллеги не зна комы с этими понятиями, вам будет* трудно добиться нужных результатов. Предположим, вы прошли курс обучения или верну лись с симпозиума. Знания, которые не применяются на практике, как правило, теряются. Нужно постараться передать все, что вы уз нали нового, своей команде. Поделитесь этим знанием сразу по возвращении. Сначала определите те области, где вы или другой из вашей ко манды, достаточно хорошо осведомлены, чтобы помочь осталь ным поднять свой уровень (кстати, еще одно преимущество такого обучения заключается в том, что вы можете обсуждать с ними, на сколько данные идеи применимы конкретно к вашим приложени ям и проектам). Совещания “со своей едой” (brown-bag session) —великолепная форма обмена знаниями внутри команды. Выберите день недели, например, сред)' (или любой другой день, кроме понедельника и пятницы). Запланируйте собраться во время обеда, чтобы не ду мать о других деловых встречах и не заботиться о специальных приглашениях. Сэкономьте на расходах: пусть каждый принесет с собой еду (непременно “в коричневом пакете”). Каждую неделю просите кого-либо из команды быть ведущим. Он или она может рассказать о каких-либо новых концепциях, про демонстрировать инструмент или просто сделать что-нибудь, ин тересное /для всей команды. Можно просто взять книгу и читать
наиболее интересные разделы, параграфы, практики и т.д.1Подхо дит все, что хорошо работает. Кто-то впереди вас? Это здорово! Легендарный джазовый гитарист Пэт Метани (Pat Methany) советует: "Все гда будьте самым худшим музыкантом в группе — где бы вы ни играли. Если вы лучший, то вам нужно играть в другой команде. Я думаю, что этот принцип работает почти везде". Почему именно так? Если вы лучший, то у вас меньше стимулов повышать свой профессиональный уровень. Если же все вокруг лучше вас, то вы ак тивно стремитесь соответствовать им. Вы будете играть на пределе своих возможностей.
Пусть совещание начинается с небольшого выступления веду щего —пя т н а д ц а т ь минут или около того. После чего вы предлагае те тему для разговора, чтобы каждый мог высказать свое мнение по данному вопросу, а также обсуди ть, насколько этот подход может быть актуален в вашей рабо те над проектами. Обсуждайте преиму щества реализации, приводите примеры из ваших приложений, договоритесь отслеживать дальнейшие результаты. Подобные совещания могут быть весьма полезны. Они не только расширяют профессиональный кругозор вашей команды, но и вы сами узнаете много нового. Благоразумные руководители обычно вы соко ценят сотрудников, способных помочь своим коллегам в повы шении профессионального уровня, так что подобная форма обмена опытом может успешно повлиять и на вашу собственную карьеру. Поддерживайте планку как можно выше — для себя само го и для всей команды . Используйте"brown-bag” совещания для расширения кругозора и опыта ваших сотрудников, а так же в целях сплочения команды. Постарайтесь вызвать повы шенный интерес команды к освоению тех технологий и прие мов , реализация которых может дать заметный выигрыш для текущего проекта.
На что это похоже Все вокруг вас как будто поумнели. Вся команда имеет представле ние о новой технологии и теперь размышляет о том, каким обра зом применить ее на практике или какие трудности могут подсте регать их на этом пути. 1 Издатели серии Pragmatic Bookshelf. Энди и Дэйв, слышали о многочис ленных случаях коллективного чтения книг их серии.
Сохраняйте равновесие • Групповое чтение книг, при котором книги читаю гея подряд по главам, может быть очень полезным занятием, но лишь в том случае, когда это действительно хорошая книга. Что-ни будь типа “Изучение XYZ за 7 дней вместе с шаблонами и UML" вряд ли можно считать книгой, стоящей вашего вни мания. • Не все темы дискуссий обязаны быть актуальными для кон кретного момента времени. Но все равно им необходимо уде лить внимание: Ной строил свой ковчег при ясном небе. • Старайтесь сделать это чем-то особенным, внут ренним для команды. Обед, заказанный в аудиторию и совмещенный с показом слайдов PowerPoint, лишен элемента интимности и ограничивает свободу дискуссии. • Придерживайтесь регулярного графика. Постоянное воздей ствие малыми дозами полезно с точки зрения гибкости. То, что предлагается редко, но помногу или подолгу, —совсем на оборот. • Если кто-то из членов команды не хочет приходить на обед, пообещайте ему пиццу. • Не ограничивайтесь книгами или обсуждением лишь специ альной тематики, другие темы обсуждения (оценка выполне ния проекта, навыки общения и т.д.) могут быть не менее по лезны для всей команды. • Совещания “brown-bag” не заменяют текущих рабочих сове щаний по проекту. Они нужны для обсуждения относительно общих вопросов, которые' могут быть важны для работы команды. Для решения конкретных проблем лучше использо вать рабочие совещания.
7
Умейте вовремя разучиться чЛ
Т ы всю жизнь делал это так, и это было разумно. Это все гда хорошо работало. Все приемы, которые тыиспользовал с самого начала, самые лучшие. На самом деле не так уж много поменялось с тех пор”.
Одним из основополагающих принципов гибкости можно на звать копирование с внесением изменений. Учитывая, что измене ния вносят ся повсеместно и непрерывно, имеет ли смысл продол жать использовать одни и те же приемы и инструменты, которые вы применяли всегда? Нет, конечно же, нет. Мы говорили много в этой главе об изуче нии новых технологий и подходов, но помните, что необходимо бывает также разучиться делать то, что вы давно привыкли делать. По мере всеобщего движения вперед многие вещи, которые прежде играли первостепенную роль, сбрасываются на обочину. Они не только становятся бесполезными, они наносят вред вашей эффективности. Когда Энди только начинал работать как програм мист, оверлеи были передовой технологией. В те времена доволь но часто было невозможно загрузить программу целиком в опера тивную память (48 Кбайт или что-то около того), и вам приходилось разбивать код на сегменты. Один сегмент загружался в память, другой —выгружался, а перекрестный вызов функций ме жду этими сегментами, разумеется, был невозможен. Это весьма ощутимое ограничение, которое серьезно сказыва лось на проектировании системы, а также на технике программи рования вообще. И еще, в былые времена (когда вы могли писат ь про певца, кото рый раньше звался Принц, даже не показывая картинку) вам при ходилось прилагать немалые усилия, чтобы выжать дополнитель ные такты процессора, вручную исправляя ассемблерный код, сгенерированный компилятором. Вы могли бы представить себе нечто подобное сейчас в контексте JavaScript илиJ2EE? Для большинства бизнес-приложений технологии программи рования ушли далеко вперед от ограниченных сегментов памяти, ручного создания оверлеев и ручной правки ассемблерного кода1. Но все^гаки нам доводилось не раз вст речать разработчиков, кото рые никогда пе меняют своих старых привычек (а Принц, кстати, снова известен просто как Принц). 1 Это все еще практикуется в разработке некоторых встроенных систем.
Однажды Энди видел фрагмент некоторого кода, написанный на С, который представлял собой один огромный цикл Гог. 1 ело та кого цикла занимало примерно шестьдесят печатных страниц. Ак тор кода “не доверял” компиляторной оптимизации и вручную раз ворачивал цикл и демонстрировал другие подобные “трюки”. Занятно будет кому-то поддерживать такой шедевр ручной работы! Когда-то, давным-давно, все это могло бы иметь хоть какое-то разумное объяснение, но только не сегодня. Компьютеры, процес соры с высокой тактовой частотой тогда стоили недешево, а те перь это ширпотреб. Теперь более дефицитным и дорогостоящим ресурсом становится время программиста. Этот факт медленно, но верно начинает доходить до многих. Все мы были свидетелями того, как крупные J2EE-npoeiabi по десятку человеко-лет проваливались, уступая место быстрым решениям на базе РНР, которые создавались за один месяц и обеспечивали прак тически ту же самую функциональность. Растущий интерес к языкам типа РЫР и web-структурам типа Ruby on Rails (см. [ТН05]) ясно по казывает, что старые методы работы более не эффективны. Но разучиться бывает непросто. Многие команды вынуждены барахтаться только из-за того, что начальство решило сэкономить 500 долларов на системе сборки, предпочитая потратить десятки тысяч долларов на оплату труда программистов, которые вынужде ны бороться с проблемами, которые могут даже никогда не возник нуть. Возможно, это было бы разумно тогда, когда компьютер сто ил 500 тыс. долларов, но только не сейчас. В процессе изучения новой технологии спросите себя, не склон ны ли вы переносить на нее привычные для вас методы и приемы работы. Применение объектно-ориентированного подхода к напи санию программ кардинально отличается от использования проце дурноориентированных языков. Легко опознать С-код, написан ный на Java, или VB-код, написанный на C# (а также Fortran в любом новом обличье). Если это происходите вами, то вы теряете все пре имущества, которые ждете от применения новой технологии. Труднее всего выбросить модели мышления, которые складывались годами
Со старыми привычками трудно расстаться, а еще труднее их обнаружить у себя. Чтобы разучиться, необходим первый шаг — осозиать, что вы применяете устаревшие прие мы. Это самая трудная часть пути. Другая не менее трудная задача —избавиться от старого. Наши модели и схе мы мышления складываются и оттачиваются в течение многих лет и, как правило, стоят немалых усилий. С ними нелегко расстаться. Вам не нужно пытаться избавиться от них полностью и оконча тельно. Приведенный пример с оверлеями демонстрирует лишь
один из способов поддержки ограниченного рабочего пространст ва при использовании более обширного кэша. Устарела не да шиш технология, а только се конкретная реализация. Вам вовсе не нуж но копат ься в собст венном мозгу и вырезать из него все дендрит ные отростки. Напротив, нужно стараться использовать старые принципы в новом контексте. Воссоздать их и применять там, где это возможно. Важно лишь следить за тем, чтобы не делать ничего просто “по привычке”. Можно сделать процесс перехода намного эффективнее, если вы меняете привычную для вас среду работы на что-то другое. На пример, изучая новый язык программирования, используйте IDE (интегрированную среду разработки), которая к нему прилагается, а не плагин, подключаемый к вашей прежней IDE. Напишите со вершенно другой тип приложения, отличный от всего того, что вы обычно писали. Не пользуйтесь средствами старого языка в тече ние всего “переходного периода”. Гораздо легче сформировать но вые ассоциации и новые привычки тогда, когда вы максимально свободны от багажа старых привычек. Учите новое , забывайте старое. Изучая новую техноло гию, освобождайтесь от старых привычек, которые могут тянуть вас назад. Автомобиль — это не просто карета без лошади.
На что это похоже Новое всегда слегка пугает. Вам приходится узнавать много ново го —и вы узнаете. Старые знания и опыт могут служить основой, но не опорой.
Сохраняйте равновесие • Маленькая ямка отличается от могилы только размерами. Употребление старых привычек по истечении срока их год ности опасно для вашей карьеры. • Не забывайте старых методов полностью, а используйте их там, где они уместны в контексте применяемой технологии. • Обращайте особое внимание на отличительные свойства ка ждого из языков, с которыми вы работали, и старайтесь по нять, насколько эти черты меняются или остаются прежни ми от языка к языку и от версии к версии.
8
Спрашивайте до тех пор, пока не поймете “Всегда принимай те объяснения, которые тебе даются. Если тебе говорят, где именно скрывается проблема, зна чит, там и надо ее искать. Не трать время на напрасную по гоню за призракамиС.
Предыдущие два совета касались повышения проф ессионально го уровня — вашего и вашей команды. Теперь мы предлагаем вам приемы, которые почти всегда помогут разобраться во всех вопро сах, возникающих в процессе проектирования и отладки, а также лучше понять требования заказчика. Предположим, у разработчиков серьезная проблема с приложе нием, и они пригласили вас поучаствовать в ее обсуждении. Вы вряд ли хорошо знакомы с самим кодом, и они пытаются вам по мочь, указывая вам на конкретный модуль, в котором, по их мне нию, и надо искать источник данной проблемы. Они считаю т, что вам не следует вдаваться в детали работы остальных частей кода, а нужно быстро понять и оценить ситуацию, тем более, если у этих людей терпение не безгранично. Если вы оказались именно в такой ситуации, вы, возможно, стесняетесь задавать дополнительные вопросы, которы е помогли бы вам глубже понять проблему, —ведь о ней вам и так рассказали подробно. Но все-таки, чтобы что-то решить, вы обязаны представ лять себе полную картину. Вам нужно проанализировать все, что, по вашему мнению, может иметь отношение к данной проблеме, независимо от того, что думают о вас другие. Вспомним, как работает врач. Когда вам плохо, он задаст вам различные вопросы — о ваших привычках, о режиме питания, о том, где болит, какие лекарства вы принимали и т.д. Наш организм представляет собой сложную систему, и необходимо учитывать множество факторов, чтобы поставить правильный диагноз. Если не задавать много вопросов, можно не учесть некоторые важные симптомы. Например, пациенту, проживающему в центре Нью-Йорка, у ко торого жар и высокое кровяное давление, сильная головная боль, боль за глазами, в суставах и связках, можно по ошибке поставить диагноз “грипп”. Но после расспросов пациента врач узнает, что несчастный только что провел отпуск в Южной Америке. Теперь, вместо гриппа, врачу открывается новый спектр диагнозов, вклю чая тропическую геморрагическую лихорадку.
11счто подобное может иметь место и в компьютерном прило жении. Ч тобы разобра ться в проблеме, вам необходимо учесть все факторы, имеющие к ней отношение. Вы просто обязаны заста вить других людей терпеливо отвечать па ваши вопросы, которые вам кажутся достаточно важными, чтобы повлиять па общую кар тину “заболевания”. Или, допустим, вы работаете со старшими разработчиками. Они, скорее всего, лучше вас представляют работу всей системы. Но и они всего лишь люди. Они могут что-то упускать время от вре мени. Ваша дотошность может оказаться полезной: свежий взгляд и простые вопросы помогают другим членам команды прояснить многие вещи, посмотреть на них в перспективе и даже найти реше ние актуальных проблем. “Почему?” — величайший вопрос. В популярной книге по ме неджменту “The Fifth Discipline: The Art and Practice of the Learning Organization” [Sen90] (“Дисциплина номер пять: искусство и прак тика обучения организаций”) автор рекомендует задавать вопрос “Почему?” не менее пяти раз подряд, когда нужно разобраться в ка кой-либо проблеме. Данный метод скорее напоминает любопытст во четырехлетнего ребенка, но это надежный способ докопаться до самых азов, до сути предмета, лежащей глубже простых и ба нальных ответов. Один из примеров такого анализа, который докапывается до первопричин явления, приведен в книге “Fifth Discipline Field Book” (“Пятая дисциплина: работа на местах”). Консультант бесе дует с менеджером в производственном помещении. Заметив пят на масла на полу, менеджер сразу отдает распоряжение вымыть пол. Но консультант спрашивает его: “Почему масло на полу?” Ме неджер, не понимая вопроса, ругает технический персонал, отве чающий за уборку помещения. Консультант снова и снова повторя ет тот же самый вопрос — в различных цехах и в присутствии многочисленных сотрудников предприятия. Наконец, он докапы вается до истинных причин этого явления: в результате бездарной политики отдела закупок предприятие приобрело солидную пар тию бракованных сальников. Этот результат произвел настоящую сенсацию как для менед жера, так и для других заинтересованных сторон: они даже не зна ли об этом прежде. Только сейчас они осознали серьезную про блему, которая в дальнейшем могла только ухудшиться и нанести им немалый ущерб. А ведь только и требовалось, что задать во прос: “Почему?” “Да просто перезагружайтесь раз в неделю —и все будет хоро шо!” В самом деле? Почему? “Нужно запускать процесс сборки три
раза подряд, чтобы осуществить полную сборку”. Правда? Л поче му? “Наши пользователи никогда не захотят иметь этого”. Вы уве рены? А почему? Почем)г? Все время задавайте вопрос “ Почему?" Никогда не довольствуйтесь малым, не принимайте ничего за чистую монету. Спрашивайте до тех пор, пока не докопаетесь но самой сути.
На что это похоже Вы как будто добываете драгоценные камни из глубины земных недр. Вы отсеиваете все несущественное, копаете все глубже и глубже, пока не увидите этот сказочный блеск. И тогда приходит ощущение полной ясности, понимания проблемы, а не отдельных се симптомов.
Сохраняйте равновесие • Вы можете слишком увлечься и начат ь задавать вопросы, не относящиеся к делу: если машина не заводится, ваш интерес к состоянию резины вряд ли встретит понимание. Лучше спро сите “Почему?”, но по делу. • Когда вы задаете вопрос “Почему?”, вместо ответа, вам могут задать встречный вопрос: “Почему вы об этом спрашиваете?” Будьте готовы обосновать свой подход: это позволяет зада вать только те вопросы, которые имеют смысл. • Не довольствуйтесь поверхностными “отмазками”. “Потому что это обычное дело” —вряд ли можно считать удовлетвори тельным объяснением. • “Вот так штука, а я не знаю почему” —это хорошее начало для поиска, а не завершающая точка.
9
Почувствуйте ритм “Мы долгое время не проводили ревизию кода, поэтому мы собираемся провести ее на этой неделе. К тому же, в тече ние этого срока мы выпустили очередную версию, так что мы берем еще три недели, начиная со вторника, на выпуск следующей версий’.
Во многих недостаточно успешных проектах события развива ются нерегулярным образом. Здесь вы имеете дело с опасностью случайного характера: вы никогда не знаете, что произойдет зав тра или когда ждать следующей пожарной тревоги. По у г ибких проектов есть ритм и пульс, которые облегчают жизнь. Например, методология Scrum защищает команду от изме нения требований заказчика в течение всего тридцатидневного спринта. Бывает удобно выделить какой-то период для проведения наиболее масштабных изменений и делать их все сразу. И, наоборот, многие практики должны работать “без останова”, т. е. в течение всего жизненного цикла проекта. Как говорится, время выдумано природой для того, чтобы все события не произошли сразу в один момент. В гибком проекте мы пойдем еще дальше и сделаем гак, чтобы колеса времени крутились с разной частотой, чем отделим все события друг от друга и не допустим, чтобы они происходили случайно, в непредсказуемые моменты времени. Начнем с того, что рассмотрим отдельный рабочий день. Всем нам хочется, чтобы к концу рабочего дня наши основные пробле мы разрешались, а не нависали над головой, как прежде. Это, разу меется, далеко не всегда можно осуществить, но почти всегда мож но спланировать свой день таким образом, чтобы успеть проверить и протестировать код, с которым вы работаете. Если уже поздний вечер, а ваш код никак не хочет работать, возможно, лучшим выходом будет его стереть и написат ь заново. Эго звучит довольно радикально, и, возможно, так оно и есть1. Но, поскольку вы пишете код небольшими фрагментами, такое са моограничение во времени может вам реально помочь: если у вас нет хорошего работоспособного решения к твердо установленно му вами сроку ( г.е. к концу рабочего дня), то, может быть, лучше по пробовать другие нуги решения. И это задает вам ритм: в конце почти любого рабочего дня у вас код проверен или, по крайней 1 Ром Джеффри учит: “Я желаю, чтобы вам хват ало мужества делать это как можно чаще”.
мере, выброшен за ненадобностью. Па следующий день вы со све жими силами готовы приняться за разрешение от других трудно стей.
Временные ограничения Гибкие разработчики получают обратную связь по множеству каналов: от пользователей, коллег, от самого кода в процессе его тестирования. Эта обратная связь используется для корректирования дальнейшей работы над проектом. Кроме того, важным источником обратной связи служит само время. Многие гибкие техники опираются на time boxing — установление ближай шей конечной даты или времени (deadline) для выполнения какой-либо ра боты, не подлежащей продлению. Вы можете пожертвовать чем-то другим, но эту дату или время изменить нельзя. Вы, как правило, не знаете точного количества временных циклов, необходимых для выполнения всей задачи, но каждый отдельный интервал короткий, хорошо обозримый и служит для достижения конкретной цели. Каждая итерация может длиться, например пару недель. Когда время вы ходит, итерация считается законченной. Ее граница фиксирована, но набор новых свойств приложения, которые в нее закладываются, может менять ся. Другими словами, вы не можете подвинуть сроки, но вполне можете “подвинуть" какую-либо функцию. Аналогичным образом можно ограни чить во времени продолжительность рабочего совещания. Тогда к концу назначенного времени совещание должно закончиться, и выбран рабочий вариант проекта для дальнейшей реализации. Жесткие временные рамки заставляют вас принимать нелегкие решения. Вы не можете позволить себе тратить время на философские дискуссии или на реализацию функций, которые всегда доделаны ровно на 80%. Же сткие ограничения заставляют вас двигаться вперед. Акулы все время плавают — иначе они погибают. Программные проекты подобны акулам: вам нужно постоянно двигаться вперед, оставляя лишь самое лучшее.
Деловую пятиминутку (stand-up meeting) (см. Практика 38, Наладьте регулярное очное общение, с. 165) лучше всего прово дить ежедневно в одно и то же время в одном и том же месте, на пример в 10 часов утра. Постепенно эта привычка закрепляется, и у вас к этому моменту обычно все готово. Самый длительный цикл —это продолжительность одной ите рации (см. Практика 17, Используйте короткие итерации, посте пенно добавляйте функциональность, с. 76). Итерация в среднем должна занимать от одной до четырех недель. Какой бы ни была ее длина, вы обязаны строго придерживаться ее —здесь важнее всего соблюдать последовательность. Следование ритму облегчает вы
полнение задач и сообщает проекту постоянное движение вперед (см. врезку на с. 44). Принимайтесь за решение задач, не дож идаясь , пока они навалятся на вас всей кучей . Если периодически появляются новые и новые задачи , гораздо легче решать их в спокойном и размеренном режиме, равномерно распределяя события во времени.
На что это похоже Это похоже на последовательный и ровный ритм движения. Прав ка, тестирование и ревизия кода составляют отдельную итерацию и реализуются в течение конечного промежутка времени, после которого выпускается новая версия. Если вы знаете, в какой мо мент будет следующее па, вам легче танцевать.
Сохраняйте равновесие • Планируйте каждый рабочий день так, чтобы успеть прове рить и протестировать свой код до конца дня, не откладывая ничего на завтра. • Не допускайте, чтобы выполнение этого плана заставляло вас регулярно засиживаться на работе сверхурочно. • Итерации для всей команды должны быть стабильными и ре гулярными (см. Практика 17, Используйте короткие итера ции, постепенно добавляйте функциональность, с. 76). Вам, возможно, придется скорректировать продолжительность одной итерации, чтобы найти оптимальное ее значение, но потом необходимо твердо ее соблюдать. • Регулярный, но чрезмерно быстрый темп может привести к перегреву. В целом, если вам приходится взаимодействовать не только внутри команды, но и за ее пределами (или за пре делами своей организации), вам подходит более медленный темп. Так называемый масштаб Интернета (Internet Time) слишком интенсивен для работы, он может принести больше вреда, нежели пользы. • Регулярный режим мешает вам скрыть свои проблемы от дру гих, но, с другой стороны, вы можете рассчитывать на пони мание коллег в тех случаях, когда смело признаетесь в своих
ошибках (см. Практика 4, К черчу торпеды, идите вперед, с. 25). Здесь, как в разгрузочной диете, даже малый успех может послужить мощным стимулом. Небольшие, реально достижи мые цели облегчают движение вперед. Отмечайте ваши успе хи: пицца с пивом, коллективный обед nO M O iyr вам отпразд новать любое достижение команды.
Стычка с неприятелем разрушает любые планы. Хельмут фон Мольткс
Глава 4
Делать, как хотят пользователи Заказчик сообщает* вам требования и ждет, что в течение пары лет* вы поставите ему прикладную программу. Вы идете и строите сис тему, соответствующую этим т ребованиям, и, в конце концов, по ставляете ее заказчику в срок. Заказчик смотрит на нее и говорит, что она ему нравится. Вы идете работать над другим проектом, а в вашем резюме красуется еще один удовлетворенный и преданный заказчик. Обычно все так и происходит с вашими проектами, не правда ли? Неправда. В реальности для большинства проектов все проис ходит совсем иначе. Легче предс тавить себе ситуацию, когда поль зователь потрясен и /и л и расстроен. Им не нравится то, что они видят, и огги требуют множества изменений. Они хотят иметь функциональность, которой не было в исходных требованиях к системе. Все это звучит намного правдоподобнее для типичного проекта. “От стычки с неприятелем рушатся любые планы”, —сказал фон Мольтке. В данном случае неприятелем не может быть заказчик, ггользователи, ваши коллеги или руководители. Неприятель —это любая перемена, В военном деле, так же как и в разработке про граммного обеспечения, ситуация может быстро измениться в лю бой момент, причем кардинально. Следование вчерашнему плану действий, несмотря на изменение обстоятельств, может привести к ка тастрофическим последствиям. Вы не можете “избавиться” от перемены ситуации —будь то рабочий дизайн системы, архитекту ра или ваше понимание требований заказчика. Гибкость, как и лю бой успешный процесс разработки, напрямую зависит от вашей способности выявлять и приспосабливаться к любым изменениям. Только в этом случае можно сделать что-то в срок, не превысить за планированные затрат ы и создать продукт, который удовлетворя ет заказчика.
В этой главе мы рассмотрим практики, которые способствуют гибкому решению всех этих задач. Сначала необходимо понять, почему так важно привлечь пользователей и заказчиков к сотруд ничеству и Позволить заказчику принимать решения; см. с. 49. Рабочий дизайн системы служит основой для разработки про граммного продукта. Без него вы не можете создать систему, но нельзя допускать, чтобы он превратился в смирительную рубашку. Мы обсудим, как Дизайн должен направлять работу, а не дикто вать на с. 52. Если уж говорить о смирительных рубашках, вам нуж но еще убедиться, что вы используете технологию, подходящую для решения задачи. Для этого необходимо Обоснованно приме нять технологию; см. с. 57. Чтобы ваш программный продукт был дос тупен пользователям, он должен быть готов к использованию в любой момен т, всегда. Чтобы свести к минимуму любые задержки в работе, обусловлен ные трудностями сборки целостной системы, Интегрируйте сра зу, интегрируйте часто; см. с. 64. И кажется достаточно очевид ным, что код не должен находиться в разобранном состоянии никогда, иными словами, Держите все наготове; см. с. 60. Вы не можете тратить драгоценное время на то, чтобы знако мить каждого пользователя с каждой добавленной функцией, по этому Автоматизируйте процесс развертывания сразу; см. с. 67. Если ваш код всегда готов к выпуску и легко развертывается, то можно Наладить регулярную обратную связь при помощи де монстрационных версий; см. с. 76. Это облегчает массовое раз вертывание вашей системы любому количеству пользователей в регулярном режиме. Чтобы йе отстать и отслеживать все измене ния, исходящие от ваших пользователей, нужно Использовать ко роткие итерации и постепенно добавлять функциональность; см. с. 76. Наконец, иногда бывает трудно привлечь заказчика к участию в процессе разработки и реализовать гибкую методику работы над проектом, особенно когда заказчик заключает с вами контракт с фиксированной ценой. Как показывает практика, Фиксирован ные цены мешают выполнению обещаний; о том, как вести себя в данной ситуации, мы поговорим на с. 81.
10 Позвольте заказчику принимать решения "Разработчики — это творческие и умные люди, они лучше всех знают, как должна работать прикладная программа. Поэтому они и должны принимать наиболее важные реше ния. Как только в процесс вмешиваются неспециалисты, они вносят полный беспорядок, они ведь не понимают всей логики нашей работы
Разработчики, безусловно, должны принимать участие в приня тии решений, касающихся дизайна системы. Однако не все реше ния по текущему проекту должны приниматься только разработчи ками, особенно это относится к бизнес-вопросам. Возьмем для примера случай одного руководителя проекта по имени Пэт. Проект Пэта —система удаленного доступа —был запу щен в срок и не выходил за рамки бюджета: эта была обучающая программа (учебник) по астрономии. Пэт, полный оптимизма, от правился к заказчику демонстрировать работу системы, но вернул ся мрачный. Оказалось, что бизнес-аналитик Пэта решал все вопросы едино лично, не обсуждая их с пользователями. В процессе разработки системы заказчик не принимал участия в решении текущих вопро сов. Проект был далек от завершения, но уже успел не оправдать пользовательских ожиданий. Пришлось отложить его, и он пре вратился в типичный пример “из учебника” —о том, как провалить проект. Таким образом, вы можете выбирать из двух возможностей: или позволить заказчикам решать в процессе вашей работы, или они будут принимать решения уже потом, что обойдется намного доро же. Избегая решения этих вопросов в процессе работы над кодом, вы идете на риск; чем раньше вы начнете к ним обращаться, тем меньше вероятность того, что вам потом придется менять дизайн вашей системы и переписывать весь код. Это позволит, кроме того, избежать уплотнения графика работы к концу выполнения проекта. Например, вы работает е над задачей и видите два пути ее реали зации. Первый путь быстрее приведет к цели, но ограничивает возможности пользователей. Другой путь потребует больше време ни на реализацию, но обеспечит пользователям более гибкие воз можности. Разумеется, времени у вас совсем мало (вам доводилось участвовать в проектах, где было по-другому?); какой же путь вы брать? Тот, который быстрее? Или все-таки тот, что удобнее для
пользователей? Что делать? Бросить монетку? Спросить у коллег или у начальства? В одном из недавних проектов, в котором участвовал Венкат, возникло нечто подобное. И тогда менеджер по разработке сделал выбор в пользу первого варианта ради экономии времени. Как можно догадаться, пользователь был шокирован и даже взбешен, когда увидел ограничения, которые всплыли во время бега-тести рования системы. В результате на переделывание этой работы команда разработчиков потратила немало денег, времени и сил. Сначала решите,
Едва ли не самое важное решение, касающееся дизайна, которое может принять раз решать работчик (а также руководитель), —это оп ределить, какие вопросы находятся вне их компетенции и должны решаться заказчиком проекта. Вам не сле дует самим заниматься решением тех вопросов, которые критич ны для бизнеса заказчика. В конце концов, это не ваш бизнес. Если вам приходится решать какую-то проблему, которая влияет на по ведение системы или на особенности ее использования, лучше проконсультироваться с заказчиком. Если руководители проекта или менеджеры пытаются решить эту проблему сами, как бы от лица заказчика, тактично убедите их в том, что уместнее предоста вить ее решать самим заказчикам из бизнеса (см. Практика 4, К черту торпеды, идите вперед; с. 25). Когда вы говорите с заказчиками, подготовьтесь к обсуждению различных вариантов решения проблемы. Приведите все аргумен ты “за” и “против”, сравните потенциальные затраты и выигрыш от каждого варианта с точки зрения бизнеса заказчика, а не с пози ций его технической реализации. Обсудите компромиссные вари анты решения, выясните, как они могут отразиться на общем гра фике выполнения проекта и на затратах. Какое бы решение ии приняли заказчики, им придется за него расплачиваться самим, по этому лучше предоставить им возможно полную информацию. Если позже им захочется чего-то другого, вы отдельно будете дого вариваться с ними о дополнительных затратах и времени на реали зацию новых требований. Как бы то ии было —это их и только их решение. ЧТО ВЫ не будете
Предоставьте заказчикам принимать решения . Разработ чики, менеджеры или даже бизнес-аналитики не должны брать на себя решение вопросов, критичных для бизнеса. Пре доставьте заказчикам из бизнеса подробную информацию на понятном им языке, и пусть они принимают решение.
»
На что это похоже Бизнес-приложения разрабатываются при сотрудничестве заказ чика с разработчиками. У вас хорошие и отрытые рабочие отноше ния с заказчиком, отношения истинного партнерства.
Сохраняйте равновесие • Ведите запись принимаемых решений и их обоснования. Как известно, человеческая память —не самый надежный помощ ник. Протоколы совещаний, дневник, Wiki, архив электрон ной переписки —все средства хороши, только следите, чтобы они не стали со временем чересчур громоздкими и трудными для просмотра. • Не перегружайте и без того очень занятых людей из бизнеса тривиальными, не слишком важными для них деталями. Если же эти детали могут отразиться на их бизнесе, их нельзя счи тать тривиальными. • “Я не знаю*’ — вполне приемлемый ответ заказчиков. Они, возможно, прежде никогда об этом не задумывались, или им нужно сначала посмотреть, как это работает, чтобы принять какое-то решение. Посоветуйте им то, что сами считаете луч шим решением, и позаботьтесь о том, чтобы ваш код был го тов воспринять возможные изменения в будущем.
11 Дизайн должен направлять работу, а не диктовать "Вся проектная документация по коду должна быть макси мально подробной , такой, что даже менее опытный про граммист сможет при желании написать такой же код. Описывайте отношения между объектами на высоком уровне, а также взаимодействие между объектами на бо лее низких уровнях. Обязательно включите подробное описание реализации методов для каждого класса, а так же каждого отдельного параметра метода. Не забудьте про поля классов . Никогда не отступайте от требований дизайна, несмотря на любые проблемы, обнаруженные в процессе его реализации”.
Этап “дизайна” всегда исключительно важен для всего процес са разработки. Он помогает понять детали работы системы, вза имные связи между различными частями и подсистемами, и ука зывает пути реализации. Традиционные методологии придают особое значение дизайну: некоторые из них, например методоло гия универсальных процессов (Unified Process), весьма строго подходит к выпуску проектной документации. Менеджеры проек тов и заказчики зачастую просто одержимы идеей подробного описания всего и вся и хотят убедиться в том, что система полно стью спроектирована и документирована еще до того, как начат процесс написания кода. Ведь именно такой подход всегда прак тикуется при строительстве мостов или других сооружений, не так ли? С другой стороны, гибкие методологии рекомендуют начинать писать код на возможно более ранней стадии разработки. Означа ет ли это, что дизайн не нужен?1 Вовсе нет —очень важно, чтобы код в результате имел хороший дизайн. Важно разработать поясни тельные диаграммы (например, на UML) для иллюстрации струк туры всей системы в терминах классов и взаимодействий между ними. Нужно посвятить время обдумыванию и обсуждению ком промиссных решений, достоинств и подвохов —для различных оп ций, которые возникают на этапе создания дизайна. Только тогда вы подойдете вплотную к такой структуре систе мы, которая подходит для того, чтобы начать работу над самим 1 См. стат ью Мартина Фаулера (Martin Fowler) “Is Design Dead?" (“Дизайн мертв?”) (http://www martinfowler.com/articles/designOead.html), где подробно обсуждается данная тема.
кодом. Если вы не потруди тесь поразмышлять над ней в самом начале, позже вас могуг подстерегать неприятные сюрпризы. Даже в строительстве обычно вначале отрезается брус длиннее, чем необходимо, а затем осторожно подрезается до точного раз мера. Но даже при наличии четкого дизайна еще до начала написания кода некоторых сюрпризов не избежать. Всегда нужно помнить, что любой дизайн на этом этапе базируется лишь на текущем уров не понимания требований. Вся уверенность проходит, как только вы начинаете писать код. Дизайн системы, как и код его реализа ции, будет постоянно эволюционировать. Некоторые руководители проектов и менеджеры всерьез счи тают, что дизайн кода должен быть настолько подробным, чтобы можно было просто отдать его “кодерам”. Они говорят, что кодер не должен принимать никаких решений, а просто переводить дизайн в код. Лично мы не хотели бы работать печатной машин кой в команде такого сорта. Надо полагать, вы тоже не горите же ланием.
Строго следовать “Нет ни малейшего смысла строго следовать тому, о чем вы имеете весьма смутное представление". — Джон фон Ньюмэн
Дизайн должен быть подробным ЛИШЬ В той мере, В какой ОН реализуем
Что будет, если дизайнеры воплотят свои идеи в чертежи и перебросят их программистам через пропасть (см. Практика 39, Архитекторы должны писать код; с. 169)? Программистам придется переводить этот дизайн в код, точно следуя чертежам. И что делать, если вдруг ока жется, что полученный дизайн далек от идеала? Ужас! На дизайн уже потрачено немало времени, нет возможности вернуться на зад и перепроектировать все заново. Солдаты команды идут впе ред, все знают —и пишут неверный код. Звучит глупо, не так ли? Да, но все-таки многие компании функционируют именно в таком режиме. Сама идея строгого следования цепочке задач “требования-дизайн-код-тестирование” берет начало в водопадном1стиле мышле* 1 Водопадный (каскадный) стиль работы сегодня означает последователь ное прохождение следующих ступеней: спецификация требований к системе, подробный дизайн, его реализация в коде, интеграция системы и, наконец, ее тестирование (сплюнем три раза). Заметим, что нынешний смысл термина от личается от авторского; подробнее см. [Roy70J.
иия, который предполагает чрезмерно подробный дизайн в на чальной стадии проекта. В течение всего жизненного цикла проек та подробный дизайн и оформление документации в соответствии с самыми современными требованиями к проектной документа ции-превращаются в серьезную и важную задачу, решение которой отнимает огромную массу времени и ресурсов, принося минималь ную отдачу. Уж лучше заниматься более полезными вещами. В дизайне существуют два уровня: стратегический и тактиче ский. Предварительный дизайн всегда стратегический, он обычно создается в тот момент, когда у вас еще нет глубокого понимания требований заказчика. Следовательно, он должен выражать гене ральную стратегию, но не вдаваться в подробности. Начальный, стратегический уровень дизайна не должен подробно описывать методы, дизайна ОТ параметры, поля или точный порядок взаитактического модействия объектов. Это задача возлагается на тактический ди зайн, который углубляется в процессе работы над проектом. Хороший стратегический дизайн служит своего рода картой, которая помогает вам идти в правильном направлении. Любой ди зайн —всего лишь исходная точка; вы будете его развивать и совер шенствовать при создании кода в течение всего жизненного цикла проекта. Возьмем историческое путешествие Лыоиса и Кларка1в 1804 г. Его “дизайн” состоял лишь в том, чтобы пересечь территорию Со единенных Штатов. Но у них не было ни малейшего представле ния о том, что они встретят в любой точке своего пути. У них была цель и набор ограничений, но не детали решения задачи. Это вполне подходящая аналогия для дизайна программного проекта. Пока вы не пересечете саму территорию, вы не можете знать наверняка ее особенностей. Так что не теряйте времени на то, чтобы спланировать свой переход реки вброд, пока не подойде те к берегу и не увидите саму реку. Только тогда имеет смысл обсуж дать тактические приемы. Вместо того, чтобы сразу прорабатывать тактический дизайн, задавая методы и типы данных, разумнее обсудить различные ва рианты конструирования классов в терминах разделения обязан ностей между ними, поскольку это опять^гаки высокоуровневое проектирование, которое задает лишь цель. Фактически, метод ди зайна на базе CRC карт именно так и работает. Классы в нем описы ваются следующим образом: ОТЛИЧИЯ
стратегического
1 И здесь мир тесен: Энди приходит ся, родственником Уильяму Кларку.
• Имя класса (Class name) • Обязанности класса (Responsibilities) —что он должен делать • Сотрудники (Collaborators) —с какими другими объектами он работает для выполнения своих задач Как можно попять, насколько хорош дизайн или, по крайней мере, адекватен? Самая надежная обратная связь при обсуждении качестве дизайна поступает от самого кода. Если небольшие изме нения требований можно реализовать без особых проблем, дизайн кода можно считать удачным. Если же небольшие изменения при водят* к заметным разрушениям или распаду всей структуры кода — качество дизайна требует улучшения. Хороший дизайн подобен карте; его нужно “развернуть”.
Дизайн указывает вам лишь направление движения. Он не за меняет реальной территории и не может навязать конкретный маршрут достижения цели. Не позволяйте дизайну (или дизай неру) держать вас в заложниках.
и это похоже Хороший дизайн аккуратен, но не совсем точен. То есть все дан ные в нем абсолютно верные, но при этом отсутствуют детали, ко торые не могут быть точно определены или которые могут изме ниться в будущем. Он задает цель, а не средства.
Сохраняйте равновесие • Отказ от “чрезмерно подробного предварительного дизай на” не означает отказа от дизайна вообще. Просто мы следу* ем дизайну лишь в той мере, в какой он может быть успешно реализован в реальном коде. Кодирование при полном отсут ствии дизайна опасно. Вы можете так поступать л ишь для обу чения или при создании прототипа, чтобы можно было по том спокойно выбросить код на помойку. • Даже если исходный вариант дизайна окажется ненужным, его все равно надо проработать: сам процесс проектирова ния исключительно полезен. Как сказал президент США Эй зенхауэр в 1957 г., “Сам план ничего не стоит. Планирование бесценно”. В процессе проработки дизайна вы многое узнае те о будущей системе, и это знание оказывается более важ ным, нежели сам дизайн.
Белая доска, рисунок от руки, пояснительные пометки —все это превосходные инструменты проектирования. Слишком сложные средства моделирования зачастую только отвлека ют внимание, а не помогают восприятию информации.
12 Обоснованно применяйте технологию "Ты начинаешь новый проект. Перед тобой огромный спи сок новейших технологий и каркасов приложений (applica tion frameworks). Это все так здорово и современно, нужно обязательно использовать их в новом проекте. Только представь, как красиво будет все это выглядеть в твоем ре зюме и какую высокотехнологичную систему можно по строить на этом каркасе".
Слепо использовать
Однажды сотрудница Венката по имени Лайза знакомила его с своим проектом приприложений —ЭТО все дожения: она предлагала писать его с примеравно, ЧТО заводить пением Enterprise Java Beans (EJB). Венкат детей, чтобы сэкономить выразил сомнение в том, насколько EJB подна налогах ходит для данного проекта, на что Лайза от ветила: “Мы уже убедили нашего менеджера, что это самый лучший путь, так что не пытайтесь его критико вать”. Эго прекрасный пример “Дизайна ради резюме”, когда технология выбирается исходя из моды и из соображения, что ее освоение расширит набор профессиональных навыков програм мистов. Но слепо использовать модный каркас независимо от типа приложения — это все равно, что заводить детей, чтобы сэконо мить на налогах. Это просто не работает. Прежде чем рассматривать применение новой технологии или нового каркаса, определите для себя, какую проблем)' вы пытае тесь решить. Многое зависит'даже от того, как вы выразите свою мысль. Если вы говорите: “Нам нужна технология xyzzy, потому что... ”, это с самого начало противоречит здравому смыслу. Нужно начинать примерно так: “Нам слишком т рудно сделать то-то и тото... ” или “Слишком много времени займет то-то и то-то...”, или как-нибудь еще в том же духе. После того как вы указали реальную проблему, требующую решения, попробуйте ответить на следую щие вопросы: НОВЫЙ каркас
Реш ает ли новая технология указанную проблему? Да, это может звучать банально, но решает ли данная технология вашу конкретную проблему? Или, если говорить точнее, не слишком ли вы доверяете маркетинговым заявлениям или непроверенной информации, оценивая реальные преимущества нового подхода? Убедитесь, что технология делает именно то, что вам нужно и без вредных побочных эффектов; при необходимости напишите не большой прототип.
Н е окаж етесь ли вы привязанны м и к данной технологии? Некоторые технологии (особенно это относится к каркасам при ложений) подобны поездке в один конец. Как только вы перешли на них, вернуться назад уже невозможно. Эта необратимость | НТОО] может впоследствии сыграть роковую роль, если изменят ся условия. Многое здесь будет зависеть оттого, открытая ли это технология или проприетарная. Как насчет затрат на сопровождение? Не окажется ли сопро вождение данной технологии со временем слишком затратным де лом? Ведь, в конечном счете, стоимость решения не должна превы шать стоимость самой проблемы, иначе это плохое вложение капитала. Мы слышали о проекте, который тратит 50 гыс. долла ров ежегодно на поддержку машины правил для базы данных (rules engine), —причем сама база данных использует всего тридцать пра вил. Слишком дорогое средство решения проблемы! Когда вы смотрите на потенциально полезный каркас прило жения (или любую технологию), вас могут привлекать разнооб разные возможности, которые он предлагает. После чего вы обнаруживаете, что одних только этих дополнительных возмож ностей вам вполне хватает для обоснования применения данно го каркаса в вашем приложении. Но насколько нужны вам все эти дополнительные возможности? Возможно, вы даже находите проблемы, которые служат оправданием выбранного вами реше ния, подобно тому, как ведет себя нерасчетливый покупатель у кассы. Не так давно Венкат имел дело с проектом, где консультант по имени Брэд сумел убедить руководство в необходимости исполь зования одного проприетарного каркаса приложений. Венкат считал, что, несмотря на определенные достоинства этого карка са, использование его в данном проекте было явно неоправдан ным. Несмотря на это, руководство настаивало на его использова нии. Венкат деликатно вышел из игры, не желая быть препятстви ем на их пути к успеху. Спустя год этот проект был далек от завер шения: они потратили несколько месяцев на написание кода сопровождения самого каркаса, а также на модифицирование и подгонку кода к новому каркасу. У Энди был аналогичный опыт: клиент хотел максимально вы играть от технологий open source, разумеется, всех сразу. В резуль тате у них образовалось настоящее “мясное рагу” из новых техно логий — настолько “жирное”, что им так и не удалось заставить отдельные его куски работать вместе.
Не стройте сами ТО, что МОЖНО загрузить
Если вы вдруг обнаруживаете, что создаете что-то новое и необычное (и вы чувствуете себя на пуги построения собственной ин фраструктуры с пуля), срочно проснитесь и встряхни тесь. Чем меньше кода вы напишете, тем меньше вам при дется сопровождать. Например, если вы страстно меч таете написать свой собствен ный персистентный уровень объектов (persistence layer), вспомни те замечание Т эда Ньюарда (Ted Neward) о том, что “отображение связей между объектами — это Вьетнам компьютерных техноло гий”. Вы потратите уйму времени только на создание условий, не обходимых для того, чтобы начать работу над самим приложени ем: всего того, ч то отражает специфику конкретной предметной области и конкретного приложения.
*
Выбирайте технологию исходя из реальной необходимо сти. Сначала определите, что вам нужно, а потом оцените, на сколько конкретная технология подходит для решения ваших проблем. Задайте наиболее важные вопросы, касающиеся ис пользования каждой конкретной технологии, и ответьте на них честно.
На что это похоже Новая технология должна ощущаться как лучше работающее новое средство, а не как новая работа.
Сохраняйте равновесие • Может быть, пока еще рано реально оценивать ваши техни ческие требования. Это нормально. Возможно, простая хештаблица может заменить базу данных, пока вы пишете прото тип системы и обсуждаете его с пользователями. Не спешите выбрать какую-либо технологию, если у вас пока нет доста точного опыта для принятия, решения. • Каждая технология имеет свои плюсы и минусы. Независимо от того, открытый это или коммерческий продукт, инфра структура, инструмент или язык, узнайте обо всех неизбеж ных минусах их использования. • Не создавайте того, что можно просто загрузить. Построе ние системы “снизу доверху” бывает необходимо, но это са мый дорогой и рискованный путь.
13 Держите все наготове "Мы только что наткнулись на препятствие, которое тыдол жен устранить прямо сейчас. Бросай все остальное и ре шай данную проблему, не отвлекайся на свои текущие дела. Не нужно даже говорить об этом кому-либо — просто делай. Как можно скорее".
Это звучит довольно невинно. Внесение важных исправлений в код —несложная задача, но требует немедленного решения, и вы обещаете ее выполнить. Все у вас проходит гладко. Вы вносите необходимые поправки в код и возвращаетесь к своим обычным, наиболее важным делам. А потом начинается не*гго. Через какое-то время —слишком позд но —вы обнаруживаете, что другой разработчик внес в код измене ния, несовместимые в вашими, и в результате работа всей системы оказалась полностью разрушенной. Теперь придется потратить гораздо больше сил и времени на то, чтобы вернуть систему в рабо чее состояние. Вы влипли! Придется сказать всем, что вы не може те выполнить то, что обещали. Лишь дьявольский хохот преследу ет вас: иХа-ха-ха!” Теперь ваше положение хуже некуда: система в нерабочем со стоянии. Вы неожиданно создали критическую ситуацию, которая может привести к опасным осложнениям. В 1836 г. генерал Антонио Лопез де Санта Анна, впоследствии президент Мексики, проводил успешное наступление на западе Техаса, вынуждая генерала Сэма Хьюстона и его армию отсту пать. Когда Санта Анна с армией дошел до берега залива Буффало (на юго-востоке Техаса), он приказал своему войску отдыхать. Со гласно легенде, он настолько был уверен в успехе, что даже не вы ставил караул. И когда генерал Хьюстон решил перейти в атаку поздно вечером, армия Санта Анны даже не успела построиться. Они проиграли это решающее сражение и потеряли Техас на всегда1. Отсутствие боевой готовности предоставляет самую благопри ятную возможность для удара неприятеля. Подумайте: насколько часто ваше приложение находится в разобранном состоянии? Не лежит ли ваш код в репозитории подобно армии Санта Анны в тот роковой полдень —в полном беспорядке и при абсолютной невоз можности немедленного запуска? 1 http://wvrw. sanjacintQ4Tiuseum.org/rhe_Battle/Apri 1
1st . 1 8 3 6
Когда b e , I работаете в команде, нужно очень осторожно вноси ть все изменения, постоянно помня о том, что вы можете воздейство вать на состояние всей системы, а это может повлиять на произво дительность всей вашей команды. Вы ведь не потерпите, если ктото будет оставлять за собой мусор на кухне вашего офиса, так мож но ли допускать, чтобы кто-то оставлял мусор в коде системы? Вот примерная последовательность ваших действий, которая позволит вам убедиться в том, что вы не собираетесь править сло манный кем-то код: П роведите собственны е тесты локально. В самом начале убе дитесь, что код компилируется и проходит все модульные тесты. Далее проверьте, что все остальные тесты также проходят без про блем. Убедитесь, что перед вами свежая версия кода. С помощью системы контроля версий загрузите самую позднюю версию исход ного текста и проведите компилирование и тестирование прило жения опять. Довольно часто именно на этом шаге вас ждуг сюр призы: например, кто-то другой недавно внес модификации, несовместимые с вашими. М одифицируйте код. Теперь, когда вы имеете дело со свежей версией кода, который нормально компилируется и проходит все тесты, можно вносить свои изменения. В процессе проверки вы можете обнаружить следующую про блему: кто-то другой модифицировал код таким образом, что код не компилируется или не проходит тестов. Как только вы это заме тили, сразу сообщите об этом и предупредите остальную команду, если необходимо. Еще лучше, если у вас для этого есть система не прерывной сверки, которая автоматически выявляет проблемы та кого сорта. Это может звучать слегка пугающе, но это вовсе не так. Систе мы непрерывной интеграции представляют собой обычные при ложения, которые проверяют целостность кода, осуществляют его сборку и тестирование —постоянно в фоновом режиме. Они достаточно просты, и вы можете легко настраивать и управлять ими с помощью скриптов. Но наиболее широкую функциональ ность и устойчивую работу предлагает существующее бесплатное решение на базе открытых систем. Более подробно эта тема рас смотрена в статье Мартина Фаулера (Martin Fowler)1 и в книге Марка Кларка (Mike Clark) “Автоматизация прагматичного про1 http://www. martinfowler . com/articles/continuouslntegration. html
скга: Как осуществлять сборку, развер тывание и контроль Javaприложений” [С1а041. Немного забегая вперед, предположим, что вы слышите о ка ких-либо грядущих серьезных модификациях, которые могут сло мать всю систему. Не позвольте этому случиться — отнеситесь к этому со всей серьезностью и изучите возможности избежать раз рушения системы в результате изменения кода. Рассмотрите вари анты, которые помогают вносить изменения и постепенно их за действовать таким образом, что всегда остается возможност ь для тестирования системы и получения обратной связи в течение все го процесса разработки. Кроме того, важно всегда держать приложение “в боевой готов ности” к выпуску новой версии, что не всегда просто осуществить. Например, рассмотрим изменения в схеме базы данных в формате внешнего файла или формате сообщения. Подобные модифика ции зачастую затрагивают большие части кода и MOiyr перевести его в нерабочее состояние до тех пор, пока значительная часть из менений не реализована. Однако у вас всегда есть возможность сде лать этот переход менее болезненным. Организуйте контроль версий для схемы базы данных, формата внешних файлов и т.д., а также для соответствующих им программ ных интерфейсов, чтобы можно было легко отслеживать всевоз можные изменения, происходящие в коде. Такой контроль версий изолирует вносимые вами изменения от всего остального кода, так что другие аспекты приложения могут продолжать находиться в процессе разработки и тестирования. Вы также можете организовать ве твление кода в системе контро ля версий (но делайте это с осторожностью: чрезмерное ветвление может создавать проблемы, вместо их решения. Более подробно см. “Прагматичный контроль версий с помощью CVS” [THQ3], “Праг матичный контроль версий с помощью subversion” [MasOS]. ЪшЖ
ju Ж
Поддерживайте готовность проекта к выпуску новых версий в любой момент. Сделайте так, чтобы ваш проект в лю-
бое время можно было немедленно скомпилировать, запустить, протестировать и развернуть.
На что это похоже Вы чувствуете уверенность в том, что, даже если ваш босс, предсе датель правления, подразделение контроля качества, заказчик или супруг(а) зайдут в ваш офис, вы всегда можете продемонстриро-
вагь вашу систему в сборке без малейшего промедления. Ваш проект всегда готов к запуску и всегда пребывает в устойчивом со стоянии.
Сохраняйте равновесие • Иногда вы не располагаете достаточным временем и силами, чтобы все время поддерживать готовность системы к выпус ку в течение ее последовательной модернизации Если нужно потратить месяц на то, чтобы приложение сохраняло рабо тоспособность в течение недели, то лучше выбрать неделю простоя. Это не должно быть правилом, а лишь исключением из правила. • Если вам приходится перевести систему в нерабочее состоя ние на длительное время, нужно всегда иметь в запасе послед нюю работоспособную версию кода или схемы, с которой вы можете продолжать эксперименты,' и вернуться к ней при не обходимости. Не допускайте, чтобы система находилась в не рабочем состоянии, причем необратимо.
14 Интегрируйте сразу, интегрируйте часто "Не думай об интегрировании своего кода до конечного эта па его разработки или, по крайней мере, до середины. В конце концов, зачем беспокоиться об интеграции еще до того, как создан сам код? В конце проекта будет масса вре мени, чтобы заняться этим вопросомГ.
Как мы уже отмечали, важнейшим аспектом гибкого проекта яв ляется непрерывный, а не эпизодический процесс разработки. По этому особое значение здесь придается процедуре интеграции ва шего кода с кодом, написанным остальными членами команды. Большинство разработчиков стремятся отложить интеграцию, имея для этого несколько веских причин. Иногда просто сама мысль о том, чтобы заняться еще другим кодом или другой подсис темой, кажется слишком обременительной. Проще думать пример но так: “Я и так уже очень занят тем, чтобы успеть все сделать в срок, мне еще не хватало брать на себя дополнительную нагрузку и беспокоиться о коде, написанном другими”. Мы слышали оправда ния типа “У меня нет на эго времени” или “Установить все это на мой компьютер потребует усилий, а мне неохота этим заниматься сейчас”. Интеграция относится к наиболее рискованным этапам разра ботки программного продукта. Если вы позволяете подсистеме развиваться независимо, без интеграции, вы подвергаете себя по стоянно растущей опасности: весь остальной мир развивается без вас, и вероятность вашего отклонения от общего курса только рас тет со временем. Вместо того, намного проще решать эти пробле мы на самой ранней стадии, пока риск еще не велик и не причинит особой боли. Но чем дольше вы будете ждать, тем острее будет боль, которую он причинит вам. В детстве, проведенном в г. Шеннее, Индия, Венкат обычно до бирался в школу на поезде. Как и многим пассажирам, ездившим в час пик в крупном городе Индии, ему постоянно приходилось за прыгивать в поезд и выпрыгивать из него уже на ходу. Так вот, вы не можете просто вскочить в движущийся поезд из положения стоя: вы немедленно ощутите па себе довольно болезненное дейст вие некоторых законов физики. Вместо этого, вы начинаете бе жать рядом с поездом, затем хватаетесь за поручень, набирая нуж ную скорость, и уже тогда вскакиваете в вагон. Интеграция программного обеспечения напоминает запрыгивание в поезд. Если вы долго разрабатываете код в изоляции и од нажды делае те скачок в сторону интеграции, не удивляй тесь удару,
который вы при этом получите. Вы, вероятно, наблюдали это в сво их проектах, когда время, оставшееся до окончания фазы разра ботки, начинает сильно поджимать. Люди проводят дни и ночи, интегрируя свой код. И тогда единственным прия тным аспектом является бесплатная пицца, которую вы получаете, если долго за сиживаетесь в офисе. Бывает довольно трудно совмещать автономную разработку и раннюю интеграцию кода. Когда вы пишете код в изоляции, это получается быстрее и более продуктивно, и все проблемы решают ся намного эффективнее (см. Практика 35, Атакуйте проблемы в изоляции, с. 152). Но эго пе значит, что вы должны избегать или откладывать интеграцию (см. врезку). Вообще, нужно проводить интеграцию своего кода по нескольку раз в день и никогда не допус кать перерывов более двух или трех дней.
Можно интегрировать и работать автономно Интеграция и изоляция (независимая работа над кодом) отнюдь не исклю чают друг друга: можно интегрировать и работать автономно в одно и то же время. Используйте заглушки) для моделирования зависимостей при автономном запуске. Таким образом вы можете тестировать свой код до интеграции. Заглушки имитируют поведение реального объекта или подсистемы. При мерно так же, как киноактеров в некоторых ситуациях подменяют дублеры, реальные объекты можно заменить их имитаторами: они не обеспечивают функциональности реальных объектов, а лишь имитируют их поведение в целях тестирования. Кроме того, используя заглушки, вы можете провести независимое мо дульное тестирование вашего кода вместо того, чтобы сразу интегриро вать и тестировать его вместе с остальной системой; как только у вас появилась уверенность в том, что код работает, можно проводить его инте грацию.
Интеграция не должна происходить подобно “Большому взрыву”
Когда вы интегрируете на раннем этапе разработки, вы сразу можете видеть, как различные части системы взаимодействуют и работают вместе, и вы можете оценить, как происходит распределение и передача данных между ними. Чем раньше вы заметите и приступите к решению этих вопросов, тем меньше усилий вы потратите на их устранение. Это правило рабо тает’ как для троих разработчиков с кодом на 50 тысяч строк, так и для 5 тысяч разработчиков с кодом в 30 миллионов строк. Если же вы отложите интеграцию, тогда эти проблемы постепенно превра
тятся в громоздкие задачи, которые потребуют глубоких и широкомасиггабных изменений в коде и вызовут задержки в рабо те и об щий хаос. Интегрируйте рано , интегрируйте часто. Интеграция кода представляет собой источник риска . Чтобы его смягчить, начи найте проводить интеграцию как можно раньше и продолжай теделать это регулярно.
На что это похоже Если вы делаете это правильно, интеграция не ощущается вами как отдельная и обременительная задача. Она становится частью регу лярного цикла написания кода. Все возникающие проблемы явля ются относительно мелкими и легко устранимыми.
Сохраняйте равновесие • Успешная интеграция означает, что система успешно прохо дит все модульные тесты. Как в клятве Гиппократа, “Глав ное —не навреди”. • Нормальной можно считать частоту интегрирования вашего года с другими от пяти до десяти раз в день, и даже чаще. Но если вы интегрируете всякий раз, когда вносите хотя бы одну новую строку в код, эффективность вашей работы снижает ся. Если вы замечаете, что тратите все свое время на повторе ние процедуры интеграции, а времени на написание самого кода почти не остается —значит вы интегрируете чересчур часто. • Если вы интегрируете недостаточно часто (например, один раз в день, раз в неделю или еще реже),’это может привести к ситуации, когда вы тратите все свое время на реш ение про блем, возникающих в процессе интеграции, вместо того, что бы продолжать писать код. Если ваши проблемы с интеграци ей велики, значит, вы интегрируете недостаточно часто. • Для написания прототипа или экспериментальной програм мы можно некоторое время работать автономно, не тратя сил на интеграцию. Но не изолируйте себя надолго: как толь ко у вас появился необходимый опыт, немедленно нацели вайтесь на интеграцию.
15 Автоматизируйте процесс развертывания сразу “Вполне нормально устанавливать программный продукт вручную, особенно подразделению контроля качества. Ведь вам не придется это делать часто, и нет ничего ужас ного в том, чтобы просто копировать все необходимые файлы".
Если ваше приложение работает на вашем компьютере и на сис темах ваших коллег и тестеров, это замечательно. Но нужно еще, чтобы оно хорошо работало при установке на компьютеры пользо вателей. Если все хорошо работает на вашем инструментальном сервере, это еще не значит, что оно будет так же хорошо работать в производственной среде. Из этого следует, что нужно'обеспечить надежную и многократ ную процедуру развертывания вашей системы на целевых маши нах. К сожалению, большинство разработчиков склонны пренеб регать вопросами развертывания своего продукта до самого конца проекта. В результате установка зачастую проводится некачествен но —с неполным набором необходимых компонентов, изображе ний, или с ошибками в организации структур каталогов. Если разработчик меняет структуру каталогов для работы при ложения, или же в творческом порыве делает каталоги с картинка ми доступными для различных приложений, это может нарушить процесс инсталляции. Нужно с тараться всегда выявить проблемы такого сорта как можно раньше, пока еще внесенные изменения достаточно свежи. Недели или месяцы спустя обнаружение подоб ной проблемы будет весьма неприятным событием, особенно если оно случается во время подготовки к важной демонстрации систе мы заказчикам. Подразделение контроля качества ДОЛЖНО тестировать развертывание Продукта
Если до сих пор вы инсталлировали свое приложение на компьютеры подразделения контроля качества (ОТК) вручную, постарайтесь изыскать время на то, чтобы автоматизировать эту процедуру. Когда вы это осу ществите, у вас будет готова основа выпуска зрелой системы инсталляции для конечных пользователей. Если вы сделаете это достаточно рано, тогда ваш ОТК сможет протести ровать ваше приложение вместе с процедурой его инсталляции1. 1 Чтобы избежать путаницы, ОТК должен всегда видеть, какую версию приложения они запускают в данный момент.
Если же вы устанавливаете приложение вручную, что происходит, когда приложение запускается в эксплуатацию? Вряд ли вы захоти те бегать по всем пользователям и устанавливать приложения на каждый компьютер или на любой сервер, везде и всюду, даже если вам платят сверхурочные. Наличие автоматизированной системы развертывания также облегчает обновление зависимостей в течение ж изненного цикла проекта. Допустим, вы забыли включить нужную библиотеку или компонент в пакет инсталляции —запуск автоматической инстал ляции на произвольном компьютере сразу обнаружит и сообщит, чего не хватает. Если что-либо может упасть в результа те отсутст вия компонента или несовместимости библиотек, вы, скорее все го, предпочтете узнать об э том рано, а не поздно. Автоматически развертывайте свое прилож ение с самого начала . Используйте эту процедуру для установки приложе ния на произвольные системы различной конфигурации, про веряя зависимости. ОТК должен тестировать процедуру раз вертывания вместе с приложением.
На что это похоже Это происходит незаметно. Установка и /и л и развертывание ва шего продукта должны идти легко, надежно и сколь угодно раз. Все это происходит как бы само собой. Энди рассказывает...
Поставка с самого первого дня Есть немало преимуществ в развертывании системы с самого начала про екта по сравнению с ее установкой на завершающем этапе работы. На са мом деле, в некоторых проектах полная инсталляция проводится еще до начала реализации проекта! Команде Pragmatic Programmers было предложено собрать простое демон страционное приложение для предполагаемого клиента — доказательство концепции, если можно так выразиться. Несмотря на то, что сам проект еще не начался, у нас уже были модульные тесты, система непрерывной интеграции и Wjndows-инсталлятор. Все это позволило нам поставить де монстрационную версию легко и просто: все, что требовалось от клиен та, — это щелкнуть на ссылке, размещенной на нашем web-сайте, и устано вить демо-версию самостоятельно и в любом объеме. Ваша готовность продемонстрировать возможности будущей системы еще до подписания контракта свидетельствует о крепком профессиональном подходе.
Сохраняйте равновесие Ваш продукт может предъявлять определенные требования для своей работы: например к версии Java или Ruby, внешней базе данных или операционной системе. Если это имеет зна чение и может быть поводом для вызова техподдержки, то все необходимые условия должны проверяться еще на этапе установки приложения. Система инсталляции не должна разрушать какие-либо поль зовательские данные без разрешения на то самого пользова теля. Аварийное восстановление системы должно запускаться мак симально просто, особенно для эксплуатационного сервера. Вы знаете, что эго может произойти, и наверняка не хотите проделывать это вручную, под давлением, в 3:30 утра. Пользователь должен всегда иметь возможность деинсталли ровать приложение —без риска и без остатка, особенно это важно для ОТК. Если сопровождение установочного скрипта затрудняется со временем, это может сигнализировать о дальнейшем повы шении затрат на сопровождение приложения (и /и л и пло хом его дизайне). Если вы практикуете непрерывную интеграцию совместно с выпуском CD и DVD, вы можете автоматически выпускать ме ченый диск, на котором записан весь комплект ПО, исполь зуемого при сборке текущей версии. Любой желающий мо жет взять последнюю версию системы (самый верхний диск в стопке) —и установить ее у себя.
1 б Наладьте регулярную обратную связь при помощи демонстрационных версий "Это не твоя вина; во всех этих проблемах виноваты заказ чики — эти гадкие клиенты и конечные пользователи. Они вечно хотят чего-то другого уже после окончательной даты выполнения проекта. Они должны с самого начала знать, что им нужно, и выдвигать требования, чтобы мы потом их реализовали. Вот как это все должно работать’’.
Требования растекаются, как чернила
Довольно часто приходится слышать, что многие люди хотят “заморозить” требования1. В реальности требования растекаются так же быстро, как чернила. Невозможно заморозить требования, как невозможно остановит ь развитие рын ка, конкуренцию, обучение, эволюцию, рост. Далее если вы и замо розите что-нибудь, то, скорее всего, совсем не то, что нужно. Если вы наивно ждете, что заказчики способны выдать вам твердые и четкие требования еще до начала реализации проекта, приготовь тесь к большому разочарованию. Мысли и взгляды люДей заморозить невозможно, и к вашим за казчикам это относится вдвойне. Даже после того, как они высказа ли вам свои пожелания, их фантазии и ожидания не стоят на месте, особенно когда они начинают осваивать по частям новую систему и осознавать ее роль и возможности. Это свойство человеческой натуры. Будучи людьми, мы склонны к достижению лучшего во всем, что бы мы ни делали, медленно и постепенно. Так что ваши заказчики, уже после того как выдали вам требования, постоянно находят все новые пути достижения лучшего эффекта от запрош енной ими функциональности. Если вы реализуете только исходные требова ния заказчика, вы определенно не сможете удовлетворить заказчи ка к моменту поставки, поскольку его требования изменились. Вы тогда подвергаете себя одному из наибольших рисков в разработке программного обеспечения: вы сделали то, что они просили, а не то, что они хотят иметь на самом деле. Результат? Вместо удовле творения —неожиданность, шок, разочарование. Много лет назад на занятии по численному анализу Венкату при шлось моделировать траекторию космического корабля при помо щи уравнений в частных производных. 1 Эдвард В. Берар замет ил: “Хождение но воде и разработка программ со гласно спецификации легко осуществимы, если заморозить и то, и другое".
Программа вычисляла пространственное положение корабля в момент времени 1+5 на базе его положения в момент времени I. В результате рассчитанная траектория на экране выглядела при мерно, как пунктирная линия на рис. 4.1. Заметим, что расчетная позиция корабля заметно отличается от реальной, поскольку гравитационные эффекты изменяют ско рость корабля не только в дискретных расчетных точках коррек ции траектории. В реальности гравитация действует постоянно, ее влияние на траекторию происходит непрерывно, а не дискрет но. Пренебрегая гравитационными эффектами в интервалах меж ду расчетными узлами, мы накапливали вычислительную погреш ность, и наш корабль прилетел совсем не туда. Сокращение временного шага (уменьшение величины 8) позво ляет снизить суммарную ошибку вычисления траектории: теперь результат расчета (сплошная линия на рис. 4.1) значительно лучше описывает реальность. Представим, что ожидания ваших заказчиков заданы неким фактическим положением, как позиция космического корабля. Успех разработки вашей системы зависит от того, насколько
Рис. 4.1.
Расчет траектории космического корабля
близко вы приближаетесь к этой точке в резуль тате итерационно го движения. Дискретные точки коррекции пути —это моменты демонстрации заказчиicy текущего состояния своей работы. В эти моменты вы получаете от них обратную связь и новую информа цию. Это позволяет вам скорректировать направление своего следующего шага —и так далее на протяжении всего процесса разработки. Легко видеть, что, чем длиннее временной зазор между момен том получения обратной связи и моментом показа работы заказчи ку, тем дальше от пункта назначения вы окажетесь в итоге. Необходимо последовательно, через регулярные промежутки времени, например в конце каждой итерации, встречаться с заказ чиками и показывать им новые свойства и функциональность, го товые на данный момент. Если вы часто консультируетесь с заказчиками, учитывая их мнение при разработке, выигрывают при этом абсолютно все. Ваши заказчики лучше информированы о вашем продвижении в работе. В результате они могут уточнить свои требования —снача ла для самих себя, а потом уже и для вас. Они могут направлять вас, руководствуясь своим растущим пониманием и более определен ными ожиданиями. А вы получаете возможность создавать про дукт, который отвечает их реальным нуждам. Исходя из вашего продвижения, а также доступного времени и бюджетных средств, заказчик может расставлять приоритеты для выполнения дальней ших задач. Есть ли смысл стремиться к слишком короткому интервалу получения обратной связи, сокращая шаг отдельной итерации? В программе расчета траектории космического корабля при уменьшении шага 5 процесс вычислений замедляется. Можно предположить, что более короткие итерации замедляют процесс разработки и могут негативно повлиять на сроки выполнения про екта. Давайте рассуждать следующим образом. Представим себе про ект сроком на два года, к концу которого вы и ваш заказчик обнару живаете, что все это время вы трактовали какое-то из ключевых требований заказчика совершенно не гак, как он это предполагал. Например, процедура обработки невыполненных заказов в вашей системе делает не то, что имел в виду заказчик. И вот теперь, за два года работы вы произвели систему, в которой миллион строк кода не имеют ничего общего с ожиданиями заказчика. Переделывание работы, которая стоила вам двухлетних усилий, обойдется, мягко говоря, недешево.
Л теперь представим, что вы pei-улярно демонстрировали им промежуточные результаты работы. Спустя два месяца от начала проекта они вдруг говорят: “Подождите минутку. Невыполненные заказы должны обрабатываться иначе’1. Эго вызывает некоторую панику, вы еще раз проверяет е технические требования к системе и оцениваете необходимый объем работ. Это относительно невы сокая цена предупреждения крупной неприятности. Обратная связь должна быть регулярной и возможно более час той. Если ваши итерации длятся квартал или год (это слишком дол го), сократите их до одной или двух недель. Постарайтесь полу чить упреждающую обратную связь от заказчика по тем функциям и свойствам, которые вы в данный момент реализуете.
Энди рассказывает...
Глоссарий проекта Непоследовательность терминологии может служить причиной непра вильного понимания требований заказчика. Практически для каждого бизнеса существует тенденция наделять общеупотребительные и невин но звучащие слова неким весьма специфическим и важным (тайным) смыслом. Я заметил, что довольно часто происходит следующее: программисты ис пользуют термины, отличные от тех, которыми пользуются пользователи и деловые люди. В результате такого несоответствия появляются ошибки в дизайне системы и в ее функционировании. Чтобы избежать подобных проблем, нужно иметь под рукой глоссарий те кущего проекта. Он должен быть общедоступен, например на интранетсайте или в Wiki. Это звучит тривиально, но он представляет собой всего лишь список терминов с их определениями. Но он помогает убедиться в том, что вы говорите с пользователями на одном и том же языке. В течение всей работы над проектом старайтесь выбирать подходящие имена для элементов программы — классов, методов, модулей, перемен ных и т.д., постоянно сверяйтесь с глоссарием и регулярно следите за тем, чтобы он продолжал соответствовать пользовательским ожиданиям.
v
Будьте на виду. В течение всего процесса разработки под держивайте ваше приложение на виду (и в умах) у заказчиков. Каждую неделю или две встречайтесь с заказчиками и получайте отних упреждающую обратную связь при помощи демо версий.
На что это похоже Спустя некоторое время после запуска проекта вы налаживаете удобную форму взаимодействия, которая позволяет вашей коман де и заказчикам наслаждаться здоровым и плодотворным общени ем. Сюрпризы должны встречаться все реже, а заказчики должны чувствовать, что они на достаточном уровне контролируют движе ние проекта.
Ведите учет По мере продвижения проекта вы получаете много информации по каналу обратной связи — поправки, предложения, требования внести изменения, расширение функциональности, исправление ошибок и т.д. Это большие объемы информации, которую надо отслеживать. Беспорядочные e-mail сообщения или пометки от руки на стикерах не решают проблемы. Лучше помещать все необходимое в систему регистрации и отслеживания (tracking system), можно с использованием web-интерфейса. Более подроб но см. Ship It! (Поставь продукт!) [RG05J.
Сохраняйте равновесие • Когда вы впервые предлагаете заказчикам данный метод ра боты, они могут сначала упереться, испугавшись перспекти вы слишком большого количества “релизов”. Важно, чтобы они поняли: это внутренние релизы (или демосы), которые полезны для них самих и которые не рассчитаны на дистри буцию среди всех пользователей. • Некоторые клиенты могут сказать, что им трудно выделить время каждый день или раз в неделю, или даже раз в две неде ли на общение с вами. В конце концов, они заняты на своей работе полный рабочий день. Уважайте их время. Если заказчику удобнее встречаться раз меся ц —пусть будет так. • Некоторые заказчики могут выделять своих сотрудников спе циально для того, чтобы те участвовали в ваших демонстра циях, причем в режиме полной занятости. Они не могут при думать ничего лучшего, чем ежечасная демонстрация с обратной связью. Это может дать грандиозные результаты, но вам может оказаться сложно поддерживать такой график и еще успевать писать код для их просмотра! Вам следует сни-
зить обороты и договориться встречаться, когда вы все при готовили и можете им показать что-то новое. Демонстрации служат для того, чтобы заказчики могли вы сказать свое мнение и повлиять на дальнейшую работу. Они не должны вызывать беспокойство или раздражение заказчи ка в связи с недостаточной функциональностью или неста бильной работой. Если работа системы нестабильна, лучше не показывайте ее. Предварительно четко и ясно объясните заказчикам, какие функции работают, а какие нет; они долж ны понимать, что перед ними приложение, находящееся в разработке, а не готовый, законченный продукт.
17 Используйте короткие итерации, постепенно добавляйте функциональность “У нас есть замечательный план, в котором все задачи и все продукты, подлежащие сдаче, расписаны на три года вперед. Когда мы выпустим готовый продукт, рынок будет нашГ
Как Unified Process, так и гибкие методологии предписывают использование итеративной и пошаговой (инкрементной) схемы разработки приложений1. Пошаговая разработка предполагает, что вы постепенно, небольшими группами добавляете функцио нальность в приложение. Каждый последующий раунд разработки строится на базе функциональности, созданной на предыдущих шагах, и добавляет новую; в результате ценность продукта неуклон но повышается. В конце каждого раунда можно выпускать новую версию или демо-версию продукта. Итеративная разработка означает' выполнение различных задач, составляющих процесс разработки, —анализ, проектирование, реа лизацию, тестирование и отслеживание обратной связи —в виде ко ротких повторяющихся циклов, называемыми итерациями. Конечная точка итерации задает контрольный этап. К этому мо менту продукт не обязан быть доступен для использования. Инкре мент продукта (приращение его функциональности) завершается тогда, когда в конце итерации вы готовы к выпуску новой версии вместе с необходимыми ресурсами для поддержки, обучения и со провождения системы. Каждый инкремент в общем случае включа ет в себя множество итераций. Представьте мне детальный долгосрочный план — и я покажу вам Обреченный проект
Как заметил Каперс Джонс, “...Разработка больших систем является рискованным предприятием". Крупномасштабные проекты, как правило, проваливаются. В общем случае они не придерживаются итеративно го и пошагового сценария разработки либо длительность итераций слишком велика. (Итеративный и эволю ционный подходы к разработке, а также их очевидная взаимосвязь со степенью риска, производительностью и дефектами хорошо обсуждаются в книге “Гибкая и итеративная разработка: Руковод ство менеджера” [Lar04]. Ларман утверждает, что разработка про 1 Все диет ы предписывают вам поменьше есть и побольше двигаться. Но конкретные советы, как этого можно добиться, сильно различаются.
граммного обеспечения не является прогнозируемым производст вом, а по своей природе ближе к изобретательству. Проект, который расписан па несколько лет вперед еще до того, как заказ чик начал использовать приложение, почти наверняка обречен на неудачу. Идея выполнения крупномасштабного проекта как последова тельности коротких шагов лежит в основе гибкого подхода. Слиш ком большие шаги повышают риск, более мелкие помогают сохра нять равновесие. Вы можете обнаружить немало примеров итеративной и поша говой разработки. Возьмем, например, XML-спецификации WWWконсорциума: Описания типа документов (DTD, Document Туре Definitions), которые определяют структуру и лексику XML-документов, были реализованы как часть исходной спецификации. Хотя они неплохо справлялись со своими задачами на момент их проектирования, дальнейшее их использование выявило целый ряд ограничений и недостатков. Обратная связь и дальнейшее уг лубление понимания позволили выработать более эффективные решения второго поколения для описания структур документов, такие как Schema. Если бы разработка началась позже, пусть даже с более высокого уровня, XML не стал бы доминирующей техноло гией к настоящему моменту; именно ранний релиз обеспечил необ ходимый опыт и понимание. Большинство пользователей согласны иметь хороший про граммный продукт сегодня, а не ждать превосходного продукта еще целый год (см. раздел “Приемлемые программы” в книге “Программист-прагматик: Путь от подмастерья к мастеру” [НТОО]; издво “Л ори”, 2007). Определите важнейшие функции приложения, которые сделают его возможным для использования, и внедрите их в производство, т.е. отдайте в руки настоящих пользователей как можно скорее. В зависимости от типа приложения выпуск версии-инкремента может занимать недели или месяцы, но если он планируется через год или два, необходима переоценка и перепланировка. Вы можете возразить, что построение сложной системы отнимает много вре мени и что вы не можете разработать его инкрементами. Если это так, то не создавайте такого приложения! Выстраивайте его из от дельных небольших блоков, которые можно использовать —это тоже инкрементная разработка. Даже NASA при разработке слож нейшей системы управления для космического шаттла практи ковало итеративную и пошаговую разработку ПО (см. “Проекти рование, разработка, интеграция: Программная система первого полета космического шаттла”) [MR84]).
Спросите у пользователей, какие свойства они считают важны ми для начала использования продукта. Me от влекайтесь на занят ные, но менее важные детали и на создание эффектного интерфей са пользователя, который рисуется в вашем воображении. Есть ряд причин, но крторым нужно стремиться отдать прило жение в руки пользователей как можно скорее: когда оно находит ся там, оно приносит доход, давая возможность дальнейшего фи нансирования разработки. Обратная связь с пользователями помогает понять, что им нравится и что нужно написать дальше. Вы можете обнаружить, что некоторые свойства, которые каза лись вам важными, не являются таковыми —все мы знаем пере менчивость рынка. Запустите ваше приложение как можно рань ше, поскольку его значимость со временем может заметно снизиться. Короткие итерации и малые инкременты помогают разработ чикам сосредоточиться на выполнении проекта. Если вам говорят, что у вас целый год на завершение проекта, вам кажется, что впере ди еще вечность. Трудно заставить себя напряженно работать, ко гда впереди еще много времени. Мы слишком высоко ценим мгно венные удовольствия — нам нужен немедленный результат, мы предпочитаем иметь все скорее рано, нежели поздно. Это вовсе не плохо; данное свойство даже полезно, если оно повышает произво дительность и стремление получать позитивные отклики пользо вателей. На рис. 4.2 показано соотношение между основными циклами разработки в гибком проекте. Наилучшая продолжительност ь каж дого инкремента —от нескольких недель до нескольких месяцев в зависимости от масштаба проекта. Внутри каждого инкремента располагаются короткие итерации (не длиннее двух недель). Каж дая итерация заканчивается демонстрацией и установкой рабо тающей версии продукта избранным представителям заказчика, которые затем обеспечивают обратную связь.
ttu y
Разрабатывайте последовательными инкрементами. Выпускайте каждую новую версию продукта с минимальным , но полезным набором новой функциональности. Внутри каждого инкремента используйте цикл итераций длиной отодной недели до четырех.
На что это похоже Короткая итерация хорошо нацелена и высокопродуктивна. У вас перед глазами конкретная четко очерченная цель, и вы достигаете
ее. Ж есткий срок завершения итерации заставляет вас принимать трудные решения, и ни один вопрос не остается открытым слиш ком долго.
Сохраняйте равновесие • Определение наиболее подходящей длины итераций играет большую роль в поддержании равновесия. У Энди был кли ент, который был твердо уверен в том, что итерации должны быть длиной ровно четыре недели, потому что где-то прочи тал об этом. Но команда изнывала от такого темпа: им трудно было совмещать разработку с текущими обязанностями по сопровождению. В результате было решено после каждой че ты рехнедельной итерации одну неделю посвящать сопрово ждению, а затем начинать новую итерацию. Нет такого зако на, что итерации должны плотно следовать друг за другом. • Если в каждой из итераций ощущается нехватка времени, зна чит, задачи слишком велики, или итерации слишком коротки (в среднем); не меняйте их длину из-за одной неудачной ите рации. Почувствуйте ритм.
Если есть заметное расхождение между потребностями заказ чика и свойствами выпущенной версии, то, возможно, итера ция была слишком долгой. Поскольку требования пользова телей, технологии и наше понимание требований меняется со временем, все эти изменения должны быть отражены в ре лизах. Если вы все еще работаете по старым понятиям и при меняете избитые методы, возможно, вы слишком долго ие корректировали их. Выпуск версии-инкремента должен содержать полезные об новления функциональности и предлагать более высокую ценность для заказчиков. Как узнать, что для них наиболее ценно? Спросите их самих.
18 Фиксированные цены мешают выполнению обещаний “Мы должны назначить твердую цену за этот проект. Мы пока не знаем всех подробностей выполнения, но должны заложить стоимость всех работ. Нужно к понедельнику под готовить все оценки, а к концу года сдать продукт
Контракты с фиксированной стоимостью представляют про блему для гибкой команды разработчиков. Мы все это время гово рили о том, как наладить непрерывную работу, постепенно выпол нять задачу в режиме итераций и инкрементов, и вдруг кто-то приходит и спрашивает, сколько времени это займет и сколько это ему будет стоить. Заказчикам такой подход кажется в высшей степени разумным. Они сами так работают: так возводятся здания, строятся дороги и т. д. Почему разработка программного обеспечения должна силь но отличаться от традиционных отраслей производства, от строи тельства? Возможно, в действительности наша работа имеет много обще го с процессом строительства зданий —только с реальным процес сом их возведения, а не с нашими представлениями о нем. По дать ным исследования, проведенного в 1998 году в Великобритании, примерно 30% затрат на возведение строительных объектов ухо дит на переделку, т.е. исправление ошибок', которые не являлись следствием изменения требований или поправок к законам физи ки, это просто обычные ошибки. Обрезали балку слишком корот ко, оконный проем сделали слишком большим. Элементарные, привычные ошибки. Проект по разработке программного, обеспечения может со держать не только элементарные ошибки, но и кардинальные поправки к начальным требованиям (пег, мне нужен не сарай, а небоскреб!). Помимо этого, производительность всей команды и каждого отдельного разработчика может варьироваться в слиш ком широких пределах (в 20 и более раз, в зависимости от уров ня подготовки), и, конечно же, необходимо учитывать непрекращающийся поток новых технологий (и тут все гвозди вечно кривые). 1 Rethinking Construction: The Report of the Construction Task Force. Department for Transport Local Government and the Regions, 01 Jan 1998. Office of the Deputy Prime Minister, London. England.
Учитывая постоянную изменчивость и уникальность, которые внутренне присущи софтверным проектам, введение для них фиксированных затрат почти всегда приво дит к невыполнению запланированного объема работ. Какие аль тернативы здесь можно предложить? Можно ли более точно все предвидеть заранее или добиваться заключения сделки по другой схеме? В зависимости от вашей ситуации можно пытаться делать и то, и другое. Если вы непременно должны оценить стоимость работ предварительно (например, в контракте с государственным учреж дением), тогда вам придется прибегнут ь к специальным методикам анализа затрат —СОСОМО или Function Point. Но они являются жесткими (не гибкими) и не бесплатными. Если новый проект по добен многим другим, которые вы уже выполняли с этой коман дой, вы определенно находитесь в более выгодном положении: разработка несложного web-сайта для одного заказчика имеет мно го сходства с другими подобными проектами. Но в большинстве случаев ситуация отличается от приведенно го примера. Как правило, эго деловые приложения, которые не имеют ничего общего с другими. Поисковые и исследовательские проекты в большей степени, чем другие, требуют взаимодействия с заказчиком. Если есть возможность, нужно договориться органи зовать весь процесс немного иначе. Предложите им следующие этапы: Ф иксированная цена гарантирует нарушение Обязательств
1. Сначала вы строите совсем небольшую часть всей системы (например, гараж —по аналогии со строительством). Выбе рите достаточно малый набор функций, чтобы эта рабо та за няла у вас не более шести или восьми недель. Объясните заказчику, что система будет иметь минимальные возмож ности, которые позволят начать ее продуктивное использо вание. 2. В конце первой итерации у клиентов есть-два пути на вы бор: они могут согласиться на продолжение в виде следую щей итерации, которая добавит новый набор функций при ложению, или же отказаться от дальнейшей работы с вами, заплатив за несколько недель сделанной работы, после чего они могут либо выбросить ваш продукт на помойку, либо нанять кого-то еще, чтобы довести его до нормального со стояния. 3. Если они соглашаются продолжать с вами, вы уже можете прогнозировать что-то более конкретное на следующую ите
рацию. В конце ее ваш клиент опять будет иметь ту же возможность выбора: остановиться немедленно или идти дальше. Преимущество для заказчика в таком сценарии заключено в том, что для него проект “выходит из тени". Заказчик получает воз можность следить за продвижением работ (или его отсутствием) практически сразу. Он контролирует ситуацию и в любой момент может все остановить без уплаты штрафов, предусмотренных до говором. Он следит затем, какие функции и в каком порядке добав ляются в систему и во сколько они ему обходятся. В целом, заказ чик рискует намного меньше. А вы опять занимаетесь итеративной и пошаговой разработкой. Проводите все оценки только исходя из реальной работы.
Щ бш чЩ р
Позвольте команде работать над текущим проектом и с текущим заказчиком — и тогда вы сможете реалистичнее проводить оценку. Позвольте заказчику контролировать функциональность продукта и его собственный бюджет.
На что это похоже Ваши оценки будут меняться в течение всего проекта —они не фик сированы. Но с каждой новой итерацией вы будете чувствовать себя все более уверенно при проведении предварительных оценок для следующей итерации. Точность ваших прогнозов со временем повышается.
Сохраняйте равновесие • Если ответ вас не устраивает, попробуйте изменить поста новку вопроса. • Если вы занимаетесь плановой разработкой в условиях, кото рые не приемлют гибкости, тогда вам лучше либо использо вать плановые негибкие методы разработки, либо поменять условия работы, • Если вы вообще откажетесь дать предварительную оценку стоимости до окончания первой итерации, заказчик может найти другого исполнителя, который предоставит ему эту оценку, пусть даже далекую от реальности. • Гибкий подход не означает4: “Давайте начнем писать код и то гда увидим, сколько это займет времени”. Вам все равно нуж-
но провести приблизительную оценку стоимости и обосно вать полученный вами результат, а также указать предельную погрешность вашей оценки на базе принятых допущений и текущего состояния. Если вы находитесь в ситуации, когда никакой из этих вари антов не проходит и вы просто должны работать за фиксиро ванную плату, вам придется проявить недюжинные способ ности к прогнозированию затрат. Можно еще рассмотреть опцию фиксированной стоимости для итераций, предусмотренных договором; при этом не фиксируется общее число итераций, которые могут быть оформлены в качестве текущих рабочих заданий.
Одно испытание стоит мнений тысячи экспертов .
Билл Най, большой ученый
Глава 5
Гибкая обратная связь В течение всего гибкого проекта нам нужна обратная связь, чтобы непрерывно вносить множество мелких поправок. Но откуда по ступает к нам вся эта обратная связь? В предыдущей главе мы говорили о тесном сотрудничестве с пользователями: как наладить канал обратной связи с ними и стро ить на ее базе свою дальнейшую работу. В этой главе мы поговорим о получении обратной связи из других источников. Как заметил Билл Най (Bill Nye), испытания во всех случаях имеют решающее значение. Применим эту идею на практике и сделаем так, чтобы вы всегда могли точно знать, в каком состоянии находится ваш про ект, а не пытались гадать. Многие проекты начинают буксовать, когда код выходит изпод контроля. Исправления ошибок порождают новые ошибки, которые, в свою очередь, требуют еще больше исправлений —и в конце концов вся эта куча с треском обрушивается. Нам нужно ор ганизовать постоянный контроль —со стороны некоторого пер манентного источника обратной связи, который будет следить за тем, чтобы код со временем не “портился” и работал если не луч ше, то, по крайней мере, не хуже чем вчера. Мы покажем, как П о садить ангелов себе на плечи и следить за состоянием своего кода; см. с. 87. Но все это, разумеется, не удержит вас от написания интерфей са или API, которые окажутся чересчур громоздкими или сложны ми в использовании. Чтобы справиться с этой задачей, необходи мо Использовать код до сборки; см. с. 92. Понятно, что, если ваш код успешно проходит модульное тести рование на вашем компьютере, это еще не значит, что он будет так же хорошо на другой машине. Различия Имеют Значение, см. с. 97.
Теперь, когда у вас вполне “пристойный” API и “чисгый” код, са мое время заняться тем, чтобы его выполнение обеспечивало пользователям те результаты, которые они ждут от его использова ния. Чтобы гарантировать это, лучше всего А втом атизировать приемочные испытания. Более подробно об этом см. па с. 110. Текущее состояние работ над проектом интересует всех, но здесь существует опасность впасть в заблуждение, поддавшись влиянию обманчивых внешних индикаторов хода процесса или же под давлением признанных авторитетов, коими считаются графи ки Гантта или PERT, а также симпатичные приложения-календари. Мы предлагаем вам И зм ерять ф актическое продвиж ение; см. с. 103. Мы уже обсуждали, как строить свою работу с пользователями, чтобы получать от них регулярную обратную связь в процессе раз работки. Но, когда с момента релиза проходит уже довольно много времени, вам необходимо опять П рислуш аться к пользователям ; см. с. 106.
19 Посадите ангелов себе на плечи "У тебя нет оснований тратить время и силы на написание модульных тестов. Это только задержит выполнение проек та. Все равно ты чертовски хорошо пишешь программы. Модульные тесты — это всего лишь бесполезная трата вре мени, а мы и без того сейчас в кризисной ситуации".
Код быстро меняется. Всякий раз, как только ваши пальцы каса ются клавиатуры, код уже изменился. Смысл гибких методик — приспосабливаться к изменениям и управлять ими. Код представ ляет собой наиболее изменчивую субстанцию. Чтобы справиться с этой задачей, нужно всегда быть в курсе “со стояния здоровья” вашего кода: делает ли он именно то, что дол жен делать? Не сломалось ли что-нибудь в результате последних из менений в нем? Хорошо бы иметь ангела-хранителя, который будет стоять за вашей спиной и следить за тем, чтобы все у вас было хорошо. Таким ангелом может стать автоматическое тестирова ние модулей. Обратная связь
Сейчас многие разработчики пренебрегают модульным тестированием кода, просто по тому, что слово “тестирование” для них зна чит то, что это работа выполняется другими людьми. Мы сейчас не будем зацикливаться на терминологии, а за метим, что это средство может служить великолепной техникой поддержки обратной связи для процесса кодирования. Представим, как в былые времена большинство разработчи ков работало с кодом: вы пишете небольшой код и затем в него вставляете операторы печати для проверки значений ключевых переменных. Запускаете код либо под отладчиком, либо из про граммной заглушки. Визуально оцениваете результаты, что-то ис правляете, а затем выбрасываете заглушку или выходите из режи ма отладки и переходите к следующему шагу. Модульное тестирование “в гибком стиле” поднимает же при вычный процесс на более высокий уровень. Вы не выбрасываете код-заглушку, а сохраняете его и продолжаете запускать автомати чески. Вместо визуального просмотра промежуточных результа тов пишете код, проверяющий состояние интересующих переменных. Поскольку сам код для проверки значения конкрегной перемен ной (а также контроль общего числа запускаемых тестов и т.п.) ДЛЯ кодирования
тривиален, вы можете пользоваться стандартными средствами, г.е. средами тестирования (frameworks), которые облегчат вашу работу по написанию и организации тестов. Среди них можно на звать JU nil для Java, NUnii для С # /.NET, HttpUnit для тестирова ния web-серверов и т. д. Еще есть среда xUnit framework, которую можно использовать практически для любого языка и во всех мыс лимых конфигурациях. Большинство из них перечислено и доступ но на сайте http://xprogramming.com/software.htm. Будьте уверены в том, что вы тестируете Наш читатель Дэвид Бок рассказал нам следующую историю. “Недавно я работал над модулем одной крупной системы управления проек тами, конвертируя процесс ее сборки из Ant в Maven. Это был крепкий, хоро шо оттестированный код, который уже был запущен в эксплуатацию. Я заси делся до позднего вечера, все шло хорошо. Я внес небольшие изменения в процесс сборки — и тут внезапно модульный тест провалился. Я потратил некоторое время на то, чтобы разобраться, каким образом внесенные изме нения могли привести к такому результату, но, в конце концов, сдался и дал обратный ход. Но тест по-прежнему проваливался. Я начал копаться в са мом тесте и обнаружил, что проблема возникала при тестировании утилиты, которая вычисляет время. В частности, она возвращала экземпляр Date, ко торый указывал на полдень следующего дня. Оказалось, что тест использу ет системное время, соответствующее моменту прохождения теста, в каче стве параметра для сравнения с результатом, полученным при тестирова нии кода. А в коде метода сидела глупая ошибка (+1/-1), которая приводила к тому, что при вызове метода в промежутке 23:00—24:00 он возвращал полдень текущего, а не следующего дня". Важные уроки из этой истории: • Убедитесь в том, что ваши тесты повторяются. Использование систем ного времени или даты в качестве параметра делает тестирование чувствительным ко времени его запуска, использование IP-адреса компьютера делает результат зависимым от того, на какой машине проводится тест и т. д. • Проверьте тест на граничные условия. 23:59:59 и 00:00:00 — самые подходящие моменты времени. • Никогда не игнорируйте неудачный тест. В предыдущем случае один из тестов всегда был неудачным, но, поскольку неудачными оказыва лись примерно две дюжины тестов ежедневно, никто не замечал псев дослучайной ошибки.
Когда у вас появляется несколько модульных тестов, автомати зируйте их, т.е. запускайте их всякий раз при компиляции и сбор ке своего кода. Относитесь к результатам модульного тестирова ния так же, как к результатам компиляции: если код не проходит тестов (или не имеет их), это гак же плохо, как если бы он не ком пилировался вовсе.
Далее организуйте работу машины сборки (build machine) в фоно вом режиме, которая постоянно берег последнюю версию исход ного кода, компилирует его, прогоняет через модульные тесты и тотчас информирует вас, если что-то не так. Наличие локальных модульных тестов, запускаемых при каж дой компиляции, в сочетании с постоянной работой сборочной машины, ко торая компилирует и гоняет модульные тесты для всей сис темы, равносильно присутствию ангела за вашей спиной. Если что сломалось, вы узнаете об этом сразу, когда можно все испра вить проще и дешевле. Если ваши модульные тесты построены на базе возвратного тес тирования (regression tests), вы можете при желании провести за одно рефакторинг кода. Вы можете переписать и перепроектиро вать код, не боясь экспериментировать: модульные тесты не допустят, чтобы вы случайно что-нибудь сломали. Это дает вам зна чительную свободу действий: вам не приходится обращаться с ко дом так, как будто вы “ходите по яичной скорлупе”. Модульное тестирование принадлежит к числу наиболее попу лярных гибких практик, поэтому множество книг и друг их ресур сов Moiyr послужить вам хорошим подспорьем. Если вы незнакомы с этой идеей, начните с “Прагматического модульного тестирова ния” (есть версии для Java [ПТОЗ] и О | НТ04]). Для более глубоко го практического изучения см. “Рецепты JU nit” [Rai04]. Чтобы подключать модульные тесты автоматически (и научить ся делать многое другое), см. “Pragmatic Project Automation: How to Build, Deploy and Monitor Java Applications” (Прагматическая авто матизация проекта: как строить, развертывать и контролировать Java-пpилoжeния)[CIa04). Несмотря на то, что в ней рассматрива ются, главным образом, Java-nроекты, аналогичные средства суще ствуют и для .NET, и для других конфигураций. Если вам еще нужны аргументы, чтобы начать использовать мо дульное тестирование, то вот некоторые из них: Модульное тестирование обеспечивает немедленную обрат ную связь. Ваш код проверяется многократно. В процессе правки и переписывания кода тесты проверяют, не нарушен ли какой-ли бо из существующих контрактов. Вы можете быстро идентифици ровать любую проблему и устранить ее. М одульное тестирование делает ваш код крепче и здоровее. Тестирование помогает вам продумывать детальнее поведение кода, проверяя его на позитивные, негативные реакции и исклю чительные ситуации. Модульное тестирование может быть эффективным средст вом проектирования. Как будет показано в Практике 20, Исполь
зуй'гс код до сборки (с. 92), модульное тестирование помогает к разработке практичного и простого дизайна. Модульное тестирование усиливает чувство уверенности. Вы протестировали свой код и проверили его поведение в широ ком диапазоне условий; э го придает вам уверенности при появле нии новых ответственных задач с жестко заданными сроками вы полнения. Модульные тесты действую т аналогично осциллографическим датчикам, применяемым для проверки печатны х плат. Вы можете быстро нащупать пульс в различных участках кода, когда возникает проблема. Эго позволяет локализовать и решать про блемы (см. Практику 35, Атакуйте проблемы в изоляции, с. 152). Модульные тесты представляю т собой надежную документа цию. Когда вы приступаете к изучению нового API (программного интерфейса приложений), любые модульные тесты, написанные для него, служат точной и надежной документацией. Модульные тесты помогают в обучении. Когда вы начинаете использовать новый API, для облегчения его освоения вы можете начать писать тесты для него. Эти учебные тесты помогают не только понять поведение API, но и выявить несовместимые изме нения, которые вы могли бы внести позже. Используйте автоматизированные модульные тесты. Хо рошие модульные тесты сразу предупреждают вас о пробле мах. Не вносите никаких изменений в дизайн или в код при от сутствии надежных модульных тестов.
На что это похоже Вы полагаетесь на модульные тесты. Код без тестов вызывает дискомфорт, как будто вы идете по натянутой проволоке без стра ховки.
Сохраняйте равновесие • Модульное тестирование требует затрат. Вкладывайтесь в него с умом. Testing accessors или тривиальные методы могут оказаться лишь пустой тратой времени. • Чтобы избежать модульного тестирования, многие разработ чики находят массу оправданий, которые, на самом деле,
явно указывают на недостатки дизайна. Как правило, чем громче прот ест, тем хуже дизайн. Эффективность модульного тестирования зависит от сферы действия (охвата). Вам, возможно, придется взглянуть на средства охвата кода тестами, чтобы грубо оценить реальное положение вещей. Большее количество тестов не означает луч шее качество: тес ты должны быть эффективными. Если тесты никогда не ло вят ошибок, возможно, они проверяют совсем не то, что, дей ствительно, нужно проверять.
20 Используйте код до сборки ’X
“Иди вперед и заканчивай работу над своим библиотечным * кодом. Потом у тебя будет достаточно много времени, что д д Р ^ узнать, что о нем думают другие. Просто перебрось его им > через стену прямо сейчас. Уверен, все будет хорошо".
Многие успешные компании живут под девизом “Ешь то, что сам производишь” (“Eat your own dog food”). Другими словами, что бы сделать свой продукт как можно лучше, нужно активно его при менять для себя. К счастью, наш бизнес не связан с производством корма для со бак. Но в нашем бизнесе создаю тся и вызываются API, используют ся интерфейсы. Таким образом, перед тем как всучить миру создан ный вами интерфейс, необходимо некоторое время пользоваться им самому. На самом деле, нужно использовать разрабатываемый вами интерфейс еще до того, как вы написали код реализации для него. Как это можно осуществить? Пишите тесты ДО написания самого кода
При использовании техники под названием Разработка на базе тестирования (Test Driven Development) сам код пишется лишь после того, как написан недостающий или непроходиой (failing) тест для нового кода. Тест всегда создается первым. Как правило, изначально такой тест ие проходит по двум причинам: либо тестируемый код не существует, либо он пока не содержит логики, необходимой для прохождения теста. Когда вы сначала пишете тесты, вы смотрите на свой будущий код глазами его пользователей, а не разработчика. И это самое главное. Вы увидите, что вы способны спроектировать более удоб ные и совместимые друг с другом интерфейсы, поскольку вам са мим приходится их использовать. Кроме того, написание тестов до крда помогает избавиться от слишком запутанного дизайна и сосредоточиться на выполнении самой задачи. Рассмотрим следующий пример —создание програм мы игры в крестики-нолики для двух пользователей. Когда вы только задумываетесь о дизайне кода для этой игры, вы можете иметь в виду следующие классы: TicTacToeB oard, C e ll, Row, Column, P la y e r, User, Peg, Score, и Rules. Давайте начнем с клас са TicTacToeBoard, который представляет само игровое поле для игры (в терминах игровой логики, а не интерф ейса пользова теля).
Вот первый возможный тест для класса TicTacToeBoard, напи санный на С И с использованием среды тестирования NUnit. Он создаст игровое поле (доску) и контролирует состояние (конец) игры, [T estF ixtu re] public class TicTacToeTest
{ p riv a te TicTacToeBoard board;
[Setup] public void CreateBoard()
{ board = new TicTacToeBoard();
} [Test] public void TestCreateBoardO
{ Assert.lsNotNull(board); Assert.1 sFalse(board. GameOver);
} > Этот тест не проходит в силу того, что класса TicTacToeBoard не существует —компилятор сообщит об ошибке. Если тест будет пройден успешно, вас это удивит, не так ли? Это может случиться не так уж ч^сто, но такое бывает. Всегда убедитесь, что ваши тесты вначале падают —до того, как проходятся успешно: это необходи мо для того, чтобы выявить потенциальные ошибки в самом тесте. Напишем реализацию для этого класса: public class TicTacToeBoard { public bool GameOver { get { return false;
} } } Свойство GameOver сейчас возвращает значение false. В общем случае вам захочется написать как можно меньше кода, чтобы за ставить его проходить тест. В этом есть некий самообман, вы ведь знаете, что код неполный. Но это в данном случае не играет роли, поскольку дальнейшие тесты вынуждают вас возвращаться назад и добавлять функциональность.
Каков следующий шаг? Сначала вы должны реши ть, кто начина ет игру, т. е. задать первого игрока. Напишем тест, устанавливаю щий первого игрока: [Test] public void TestSetFirstPlayer() { / / что здесь должно быть?
)
В этом месте тест требует принятия решения. Перед тем как он может быть пройден, вам необходимо решить, каким образом представлять игроков в программе и как приписывать их к игрово му нолю. Вот одна из возможностей: board.SetFirstPlayer(new Р1ауег("Afark"), " Г ) :
Это сообщает игровому полю, что игрок Mark будет ставить кре стики. Все эго, без сомнения, будет работать. Но требуется ли вам класс Player или имя первого игрока? Вполне возможно, позже вам придется отслеживать победителей. Но пока в этом нет особой ну жды. Принцип YAGNI1(You Aren't Gonna Need It —Вам не нужно) предписывает вам не браться за реализацию какого-либо свойства до тех пор, пока в нем нет необходимости. На данном этапе нет ост рой нужды в классе Player. Вспомним, что мы еще не написали метод SetFi r s t Р1ауег() в классе TicTacToeBoard и не написали класс Player. Мы все еще пы таемся написать тест. Предположим, что следующий код задает первого игрока: board.SetFi rstPlayer( “X” );
Это сообщает коду, что первый игрок ставит крестики. Этот ва риант проще, чем первый. Но он скрывает в себе некоторую опас ность: передача произвольного символа методу S etF irstP layer() означает, *гго потребуется код для проверки, принимает ли дан ный параметр значение О или X, и вам придется также предусмот реть ситуацию, когда этот параметр принимает другое значение. Итак, попробуем упростить еще больше. Мы будем использовать флажок для индикации первого игрока. Исходя из этого, напишем наш модульный тест следующим образом: [Test] public void T estS etFirstP layer() {
1 Придумано Роком Джеффризом (Ron Jeffries).
board.FirstPlayerPeglsX = true; Assert. IsTrue(board.TirstPlayerPeglsX);
} Мы можем объявить FirstP layerP eglsX логическим свойством и присвоить ему требуемое значение. Это выглядит простым и удоб ным в использовании —намного проще, чем в случае создания клас са P layer. Как только тест готов, вы можете запустить его после на писания реализации для свойства F irstP layerP eglsX в классе TicTacToeBoard.
Вы видите, что мы начали с предположения, что у нас есть класс а пришли к тому, что можно просто ввести логическую пере менную? Такое упрощение произошло в процессе создания тестов для кода, еще до написания собственно тестируемого кода. А теперь вспомним, что мы вовсе не стремимся отказаться от практик хорошего дизайна и кодировать все в виде огромного на бора булевых переменных! Наша цель —ценой наименьших уси лий успешно реализовать конкретную функцию. В целом, сейчас программисты более склоны выбирать противоположное направ ление, чрезмерно все усложняя, поэтому очень полезно иногда двигаться по пути упрощения. Очень легко упростить код, избавляя его от классов, которые пока не написаны. В противоположность этому, как только вы на писали код, вы чувствуете необходимость сохранять этот код и продолжать работу с ним (даже по истечении срока его годности). Player,
Хороший дизайн не означает больше классов
Когда вы проектируете и разрабатываете объектно-ориентированные системы, вы чувствуете необходимость использовать объекты. Многие склонны считать, что ООсистемы просто обязаны состоять только из объектов, неважно, нужны они или нет. Добавление кода без особой надобности —все гда плохая идея. Дизайн на базе тестирования заставляет вас поразмышлять о том, как вы будете использовать код еще до того, как вы получаете возможность его писать (или, по крайней мере, пока вы зашли слишком далеко в реализации). Это заставляет вас думать об удоб стве его использования и открывает вам более практичный ди зайн. И, конечно, дизайн не может быть окончательным в самом нача ле. Вы непрерывно будете добавлять все новые тесты, писать но вые фрагменты кода и перепроектировать классы в течение их жизненного цикла (см. Практика 28, Пишите код инкрементами; см. с. 126).
Используйте код до его сборки. Используйте разработку на базе тестирования как инструмент проектирования. Он позво лит вам найти более практичный и простой дизайн кода.
На что это похоже Вы в любой момент ощущаете реальный повод к написанию кода. Вы можете сосредоточиться на проектировании интерфейса, не отвлекаясь на детали его реализации.
Сохраняйте равновесие • Не вешайте на начальный тест те функции, которые должен выполнять тест перед правкой кода. Начальный тест улучша ет дизайн кода, но всегда нужно писать специальные тесты перед каждой правкой кода. • Любой дизайн можно улучшить. • Модульные тесты —не всегда подходящее средство для напи сания экспериментальных приложений или прототипов. В том нежелательном случае, когда такой код перерастает впоследствии в реальную систему, вам придется добавлять тесты потом (хотя почти во всех случаях лучше переписать код заново). • Сами по себе модульные тесты не гарантируют лучшего ди зайна, они лишь облегчают его создание.
21 Различия имеют значение “Пока код работает на твоем компьютере, все в порядке. Какая разница, будет ли он работать на другой платформе. У тебя же ее H e f \
Когда вендор иди сослуживец произносит эти сакраментальные слова: “О, да. это не имеет значения”, вы не можете им возразить. Но, если существуют различия, всегда есть вероятность, что они все же будут иметь значение. Венкат понял эго на собственном горьком опыте. Его коллега пожаловался, что код Венката не проходит тесты. Странно, усло вия тестирования были идентичны тем, что были на компьютере Венката. Код работал на одной машине и не работал на других! В конце концов, они поняли, что причина была в различном по ведении .NET API на разных платформах: в данном случае Windows ХР и Windows 20031. Платформы различались — и это возымело значение. Им повезло, они случайно обнаружили проблему, иначе она всплыла бы после поставки продукта. Позднее обнаружение тако го рода проблем может' дорого обойтись: только представьте себе выпущенное приложение, которое отказывается работать на од ной из платформ, поддерживаемых вами официально. Вы могли бы попросить свой ОТК тестировать ваше приложе ние на всех поддерживаемых вами платформах. Но это не самый удачный подход, если они проводят тестирование вручную. Нужен подход, более ориентированный на разработчика! Вы уже и гак пишете модульные тесты для проверки вашего кода. Всякий раз, когда вы модифицируете код или проводите ре факторинг, вы проверяете его на тестах до внесения изменений. Все, что требуется теперь, это тестировать код на каждой из под держиваемых платформ или конфигураций. Если ваше приложение должно запускаться на различных опе рационных системах (MacOS, Linux, Windows и т.д.) или даже на различных версиях одной и той же операционной системы (Windows 2000, Windows ХР, Windows 2003 и т.д.), необходимо тестировать на каждой из них. Если предполагается, что прило жение должно работать на различных виртуальных машинах (VM) Java или в общеязыковой среде выполнения .NET (CLR, Common Language Runtime), вы должны проверить свой код и на них. 1 См. глюк #74 в списке глюков
.NET
[Sub05]
Энди рассказывает...
Но на моем компьютере все работает... Однажды у нас был клиент, который хотел повысить производительность своей OS/2, и один отчаянный разработчик взялся переписать планиров щик операционной системы OS/2 на ассемблере. И это заработало. Отчасти. Все работало хорошо на компьютере самого вендора, но ни на одной другой машине не работало вообще. Купили ана логичное оборудование от того же самого клиента, загрузили те же вер сии операционной системы, базы данных и другие средства. Все безус пешно. Они поставили компьютеры одинаково, в один и тот же момент времени, устроили пляски с бубном (тут я слегка приукрашиваю, но все остальное — чистая правда). В конце концов, команда отказалась от дальнейших попыток. Связываться с недокументированными внутренними переменными операционной систе мы — это скорее хрупкая техника, нежели гибкая.
Автоматизируйте, чтобы сэкономить время
Вы и так уже испытываете острую нехватку времени, где же взять время еще и на тестирование кода на многочисленных платфор мах? Спасение заключается в непрерывной и последовательной интеграции!1 Как мы видели в разделе “Держите код наготове”, механизм не прерывной сборки периодически берет свежую версию вашего кода и проверяет ее. Если какие-либо тесты не проходят, она уве домляет тех разработчиков, которых это может касаться. Уведом ления могут рассылаться по электронной почте, на пейджер, за писываться па RSS-ленту или доставляться по другим, менее стандартным каналам связи. Чтобы тестировать код на различных платформах, вы просто устанавливаете систему последовательной сборки на каждую из них. Когда вы или ваш коллега вносит изменения в код, тесты за пускаются автоматически на всех рабочих платформах. Только представьте, что вы уже через несколько минут после правки узнае те о проблеме, последовавшей за ней, на любой платформе! Это ис ключительно разумная трата ресурсов. Стоимость оборудования для машин сборки эквивалентна не скольким часам работы программиста. Если надо, вы можете даже еще более сократить затраты, используя программные продукты 1 Прочтите содержательную статью Мартина Фаулера “Continuous Integ ration’' (Непрерывная интеграция) на htLp://www martinfowler com/articles/ continuouslntegration.him!.
типа VMwarc или Virtual PC, позволяющие запускать различные операционные системы, виртуальные машины или CLR на одном компьютере. Различия имеют значение. Запускайте модульные тесты на всех комбинациях платформ и сред, используя средства не прерывной интеграции. Активно занимайтесь поиском про блем, иначе потом они сами найдут вас.
На что это похоже Это похоже на модульное тестирование, но не только: это модуль ное тестирование в различных мирах.
Сохраняйте равновесие • Оборудование обходится дешевле, чем время разработчика. Но, если число поддерживаемых платформ и конфигураций велико, нужно решить, какие из них подлежат тестированию при разработке. • Некоторые ошибки, которые существуют на всех платфор мах, могут* быть замечены лишь при изменении организации работы стека, порядка выравнивания байтов в слове и т.п., поэтому, даже если у вас меньше клиентов, работающих на Solaris, чем пользователей Linux, нужно тестировать на обе их платформах. • Вряд ли вам хочется получать многократные уведомления об одной и той же ошибке (это как двойное налогообложение и засоряет e-mail). Можно снизить частоту интеграционной сборки на всех платформах/конфигурациях, кроме одной, наиболее важной: это даст вам время на устранение проблем, возникших в процессе основной сборки. Или же сделайте гак, чтобы все замеченные ошибки на всех платформах соби рались в один удобочитаемый отчет.
22 Автоматизируйте приемочные испытания “Итак, все хорошо, модульные тесты показывают, что твой код работает так, как ты этого хочешь. Поставь его. Очень скоро мы узнаем, хотят ли заказчики того же самого".
Вы работали с пользователями, чтобы ввести в код все те функции, которые им нужны, а теперь нужно убедиться в том, что результат верен или, по крайней мере, совпадает с их ожида ниями. Несколько лет назад Энди участвовал в проекте, в котором вре мя 12:00 считалось в отраслевом стандарте последней минутой те кущего дня, а 12:01 —первой минугой следующего дня (обычно де ловые и компьютерные системы считают 23:59 окончанием дня, а 24:00 — началом другого дня). Эта маленькая деталь сыграла важ ную роль, когда дело дошло до приемочных испытаний, поскольку от этого зависели результат ы работы. Наиболее важные функции бизнес-логики нужно тестировать независимо от остальных функций приложения, а пользователи должны утвердить результаты тестирования. Вряд ли нужно приглашать пользователей на просмотр резуль татов всякий раз при прохождении модульных тестов. Вместо это го, нужно автоматизировать процедуру сравнения вашей реализа ции с ожиданиями пользователей. Один лишь штрих отличает приемочные тесты от обычных мо дульных тестов. Нужно сделать гак, чтобы пользователи могли до бавлять, обновлять и модифицировать необходимые данные, не влезая в сам код. Это можно сделать разными способами. Энди использовал ряд схем: они полагались на данные, запи санные в однородный файл, который пользователи могли ре дактировать напрямую. Венкат недавно делал нечто подобное с помощью Excel-таблиц. В зависимости от ваших конкретных усло вий можно использовать средства, наиболее привычные для ва ших пользователей, —обычный текстовый файл, таблица Excel, база данных или другие. Кроме того, можно использовать сущест вующий механизм, который уже используется вами в решении по добных задач. FIT1 — среда иитегрированного тестирования (Framework for Integrated Testing) —полезная технология, облегчающая использо1 http://fit с2 com
ваиис I ITML-таблиц для задания и сравнения тестовых значений с результатами. С помощью FIT заказчик описывает новую функцию, а также примеры ее использования. Далее заказчики, тестеры и разработ чики на базе созданных примеров составляют таблицы, которые задают возможные значения для входных и выходных данных для данной функции кода. Потом разработчик может написать тести рующие процедуры для сравнения примеров в ЕГГ-таблицах с вы ходными данными разрабатываемого кода. Результаты тестирова ния —удачные и неудачные тесты —представлены в виде HTMLстраницы, облегчая пользователям просмотр. Если специалисты в данной отрасли дают вам алгоритмы, фор мулы для расчета и т.п., нужно обеспечить им возможность само стоятельного тестирования вашей реализации (см. Практика 35, Атакуйте проблемы в изоляции, с. 152). Добавьте эти тесты в свой стандартный набор тестов, чтобы быть всегда уверенным в резуль татах расчетов в течение всего жизненного цикла проекта. Пишите тесты для основной бизнес-логики приложения.
Пользователи должны самостоятельно верифицировать эти тесты, а вам нужно добавить их в базовый набор тестов, кото рый автоматически запускается при каждой проверке кода.
Венкат рассказывает...
Сбор приемочных данных Один заказчик применял модель ценообразования, которая была им раз работана с помощью Excel. Мы написали тесты, которые сравнивали ре зультаты работы нашего кода ценообразования с выходными данными Excel-таблиц заказчика, после чего мы корректировали нашу логику и фор мулы, если это было необходимо. В результате у всех появилась уверен ность в том, что критичная бизнес-логика ценообразования работает пра вильно, а заказчик получил возможность легко менять приемочные крите рии оценки нашего продукта.
На что это похоже Это похоже па кооперативное модульное тестирование: вы попрежнему пишете тесты, но кто-то другой сообщает вам правиль ные ответы.
Сохраняйте равновесие • Не все заказчики могут заранее предоставить вам точные данные для тестов. Если бы верные ответы им были извест ны, им не пришлось бы заказывать новую систему. • Вы можете обнаружить (на компьютере или в руководстве пользователя) неизвестные прежде ошибки в старой системе или более серьезные проблемы, которые ранее не существо вали. • Используйте бизнес-логику заказчика, но не увлекайтесь де тальным ее описанием в сопровождающей документации.
23 Измеряйте фактическое продвижение “Используй акты учета рабочего времени (time sheets) для отчета о состоянии работ. Они будут использованы для пла нирования работ по проекту. Заполняй по 40 рабочих часов в неделю, независимо от того, сколько времени ты потра тил на эту работу в действительности.
чЛ
'*■
Временные затраты (которые обычно сильно занижены) слу жат прекрасным источником обратной связи: ведь наилучшее средство оценить, насколько вы укладываетесь в график работ, — это сравнить фактические временные затраты на выполнение ра боты с вашими предварительными оценками. Вы говорит е, что уже отслеживает е это с помощью таймшитов. К сожалению, в большинстве крупных компаний таймшиты ис пользуются для начисления зарплаты, а не для измерения реально го продвижения в работе над софтверными проектами. Даже если вы потратили шестьдесят часов па выполнение работы, ваш босс, вероятно, попросит заполнить лишь сорок часов в таймшите —это для бухгалтерии. Таймшиты редко говорят о том, какая часть рабо ты выполнена на самом деле, и, следовательно, не Moiyr быть по лезны для достоверного планирования, составления сметы или из мерения производительности. Сосредоточьтесь на выполнении задачи
Даже и без таймшитов, некоторые разработ чики с трудом могут заставить себя сосредо* точиться на выполнении задачи. Вам когданибудь приходилось слышать, как разработ чик сообщает, что он на 80% выполнил свою задачу? Проходят дни, недели, а у него по-прежнему 80%. Это по меньшей мере бесполез но —оценивать ход работ ы таким способом. Эт о то же самое, что го ворить правду на 80% (если вы не политик, то правда (true) и ложь (false) являются булевыми состояниями). Вместо того чтобы пы таться оценить некую фиктивную степень “готовности", попытай тесь определить, сколько еще вам осталось сделать до полного вы полнения задачи. Если вы изначально отвели на выполнение задачи 40 часов, а спустя 35 часов работы вдруг поняли, что потребуется еще 30 часов, тогда это может служить важным показателем (чест ность играет’ здесь важную роль, нет смысла скрывать очевидное). Когда вы, наконец, решили задачу, оцените, сколько времени потрачено на нее в действительности. Весьма вероятно, что это время превысит ваши предварительные оценки. Это нормально; просто пометьте себе, чтобы учитывать это в дальнейшем. Когда
будете оценивать временные затраты на решение следующей зада чи, корректируйте их исходя из накопленного таким способом опыта. Если вы планировали справиться с какой-то задачей за два дня, а сделали се за шесть, значит, ваша оценка оказалась втрое за ниженной. Если при этом не было каких-то чрезвычайных обстоя тельств, возможно, вам стоит впредь умножать свои ожидания па три. Какое-то время вы будете двига ться зигзагообразно, то зани жая, то завышая свои оценки, но спустя некоторое время ваши про гнозы будут все ближе подходить к реальности, и вы сможете луч ше предугадывать сроки выполнения для каждой конкретной задачи. Удобно измерять продвижение, постоянно отслеживая задачи, которые еще осталось выполнить. Наилучший способ для этого — ведение перечня задолженностей (невыполненных заказов). Перечень задолженностей —эго просто список задач, которые пока еще не выполнены полностью. Когда задача выполнена, она изымается из перечня (можно просто вычеркнуть или пометить ее как выполненную в общем списке задач). Когда появляются новые задачи, для них определяется приоритет и они добавляются в пере чень. Можно вести индивидуальный список задач, список задач д ля текущей итерации и список для всего проекта1. Используя список задач, вы всегда знаете, какая важная задача вас ждет на следующем шаге. Поскольку ваш опыт предваритель ной оценки со временем накапливается, с каждым следующим ша гом вы все точнее оцениваете свои возможности. Это мощная техника для отслеживания фактического прогресса в работе над проектом.
Э н д и рассказы вает ...
Учет рабочего времени Жена моего брата как-то работала в крупной международной консалтинго вой компании. V них рабочее время измерялось интервалами по шесть ми нут — в течение всего рабочего дня, каждый день. Они даже применяли специальный шифр для отслеживания использования рабочего времени: каждый сотрудник вводил его для заполнения формы индивидуального учета. Причем этот шифр не был 0, 9999 или что-либо легко запоминающееся, а нечто вроде 948247401299-44Ь. Поэтому и не нужно допускать, чтобы правила и условности, изобретенные бухгалтерским отделом, переносились на вашу работу над проектом.
1 Более подробно об использовании перечней задолженностей и списков задач как индивидуального и коллективного средства управления проектом см. в “Поставь Продукт!” [RC05J.
Измеряйте, сколько работы еще осталось. Не обманывайте себя и свою команду, вводя фиктивные показатели работы. Используйте перечень задолженностей.
Спринты в методологии Scrum В методе Scrum [Sch04] каждая итерация называется спринтом (sprint) и длится 30 дней. Перечень задолженностей 8 спринте содержит лишь те за дачи, которые решаются в рамках текущей итерации, он показывает также, сколько времени требуется на завершение каждой задачи. Ежедневно каждый член команды корректирует количество часов до окон чательного решения своей задачи. В любой момент, если общее количест во часов для всех задач превышает фактическое время до окончания спринта, эти задачи должны быть перенесены на следующую итерацию. Если же до завершения итерации осталось больше времени, чем требуется для данного набора задач, тогда можно добавить новые задачи в список. Заказчикам обычно это очень нравится.
На что это похоже Это очень удобно: знать, что уже сделано, что осталось сделать и в какой последователь! юсти.
Сохраняйте равновесие • Ш ести минутные единицы измерения времени слишком мел ки и не подходят для гибких проектов. • Единицы длиной в неделю или месяц слишком велики и гоже не подходят для гибких проектов. • Сосредоточьтесь па выполнении функций, а не на календаре. • Если вы тратите слишком много времени на отслеживание того, сколько времени вы тратите на работу, так что у вас ос тается слишком мало времени собственно на работу над про ектом, это значит, что вы тратите слишком много времени на отслеживание того, сколько времени вы тратите на работу. Все ясно? • Из сорока часов рабочей недели далеко не все часы можно посвятить работе над кодом по проекту. Совещания, теле фонные звонки, e-mail и другая деятельность могут отнимать у вас довольно много рабочего времени.
24 Прислушайтесь к пользователям "Пользователи всегда чем-то недовольны. Это не твоя вина, просто они слишком глупы и плохо читают это черто во руководство пользователя. Это не ошибка в программе, они просто не понимают ничего. Это их трудностьf.
Энди как-то работал в одной крупной компании, которая зани малась разработкой программных продуктов для высокопроизво дительных рабочих станций под Unix. Это не та конфигурация, на которой вы можете просто запустить setup.exe или pkgadd, чтобы установить программное обеспечение. Нужно копировать файлы и настраивать различные параметры для конкретной рабочей станции. Энди и его команда полагали, что все идет хорошо, пока однаж ды Энди, проходя мимо отдела техподдержки, не услышал, как ин женер со смехом говорит заказчику по телефону: “Это не баг: вы сделали ту же ошибку, что и всё". И дело было вовсе не в этом одном инженере. Весь отдел от души смеялся над бедными, наивными и глупыми пользователями. Наверняка, и вы попадали в ситуации, когда вам, несчастному пользователю, приходилось залезать в какой-нибудь загадочный системный файл и записывать в него не менее загадочный набор символов, чтобы заставить какое-либо приложение работать. Ни каких сообщений об ошибке, ни падения системы, просто черный экран после внезапного сбоя приложения. Хорошо еще, если этот случай упоминается в инструкции по инсталляции продукта, но, скорее всего, примерно 80% пользователей не обратили на него внимаиия и потому стали объектом насмешек со стороны инжене ров технической поддержки компании-разработчика. Это баг!
Как мы покажем в главе 7 (с. 143) , когда чтонибудь ломается, пользователь должен полу чать максимально подробную информацию о случившемся. Черный экран или внезапное завершение процесса (без объяснений) недопустимы. Хуже того, в приведенном выше примере компания-разработчик получила реальную обратную связь от своих пользователей, но, вместо того чтобы заняться устранени ем проблемы, лишь смеется над несмышлеными клиентами! Любая ошибка в программном продукте или в документации, или даже в нашем понимании поведения пользовательского сооб щества —это проблема самой команды разработчиков, а ни в коем случае не пользователей.
И еще был такой случай. 11екая компания поставила в производ ственный цех довольно дорогую систему управления оборудовани ем, которой никто не мог воспользоваться. Кажется, для входа в систему требовалось ввести имя и пароль, а большинство рабочих на этом производстве были неграмотными. Никому м в голову не приходило спросить их и получить обратную связь, так что систе ма оказалась абсолютно бесполезной. Разработчикам потом при шлось полностью переделать GUI (графический интерфейс поль зователя), заменив все надписи картинками, что потребовало огромных дополнительных затрат. Мы тратим много сил на обеспечение обратной связи от самого кода, не жалея времени на создание модульных тестов и тому по добных инструментов, но игнорировать мнение пользователей слишком безответственно. Поэтому нужно разговаривать с настоя щими пользователями (не с их руководителями или их заместите лями, бизнес-аналитиками) и внимательно прислушиваться к тому, что они говорят. Даже если они говорят, на ваш взгляд, полную чушь.
Любая жалоба имеет под собой почву . Докопайтесь до сути и устраните реальную проблему.
На что это похоже Не нужно раздражаться, выслушивая глупые жалобы. Вернитесь назад, и вы увидите, в чем заключается главная проблема.
Сохраняйте равновесие • Для вас нет такого понятия —бестолковый пользователь. • Тупой, самоуверенный разработчик —это бывает па самом деле. • “Просто оно так устроено” нельзя считать ответом. • Если нельзя исправить код, то документацию или обучение пользователей скоррект ировать всегда проще. • Ваши пользователи могут прочитать всю документацию, по сле чего будут держать в голове все, что нужно знать о вашем приложении, и, разумеется, вечно. Но, скорее всего, у них этого не получится.
Каждый глупец может сделать вещи больше, сложнее, интенсивнее. Требуется со всем чуть-чуть гениальности и очень много смелости, чтобы двигаться в противопо ложном направлении. Д ж ои Драйдеи, Послание неизвестного апостола к прихожанам
Глава 6
Гибкое программирование Когда вы только начинаете писать систему с нуля, ваш код понятен и с ним легко работать. Однако по мере дальнейшей разработки вы замечаете, как проект медленно превращается в монстра, поддер жание которого в рабочем состоянии отнимает у разработчиков все больше времени и стоит им нечеловеческих усилий. Что превращает проект, который начинается столь успешно, в трудно управляемую систему? В процессе работы над задачами вы, вероятно, иногда склонны искать наиболее простой и быстрый путь решения проблем. Тем не менее, довольно часто выходит гак, что быстрое решение лишь откладывает текущую проблему, а не устраняет ее (как мы уже видели в Практике 2, Быстрая правка не решает проблем, с. 16). Эта нерешенная проблема может в даль нейшем преследовать вас и всю остальную команду, как только вре мя начнет поджимать. Каким образом можно поддерживать нормальную интенсив ность работы в течение всего процесса разработки, без чрезмерно го напряжения к концу проекта? Наиболее простой путь —следить за состоянием кода. Немного усилий каждый день в течение всего цикла разработки помогут уберечь ваш код от порчи и сделать при ложение более понятным и удобным в сопровождении. Практики, приведенные в данной главе, помогут вам разрабо тать код, который легче поддается пониманию и расширению, а также сопровождению в течение жизненного цикла проекта и да лее. Эти практики научат вас приемам санитарной проверки кода и не допустят его превращения в монстра.
С самого начала нужно стремиться быть предельно ясным, а не умным, П рограм м ирова ть осмысленно и выразительно; с. 110. Комментарии Moiyr помогать, а могут серьезно отвлекать; необхо димо всю важную информацию Говорить самим кодом; стр. 116. В инженерном искусстве и прикладных науках за все приходится расплачиваться, поэтому нужно очень ответственно подходить к принятию решений и продумывать все последствия их реализа ции. Чтобы принимать оптимальные решения, важно уметь Ак тивно анализировать все плюсы и минусы; с. 122. Поскольку проект выполняется поэтапно (инкрементами), уме стно также П исать код инкрементами; см. с. 126. Создавая любую программу, бывает очень трудно Следить за простотой кода; на самом деле, написать простой код намного труднее, нежели слож ный и запуганный. Но достижение этого результата окупит все ваши усилия сторицей, как мы покажем на с. 128. Принципы крепкого объектно-ориентированного проектиро вания систем предполагают, что нужно Писать связный код, о чем мы поговорим на с. 131. Прекрасный способ поддержки чисто т а кода и недопущения его запутывания —следование принципу Говорите, не спраш ивайте, который обсуждается на с. 135. И, на конец, вы можете сделать свой код гибким, поддающимся измене ниям в неопределенном будущем, если спроектируете систему так, что ее всегда можно будет Заменить, сохраняя контракт, о чем пойдет речь на с. 138.
25 Программируйте осмысленно и выразительно “Хорошо, когда код нормально работает и доступен для по нимания, но еще важнее сделать его интеллектуальным. Тебе платят за изобретательность — вот и покажи нам, на что ты способен".
Хоар о проектировании программного обеспечения (Hoare on Software Design) Чарльз Энтони Ричард Хоар (C.A.R. Ноаге) Есть два способа проектирования программных систем. Один путь — это создать настолько простой дизайн, который явно не имеет недостатков. Другой путь — сделать дизайн настолько сложным, чтобы он не имел яв ных (т.е. видимых) недостатков.
Вам, вероятно, много раз приходилось иметь дело с программа ми, в которых невозможно разобраться, которые сложно поддер живать в рабочем состоянии и которые, что самое ужасное, содер жат в себе ошибки. Вы можете ругать программу как угодно, в то время как ее создатели кружатся вокруг нее, как наблюдатели во круг ЫЛО, —с гой же смесыо страха, смущения и собственной бес помощности. Какая польза от кода, если никто не способен по нять, как он работает? При разработке кода всегда нужно отдавать предпочтение читаемости кода, а не удобству кодирования для разработчика. Нужно помнить, что код пишется однажды, а читается потом многократно, поэтому лучше потратить больше времени на его на писание (снизив продуктивность кодирования) только ради того, чтобы он стал более читабельным. На самом деле, ясность и читае мость кода намного важнее высоких показателей вашей продуктив ности. Например, если аргументы по умолчанию или необязательные параметры делают текст вашего кода менее читабельным и затруд няют его понимание или создают благоприятные условия для появ ления ошибок, лучше сделать их явными, чтобы не создавать из лишней путаницы впоследствии. Когда вы вносите изменения в код, исправляя ошибки или до бавляя новые свойства, всегда старайтесь подходить к этому сис темно. Сначала необходимо разобраться в том, что делает1 про грамма и как она работает. Потом нужно определить, что вы собираетесь изменить в ней. Затем вы вносите изменения и тести руете. Первый шаг (изучение работы программы), как правило, наиболее трудный. Если кто-то вам отдает код, который доступен для понимания, этим он существенно облегчает вашу жизнь. Сле
дуя Золотому правилу, вы обязаны сделать свой собственный код столь же ясным и читаемым. Один из приемов сделать код попятным —это обеспечить, что бы сам текст его ясно говорил о том, что происходит. Рассмотрим некоторые примеры: coffeeShop. PlaceOrder(2);
Видя такой текст, вы могли бы заключить, что мы подаем заказ (place an order) в кафе (coffee shop). Только что же может означать параметр 2? Может, это 2 чашки кофе? Или двойная крепость? Или размер самой чашки? Чтобы узнать это наверняка, вам придется искать описание данного метода или обратиться к документации. Этот текст кода нельзя понять по его прочтению. Можно добавить комментарии, чтобы хоть как-то облегчить по нимание данного фрагмента: cof feeShop. Place0rder(2 / * large cup * /) ; Л большая чашка • /
Э го немного улучшает ситуацию, но это как раз то г случай, ко гда комментарии призваны скомпенсировать плохой стиль про граммирования (см. Практика 26, Говорите самим кодом, с. 116). Java 5,.NET и другие языки и архитектуры используют концеп цию перечислимых типов. Попробуем применить ее в нашем слу чае. Определим переменную CoffeeCupSize в C# следующим обра зом: public enum CoffeeCupSize (Размер чашки кофе)
{ Small, (маленькая) Medium, (средняя) Large (большая)
} После чего мы можем использовать ее для заказа кофе: cof feeShop. PlaceOrder(Cof feCupSize. Large):
Этот код уже ясно говорит о том, что мы заказываем большую1 чашку кофе. Как разработчик вы всегда должны спрашивать себя, что еще можно сделать для повышения ясности кода. Вот другой пример: 1 Это отдушина для вас, поклонники Starbucks (крупнейшая в мире сеть ко* феен. — Прим. пер.).
Строка 1
public in t compute(int val)
{ in t result = val < 1;
/ / ... некий код ... 5
return result;
} Что означает оператор сдвига в строке 3? Если вы хорошо зна комы с двоичными операциями или логическими схемами, или же с программированием на Assembler, тогда вы вполне можете предположить, что значение переменной val мы прбсто умножи ли на 2.
Принцип Р(Е Код, который вы пишете, должен отражать смысл того, что вы делаете, и быть выразительным. В этом случае код получится читаемым и доступ ным для понимания. Если ваш код написан таким образом, что он не сбива ет с толку, вы избежите также многих потенциальных ошибок. Программи руйте осмысленно и выразительно (принцип PIE — Program Intently and Expressively).
Как быть с теми, у кого нет за плечами такого опыта? Ч то поду мают они про операцию в строке 3? Возможно, в вашей команде есть менее опы тны е люди, которые совсем недавно занялись программированием. Эти люди будут чесать в затылке до тех пор, пока все волосы выпадут1. Хотя такой код, возможно, рабо тает эф ф ективно, его текст недостаточно осмыслен и вырази телен. Сдвиг ради умножения служит примером излишней и небезо пасной оптимизации кода по времени исполнения. Код resu lt = val*2 выглядит понятнее, работает и даже, вероятно, более эффек тивно исполняется, если у вас нормальный компилятор (старые привычки проходят с трудом; см. Практику 7, Умейте вовремя разу читься, с. 37). Вместо того чтобы писать слишком интеллектуаль но и туманно, следуйте принципу PIE: Программируйте осмыслен но и выразительно (Program Intently and Expressively; см. врезку выше). Нарушение принципа PIE может не только снизить читаемость и ясность кода —оно такжз может негативно сказаться на правиль ности работы. 1 Нет. это не лысина, это —солнечная батарея для кодирующей машины...
Вот ме тод на С#, ко торый пытае тся синхронизовать вызовы ме тода MakeCoffce() класса CoffeeMakcr: public void MakeCoffeeO
{ lo c k (th is )
< , действие ) )
Автор данного кода хотел определить критическую секцию, чтобы в любой момент времени только один поток мог исполнять код в секции (т.е. “действие”). Для этого он пы тается блокировать экземпляр класса Cof feeMaker. Поток сможет исполнять данный ме тод, только если он не заблокирован в данный момент (в Java вме сто lock используется synchronized, но смысл тот же). Хотя данный фрагмент кода покажется вполне разумным любо му программисту на Java или .NET, он скрывает в себе две коварные проблемы. Во-первых, критическая секция имеет слйшком широ кий охват, а во-вторых, делается попытка заблокировать объект, имеющий глобальную область действия (вся программа). Рассмот рим обе проблемы более подробно. Предположим, наша кофеварка может готовить также просто кипяток —для тех, кто утром предпочитает пить чай с бергамотом. Допустим, нам хочется синхронизовать метод GetWater() и мы ста вим lock( t h i s ) внутри него. Это обеспечит синхронизацию для лю бого кода, который использует блокировку экземпляра Cof feeMaker с помощью lock. Это приведет к тому, что нельзя готовить кофе и наливать кипяток одновременно. Может, этого мы и добивались, но, вполне вероятно, мы заблокировали больше, чем требуется. Из самого текста кода этого не понять, и его пользователям остается лишь недоумевать. Кроме того, реализация метода MakeCoffeeO блокирует объект Cof feeMaker, который виден всему приложению. Что произойдет, если вы заблокируете экземпляр Cof feeMaker в одном потоке, а за тем другой поток вызовет метод MakeCof fee() для того же объекта? В лучшем случае это снизит производительность, в худшем приве дет к взаимной блокировке (deadlock). Попробуем применить принцип PIE к данному коду, изменив его так, чтобы код стал более определенным. Мы хотим разрешить лишь одному потоку исполнять метод MakeCoffee() в каждый мо мент времени. Почему бы не создать объект специально для этих целей и не заблокировать его?
private object
makeCoffeeLock
= new object():
public void MakeCoffee() { lock(makeCoffeeLock) { / / ... действие ) )
Данный код устраняет обе проблемы, рассмотренные выше: для синхронизации мы вводим явный объект, и, кроме того, мы выра жаем свои намерения более понятно. Когда вы пишете код, используйте наиболее выразительные возможности языка. Названия методов должны передавать их предназначение, имена параметров должны максимально отра жать их смысл. Исключения должны сообщать, что именно могло произойти и какие меры нужно принять; используйте их активно и выбирайте для них подобающие названия. Если вы соблюдаете дисциплину при написании программ, это сделает ваш код более понятным, значительно снизит необходимость в комментариях и в подробной документации.
VH
Пишите код так, чтобы он был понятным, а не умным. Вы-
f
ражайте свои намерения как можно яснее для тех, кто будет читать ваш код. Нечитаемый код нельзя назвать умным.
На что это похоже Вы видите, что вы (или кто-то другой) можете быстро разобраться в коде, который вы написали год назад, и понять, что он делает, лишь по однократном прочтении.
Сохраняйте равновесие • То, что очевидно для вас сегодня, спустя год может быть не очевидно для других или даже для вас. Представьте, что ваш код подобен запечатанной капсуле, которая будет вскрыта в неопределенном будущем. • Для вас не существует приятия потом. Если вы не сделаете этого прямо сейчас, вы не сможете сделать это потом.
Осмысленный код не означает, что нужно создавать больше классов или типов. Чрезмерная абстракция не имеет ничего общего с осмысленностью. Используйте тип связывания, который наиболее подходит для данной ситуации: например, свободное связывание че рез хеш-таблицу должно использоваться там, где элементы свободно связаны и в реальности. Не используйте его гам, где компоненты тесно связаны между собой, так как этот ме ханизм плохо отражает реальные отношения между объек тами.
26 Говорите самим кодом "Если код труден для чтения, ему требуются комментарии. Подробно объясняй, строчка за строчкой, что ты делаешь. Не объясняй, зачем ты это делаешь. Нам нужно знать, что делает код, и все'1.
Обычно программисты ненавидят писать документацию. Это потому, что документация, как правило, хранится отдельно от кода, и постоянно обновлять ее параллельно с кодом довольно трудно. Как результат, нарушается принцип DRY (Donl Repeat Yourself —He повторяйтесь) [HTOO]), а сама документация может лишь вводить пользователей в заблуждение, а этот обычно хуже, чем полное отсутствие документации. Более эф ф ективное описание кода достигается комбинирова нием следующих двух приемов: говорить языком самого кода и использовать комментарии для сообщения иекодовой инф ор мации. Если вы читаете метод, желая понять, что он делает, у вас может уйти на это уйма времени и сил, прежде чем вы сможете использо вать этот метод в своем коде. С другой стороны, всего несколько строчек комментария с объяснением поведения метода могут за метно облегчить вашу жизнь. Вы быстро можете понять, для чего он нужен, каковы ожидаемые результаты и за чем необходимо сле дить —т.е. в этом случае вы тратите намного меньше усилий. Не комментируйте
Нужно ли описывать весь ваш код? До опрсделенной степени — да. Но это не значит, что нужно использовать комментарии везде, особенно в теле ваших методов. Исходный текст кода должен быть понятен не потому, что в нем много ком ментариев, а в силу четкого и ясного стиля написания: выбора под ходящих имен для переменных, умелого использования пробелов и табуляции, разделения логики, компактных выражений. Имена могут играть важнейшую роль. Зачастую тот, кто читает код, ограничивается чтением имен программных элементов1. Хо рошо подобранные имена могут точно передать ваши намерения и сообщить любую информацию читателю вашего кода. С другой стороны, применение искусственных схем именования (напри-
ДЛЯ прикрытия
1 Например, в романе “Волшебник Земномооья” знание подлинного имени дает полную власть над тем, что носит это имя. Магическая сила, заключенная в имени. — весьма распространенная тема в литературе и мифологии, а в про граммировании она имеет' еще и прикладное значение.
мер, в венгерской нотации, теперь уже непопулярной) может серь езно затруднить чтеиис кода и его понимание. Эти схемы обруши вают па пас поток низкоуровневой информации о типах данных, жестко закодированной в именах переменных и методов, что дела ет код хрупким, негибким. При хорошо подобранных именах и четко обозначенных вет вях выполнения код почти не нуждается в комментариях. На са мом деле, когда Энди и его соавтор Дэйв Томас написали первую книгу о языке программирования Ruby [TH01J, они фактически могли описать (документировать) весь язык с помощью интер претатора Ruby, который читает код. И это здорово, когда код го ворит сам за себя, вместо того чтобы полагаться на комментарии: создатель Ruby, Ю кихиро Мацумого (Yukihiri Matsumoto) —япо нец, а Энди и Дейв в японском языке дальше “сукияки” и “сакэ" не продвинулись. Что значит хорошо подобранное имя? Это имя, которое несет максимум верной информации дня читателя кода. Плохое имя не передает никакой информации, а ужасное имя сообщает неверную информацию. Например, название readAccount() для метода, который на са мом деле записывает адресные данные на диск, можно считать ужасным (и такое было в действительности, см. [НТОО]). foo —великолепное, исторически сложившееся имя доя времен ной переменной, но оно ничего не сообщает о намерениях автора кода. Старайтесь избегать непонятных имен переменных. Корот кое имя не обязательно будет непонятным: “i" традиционно ис пользуется как переменная цикла во многих языках, a “s” обычно обозначает переменную типа String (строка). Это естественные обозначения, принятые во многих языках, и, хотя они короткие, их нельзя отнести к непонятным именам. Использование “$” для переменной цикла в подобной среде — плохая идея, впрочем, “indexvar” ничем не лучше. Не следует втолковывать очевидное по средством многословных имен переменных. С теми же проблемами мы сталкиваемся и в комментариях, сообщающих очевидную информацию, например / / Constructor next to a class constructor (Конструктор, следующий за конструк тором класса). К сожалению, комментарии такого сорта весьма распространены: обычно они вставляются в код чересчур заботли вой средой разработки (IDE). В лучшем случае, они добавляют шуму в исходный текст кода. В худшем —со временем они сгановятся просто неверными. • Многие комментарии просто не сообщают никакой полезной информации. Например, какая польза вам от комментарии: “This
m ethod allows you to passthrough” (Данный метод позволяет вам пе ресылать данные) к методу под названием passth rough()? Коммен тарии такого рода лишь отвлекают внимание и со временем могут стать неуместными (если вы мотом измените название метода на sendToHost()). Бывает полезно использовать комментарии в качес тве схем на правлений, чтобы читателю кода можно было выбирать правиль ные пути. Для каждого класса или модуля вы можете добавить короткое описание его назначения и любых специфических требо ваний. Для каждого метода в данном классе хорошо бы сделать по яснения, например: • Назначение (Purpose): Зачем существует этот метод? • Требования (Pre-conditions, предпосылки или предваритель ные условия): Какие входные данные требуются, и в каком со стоянии должен находиться объект, чтобы данный метод ра ботал? • Обещания (Post-conditions, результаты или окончательные условия): В каком состоянии находится объект, и какие зна чения возвращаются после успешного выполнения данного метода? • Исключения (Exceptions): Что может произойти не так, как надо, и какие исключения могут быть обработаны? Благодаря таким инструментам, как RDoc, javadoc, и ndoc, у вас есть возможность легко создавать полезную, хорошо отформати рованную документацию непосредственно из комментариев, встроенных в ваш код. Эти средства берут ваши комментарии и создают на их базе сим патичный HTML-файл, снабженный гиперссылками. Ниже представлены некоторые выдержки из документирован ного кода на С#. Обычная строка комментариев начинается с а комментарии, которые предназначены для документации, начи наются с “/ / / ” (разумеется, они работают и как обычные коммен тарии). Using System; namespace Bank
{ / / / <summary> / / / A BankAccount represents a customer’ s non-secured deposit / / / account in the domain (see Reg 47.5, section 3). / / / public class BankAccount
{ / / / <summary> / / / Increases balance by the given amount. / / / Requirements: can only deposit p positive amount. / / /
III / / / <param name = "depositAmount">The amount to deposit, already / / / validated and converted to a Money object / / /
/// / / / <param name =' ‘'depositSource">Origination of the monies / / / (see FundSource for details) / / /
III Ш
Resulting balance as a convenience
/ / /
III I I I Exception cref = MlnvalidTransactionException”> / / / I f depositAmount is less than or equal to zero, or FundSource / / / is invalid (see Reg 47.5 section 7) Ш or does not have a sufficient balance.
I l l public Money Deposit(Money depositAmount, FundSource depositSource)
{ ) } } На рис 6.1 показан фрагмент документации, созданный с помо щью ndoc на базе комментариев в приведенном примере С#-кода. Javadoc для Java, RDoc для Ruby и другие подобные средства работа ют аналогичным образом. Такое описание кода предназначено не только для людей, рабо тающих за пределами вашей команды или организации. Пред ставьте, что вас просят исправить код, который написан вами не сколько месяцев назад: вам будет намного легче это сделать, если вы можете бегло просмотреть комментарии в начале каждого метода и вытащить из них те детали, которые необходимы вам для выполнения задачи. Если какой-либо метод работает лишь во время полного солнечного затмения, хорошо бы знать об этом заранее без необходимости подробного изучения всего текста кода —или десятилетнего ожидания момента наступления данного события.
6 * L U n k C C o iiP b O b c u m e n l i t in n Me Ip
© & И* SШ -ocrft в«1 £fmrfi | | imthj : Ш 5Z* - В«*Ааы№1Out
О
Slop
©
Rrf-cih
Й
Home
< ® Qfrtan: Iff ftn
и в а й » tA^oAte t f the
а*поцп(. «*<цл rt m cnt*; n n only depgut а оэнь-*
Вй^ГтМИМнпЬ. ;*)
Селзь
a n J i li c Д т - у I w t H
.1
%v:a'.js^*LVj8^ss№at4^aiiiBPT.i.jr'
аьш ТшгнАЕпчтчг* J«6Q»ttfa«r(4
: (и OUutefllCjWkUClM CwirfttfoMJew*I, j Ct-lftrtrOSepOi*‘ \
Ш
F ira m a tin
JfltpTh* oJiM rfcStuft* trr.our.+ ;g deporit, aEnadr
■- (Q F iiv ij( « « 04?1
4*-*J$«ice Mart** ■ f Ffuiji ла»tenuis.-\
MHN>* ^ InvefcdrHfKAfWrtu 1 Jj ln*aW rivnKbonE* & ф Mfthctfi i? (£j ивчуОя.
ш»Л » г и -*г » в bo а Чол»г objart
fnigirutiafi at vti-в m o n n [ la t e'unJStgrc* fnr detaitij Return V alu r
Rsiulting h iJin c t 4J a клу*ши%и Е и са р М а м
jS*SS^SWi3S^V--.;
ji j McndyMwfceti Щ Meney СоиЛиеим
:nr jkdTf anta ctw rt Scotian
* ф M tftrt!
Er tQS^iAocartClSfi 1) 1**vWpACtAJTl Мы ;i] SawtpsaecftrtСв
con*№fan. Щ&о ■If d«C?otitAmt>ii/i£ 11 Irvt гЬлл nr аф»*1 со i t t o , or FundSoure* it mvalid {tea ftpg
i
Sols Al>o й а гк й^евшщ
Рис. 6.1.
J Ьдг^ Нашем?*!-.*
Документация, созданная на базе встроенных комментариев с помощью ndoc
Код всегда читается во много раз чаще, чем пишется, гак что не много дополнительных усилий с вашей стороны для его докумен тирования окупятся сполна. Комментарии д ол ж н ы сообщать инф ормацию. Исполь зуйте тщательно продуманные, имеющие смысл имена и на звания. В комментариях описывайте назначение кодовых элементов и их ограничения. Не подменяйте хороший код комментариями.
На что это похоже Комментарии должны стать для вас хорошими друзьями; вы може те только взглянуть на них —и уже будете точно знать, что и как де лает код и зачем.
Сохраняйте равновесие Блез Паскаль однажды сказал, что у него нет времени писать короткое письмо и потому ему придется писать длинное. По тратьте время на написание коротких комментариев. Не используйте комментарии там, где сам код может быть бо лее красноречив. Писат ь в комментариях, что именно делает код, приносит* мало пользы; лучше объясните, почему он эт о делает. Когда вы переопределяете метод, сохраняйте назначение ба зового метода и оставляйте комментарии, в которых описы вается цель и ограничения для исходного (переопределяемо го) метода.
27 Активно анализируйте все плюсы и минусы "Быстродействие, продуктивность, изящество, стоимость и сроки вывода на рынок — вот наиболее важные, критиче ские аспекты разработки программных продуктов. Нужно стремиться к максимально высоким результатам на всех этих фронтах".
Вы можете оказаться в такой команде, где менеджер или заказ чик придают огромное значение внешнему виду вашего приложе ния. Бывает и так, что клиенты наибольшее внимание уделяют производительности продукта. Внутри самой команды один из ведущих разработчиков может отстаивать некую “правильную*' парадигму написания приложе ний и ставить эту концепцию превыше всего остального. Любые перекосы такого рода без учета их реальных последствий для всег о проекта могут привести к трагическим результатам. Безусловно, производительность валена всегда, поскольку низ кая производительность делает невозможным успех продукта на рынке. Однако, если ваше приложение и так работ ает достаточно быстро, нужно ли тратить силы на то, чтобы сделать его еще быст рее? Вряд ли. Есть много других аспектов, которые также важны для вашей системы. Вместо того чтобы тратить время в погоне за еще одной миллисекундой, подумайте, не лучше ли побыстрее вы вести ваше приложение на рынок —с меньшими трудозатратами и по более низкой цене? Рассмотрим один пример из жизни: .NET-приложение для Windows, которое должно связываться с удаленным Windows-cepвером. У вас есть два пути для технической реализации этой воз можности: использовать либо средства удаленного доступа в .NET, либо web-сервисы. Сегодня одно лишь упоминание о web-сервисах вызывает у некоторых разработчиков примерно такую реакцию: “У нас обе системы (приложение и сервер) работают под Windows, а в литературе рекомендуется использовать для таких ситуаций средства удаленного доступа .NET. Кроме того, web-сервисы рабо тают медленно, и у нас возникнут проблемы с быстродействием”. Это своеобразный политический курс. Тем не менее, как раз для данного случая web-ссрвисы предлага ли наиболее простой способ-решения задачи. Простые замеры производительности показали, что размер XML-файла1оказывал 1 Документы XML в чем-то, как люди, —они прелестны и забавны, пока ма ленькие. но часто раздражают, когда вырастают большими.
ся совсем небольшим, а время, затраченное на создание и анализ XML-текста было ничтожно малым по сравнению со временем сра батывания самого приложения. Выбор в пользу web-сервисов не только экономил время для коротких запусков приложения, но и оказался более дальновидным, поскольку данной команде разра ботчиков пришлось в дальнейшем переключить свою систему на обслуживание сторонней фирмой.
Энди рассказывает...
Не впадайте в крайности У меня был один клиент, который был настоящим фанатом конфигурирова ния — настолько, что их приложение имело порядка 10000 настроек конфи гурации. Добавление кода в систему превратилось в весьма трудоемкий процесс, из-за необходимости поддержки приложения, управляющего кон фигурацией, и базой данных. Но они клялись, что такой уровень гибкости им жизненно необходим, поскольку все их заказчики имеют разные требо вания, и потому им нужны различные настройки. У них было всего девятнадцать клиентов, и их ожидаемое количество не должно было превысить пятидесяти. Не слишком разумное решение.
Рассмотрим приложение, которое должно брать данные из внешней базы данных и на экране представлять их в виде табли цы. Можно применить изящный объектно-ориентированный подход: данные извлекаются из базы данных, создаются объекты, которые затем передаются на уровень интерфейса пользователя (UI). На уровне UI (UI tier) данные извлекаются из объектов и за полняют таблицу. Какие еще достоинства у этого подхода, кроме изящества? Может быть, достаточно ввести уровень данных, с помощью которого организовать данные в набор или коллекцию, а затем запол нить ими таблицу? В этом случае вам не нужно заботиться о созда нии и уничтожении объектов. Если вам нужно всего лишь отобразить данные, зачем тратить лишние усилия на создание объ ектов? Если не пользоваться объектами, можно сэкономить много сил (к тому же программа будет работать быстрее). Конечно, этот подход имеет серьезные недостатки, и самое главное —это пони мать, в чем заключаются эти недостатки, а не слепо отказываться от старых привычек. \ Все-таки стоимость разработки и время выведения продукта на рынок —это факторы, которые могут серьезно влиять на успех ва шего приложения. Компьютеры с каждым днем становятся все дешевле и быстрее, гак что можно потратить деньги на обору
дование и купить производительность, а освободившееся таким способом время потратить на совершенствование других аспектов ваших продуктов. Конечно, все это работает хорошо лишь до определенной степе ни: если ваши потребности в оборудовании велики, т.е. вам нужна компьютерная сеть распределенных вычислений (grid) и порядоч ный штат сотрудников для ее поддержки (допустим, порядка мас штаба Google), тогда явный перевес будет на противоположной стороне. Только кто же может решить —хватает ли производительности и насколько “ослепителен” внешний вид приложения? Заказчики или заинтересованные стороны должны провести оценку* и при нять решение (см. Практика 10, Позвольте заказчику принимать решения; с. 49). Если ваша команда считает, что производитель ность нужно повысить или вы можете сделать нечто более привле кательное на вид, проконсультируйтесь с заинтересованной сторо ной, и пускай она решает, на чем вам следует сосредоточить свои дальнейшие усилия. Нет наилучшего решения
Нет такого решения, которое бы работало лучше всех при любых условиях. Вам нужно оценить проблему всеми доступными сред ствами и придти к наиболее разумному ре шению. Дизайны сильно отличаются друг от друга в том, как они решают ту или иную проблему, и самые лучшие из них выбираются путем поиска компромиссов.
Vq
j щ лй
Активно анализируйте все плюсы и минусы. Оцените быстродействие, удобство, продуктивность, себестоимость и время выхода на рынок. Если быстродействие удовлетворительно, сосредоточьтесь на других аспектах. Не усложняйте дизайн только ради видимого повышения быстродействия или изящества.
На что это похоже Хотя вы не можете иметь всего, вы имеете наиболее важные вещи —те свойства, которые важны для заказчика.
Сохраняйте равновесие • Если вы тратите дополнительные усилия сейчас, чтобы по лучить некий ощутимый результат потом, убедитесь, что
ваши усилия окупятся (в подавляющем большинстве случаев это не так). • Действительно высокопроизводительные системы проекти руются таковыми с самого начала. • Необдуманная оптимизация —источник всех зол1. • Решение или подход, который вы применяли в прошлом, мо жет быть —или не быть —применим к вашей нынешней ситуа ции. Не принимайте безоговорочно ни то, ни другое утвер ждение, найдите подтверждения или опровержения.
1 Дональд Кнут: Собрание изречений Хоара [Knu92]
28 Пишите код инкрементами “Настоящие программисты работают по многу часов под ряд, без перерывов, не поднимая головы. Не останавливай ся ради компиляции — просто сиди и печатайГ
Когда вам нужно проехать долгий пугь на машине, будете ли вы все время крепко держать руль в одном положении, смо греть прямо впе ред и давить на газ до упора в течение пары часов? Конечно, нет. Нуж но вести машину. Нужно постоянно следить за движением транспор та вокруг вас. Нужно проверять, сколько осталось топлива. Нужно делать остановки, чтобы заправить машину, перекусить и т.д.1 Не пишите код часами или даже минутами, не останавливаясь ради того, чтобы убедиться, что вы на правильном пути, регулярно тестируя написанное. Вместо этого пишите короткими шагами — инкрементами. Вы обнаружит е, что такой подход постепенно при ведет к лучшему качеству кода и его структуризации. Код с мень шей вероятностью станет сложным и запуганным; он будет постро ен с учетом непрерывной обратной связи, которая поступает на каждом шаге кодирования и тестирования. Когда вы пишете, и тестируете инкрементами, вы более склон ны создавать небольшие методы и классы, которые лучше “сцепля ются” друг с другом. Вы не сидите не поднимая глаз, вслепую наби рая огромные куски кода за один прием. Наоборот, вы постоянно следите за тем, как ваш код приобретает форму, внося множество мелких и незначительных поправок, вместо немногих, но поисти не масштабных. Пока вы пишете код, постоянно ищите тропинки, которые мо^ гут привести к улучшению его качества. Хорошо бы еще следить и за его читаемостью. Возможно, вы можете разбить какой-то метод на более мелкие методы, что облегчит его тестирование. Многие небольшие усовершенствования, которые вы обычно вводите в код, напрямую относятся к рефакторингу (см. “Рефакторинг: Улуч шение существующего кода” [FBB+99]). Можно использовать так же метод разработки на базе тестов (см. Практика 20, Используйте код до сборки; с. 92) для обеспечения пошаговой разработки. Смысл заключается в том, чтобы все время делать что-то неболь шое и полезное, вместо того, чтобы сначала долго писать огром ный код, а потом проводить его мощный рефакторинг. Это и есть гибкий подход.
1 Кент Бек использовал аналогию с вождением автомобиля, подчеркнув важность рулевого управления, в книге “ХР Explained” (ВесОО].
I
Пишите код короткими циклами: правка/сборка/тестиро вание. Это лучше , чем кодирование в течение продолжитель ного периода времени. В результате ваш код получится про ще, яснее, его будет легче поддерживать.
На что это похоже Вы чувствуете потребность запустить сборку/тестирование, как только вы написали несколько строчек. Вы не хотите заходить слишком далеко без обратной связи.
Сохраняйте равновесие • Если сборка и тестирование отнимают слишком много вре мени, вы не захотите проводить их достаточно часто. Сде лайте гак, ч тобы тесты работали быстро. • Во время запуска на компиляцию и тестирование останови тесь и обдумайте свою работу, посмотрите на нее со стороны, не вдаваясь в детали. Это хороший способ не сойти с рельсов. • Когда у вас перерыв, это должен быть настоящий перерыв. Оторвитесь от клавиатуры! • Подвергайте рефакторингу тесты так же част о, как тестируе мый код.
29 Следите за простотой кода "Разработка программных продуктов — нелегкое дело. Ка ждый дурак может написать простой и изящный код. Ты об ретешь славу и признание (не говоря уж о гарантированном рабочем месте), если будешь писать максимально изо щренные и сложные программьГ.
Допустим, вам на глаза попалась статья, где обсуждается некий принцип проектирования приложений, который преподносится как шаблон с изящным и модным названием. Вы закрываете жур нал, и вам начинает казаться, что код, который у вас перед глазами, может серьезно выиграть от применения новой идеи —того самого шаблона из журнала. Спросите себя, нужен ли он вам на самом деле, и поможет ли он решить ваши текущие проблемы. Действи тельно ли конкретные проблемы заставили вас принять такое ре шение? Не поддавайтесь желанию перепроектировать весь код и сделать его чрезмерно сложным. Энди был знаком’с одним парнем, который увлекался шаблона ми проектирования настолько, что пытался использовать их все. Причем сразу. Причем внутри одного маленького кода (порядка нескольких сотен строк). Он умудрился засунуть в несчастную про грамму что-то вроде семнадцати шаблонов из классической книги [GHJV95], пока не “попался с поличным”. Это не путь для создания гибкого кода. К сожалению, многие программисты склонны смешивать поня тия трудоемкости и сложности разработки программ. Если вы смотрите на какое-то решение и говорите, что оно простое и по нятное, его автор может на вас обидеться. Многие разработчики получают удовольствие от создания чего-то сложного —они просто светятся от радости, если слышат: “О, это так сложно; наверное, пришлось очень много потрудиться, чтобы придумать все это!” Все наоборот: вам нужно гордиться, если вы придумали простой ди зайн, который хорошо работает. Простой не значит упрощенный
Простота часто понимается искаженно (в программировании, как и в жизни вооб ще). Простота не имеет ничего общего с уп рощением, непрофессионализмом или не достаточной проработкой. Совсем наоборот. Простота, как правило, дается намного труднее, чем чрезмерно сложные и сля панные наспех решения.
Простота в кодировании или написании книг —это как фирмен ный соус шеф-повара. Сначала берется много вина, бульона и спе ций —и все это долго варится до состояния концентрированной эс сенции. Таким же на вкус должен быть хороший код —не огромная водянистая масса, а густой изысканный соус.
Энди рассказывает...
Что есть изящество стиля? Изящный код не дает повода для сомнений в своей практической пользе и прозрачности. Но сам подход к решению задачи в нем далек от тривиаль ности. Таким образом, изящество — это то, что легко постигается и высоко ценится, но трудно достигается.
Один из наиболее надежных методов оценки качества дизай на—это прислушаться к собственной интуиции. Ин туиция не есть нечто сверхъестественное, это высшее воплощение вашего опыта и знаний. Когда вы смотрите на дизайн, прислушайтесь к своему внутреннему голосу. Если что-то беспокоит вас, тогда самое время разобраться, что именно. Хороший дизайн вызывает ощущение полного комфорта.
Разрабатываете самое простое решение, которое работа-
ег. Применяйте шаблоны, принципы и технологии лишь тогда, когда для этого есть очень веские причины.
На что это похоже Вы все сделали хорошо, если нет такой строчки, которую можно выбросить без нарушения общей функциональности кода. Код лег ко отслеживается и правится.
Сохраняйте равновесие • Код можно совершенствовать до бесконечности, но по дости жении некоторого уровня дальнейшие улучшения уже не приносят ощутимого эффекта. Остановитесь и переключи тесь на другие дела еще до того, как вы выйдете на этот уро вень.
Всегда помните о том, что ваша цель —простой и читабель ный код. Натянутое изящество подобно необдуманной опти мизации и столь же вредно. Простые решения должны быть адекватны решаемым зада чам. Ж ертвовать функциональностью ради простоты —это упрощенческий подход. Краткость не означает простоту, она слишком некоммуника тивна. Ч то кажется простым для одного человека, может быть слож ным для другого.
30 Пишите связный код ‘Ты собираешься добавить новый код, и сначала тебе надо решить, куда поместить его. Нет никакой разницы, где его вставить, так что не останавливайся и просто добавь его в класс, который в данный момент развернут в твоей IDE. Если весь код располагается в одном большом классе или компоненте, за ним легче следить", Связность служит показателем того, насколько отдельные части компонента (пакета, модуля или сборки) функционально согласо ваны (связаны) между собой. Высокая степень связности говорит о том, что все элементы работают вместе на обеспечение функции или набора функций. Низкая связность означает, что элементы обеспечивают функции, которые далеки друг от друга. Представьте, что вы складываете всю одежду в один ящик. Если вам нужно найти пару носков, вам придется перевернуть всю одежду, которая там находится, —брюки, белье, футболки, и т.д., — прежде чем вы найдете нужную пару носков. Это может раздра жать, особенно если вы торопитесь. А теперь представьте, что все ваши носки парами лежат в одном ящике, все футболки —в другом и т.д. Все, что требуется теперь, —это открыть нужный ящик. Точно так же о ттого, как вы организуете программный компо нент, будет в большой степени зависеть, ваша продуктивность и возможность дальнейшего обслуживания всего кода. Если вы ре шили создать новый класс, подумайте, насколько его функции близки и тесно увязаны с функциональностью других классов внутри данного компонента. Это и есть связность на уровне ком понента. Классы тоже можно делать с разной степенью связности. В связ ном классе все методы и свойства работают вместе, обеспечивая одну функцию (или набор близко родственных функций). Чарльз Хесс в I860 г. запатентовал “Раскладное пианино, диван и комод” (см. рис. 6.2). Согласно его заявке, отличительным свой ством данного изобретения является “...заполнение неиспользуе мого пространства под пианино путем добавления дивана и комо да...” Далее он обосновывает полезность своего “трансформера”. Вы, возможно, нередко создавали в своих проектах такие классы, которые чем-то напоминают изобретение г-на Хесса. Его “связ ность” весьма сомнительна, и можно вообразить, что обслужива ние подобного агрегата (смена постельного белья, настройка пиа нино и т.д.) —вероятно, не слишком простая задача.
ffirdwh, Piano andL''sdstsna, Jf?56,U 5. PatentedJub/r, /Ш
Рис. 6.2.
Патент 56.413 США: Раскладное пианино, диван и комод
А вот пример уже из нашего века. Венкат как-то видел web-приложеиие с применением технологии ASP на двадцати страницах. Каж дая страница начиналась с фрагмента HTML и содержала множест во сценариев (скриптов) на VBScript со встроенными SQLоператорами для доступа к базе данных. Клиент справедливо запо дозри;!, что данное приложение уже успело выйти из-под контроля, и его будег трудно обслуживать. Если каждая страница кода включа ет логику отображения, деловую логику и код для доступа к данным, то в одном и том же месге происходит слишком много событий. Предположим, вы хотите внести небольшое изменение в схему базы данных. Всего лишь мелкое изменение потребует много строчной правки для каждой из страниц приложения, и такая сис тема очень быстро превращается в настоящее бедствие. Если бы приложение использовало объект среднего уровня (на пример, COM-компонент) для доступа к базе данных, то любое из менение в схеме базы окажется локализованным, что существенно облегчит обслуживание кода в целом. Последствия низкой связности могут быть достаточно неприят* ными. Пусть у вас имеется класс, который реализует пять различ ных функций. Если требования к этим функциям или их поведение как-нибудь изменятся, весь класс должен будет измениться. Если
же класс (или компонент) меняется слишком часто, то каждый раз любое изменение волной прокатывас'гся по всей системе, и в ре зультате издержки на обслуживание кода возрастают. Л 'теперь допустим, что у нас есть класс, который реализует лишь одну функцию. Оп уже не так часто подвергается изменениям. Анало гично, любой компонент, который имеет высокую связность, реже модифицируется и потому более стабилен. Согласно Принципу Разделения Обязанностей (Single Responsibility Principle) (см. Agile Software Development: Principles, Patterns, and Practices [Mar()2|), лишь одиа-единствсппая причина может приводить к изменению конкретного модуля. Полезно воспользоваться некоторыми техниками проектирова ния. Например, мы часто пользуемся шаблоном модель-видкоитроллер (Model-View-Controller) для разделения логики ото бражения, управления и модели. Данный шаблон достаточно эф фективен, поскольку позволяет добиться высокой степени связно сти: классы, относящиеся к модели, обеспечивают свой набор функциональности, сегмент управления выполняет свои задачи, а классы вида отвечают за UI (интерфейс пользователя). Связность влияет также на возможность повторного использо вания компонентов. Уровень модульности компонента (“зерни стость” его структуры) —важный аспект проектирования. Соглас но принципу Эквивалентности Повторного Использования и Релиза (Reuse Release Equivalency) ([Маг02]), “Модульность по вторного использования эквивалентна модульности релиза”. Дру гими словами, пользователь вашей библиотеки должен использо вать всю библиотеку, а не отдельную ее часть. Если данный принцип не выполняется, пользователи вашего компонента выну ждены использовать поставленный вами компонент лишь частич но. К сожалению, любые обновления системы, пусть даже их не ка сающиеся, будут сказываться и на них. Чем крупнее система, тем менее она приспособлена для повторного использования.
ш Я
Классы должны быть нацеленными на определенные функции, а компоненты не должны бьггь большими. Не поддавайтесь искушению создавать большие классы и компоненты, а также классы смешанного назначения.
На что это похоже Ваши классы и компоненты имеют определенное назначение: каж дый делает свое дело и делает его хорошо. Ошибки легко отслежи
ваются, код легко модифицируется, поскольку обязанности четко разграничены.
Сохраняйте равновесие • Все можно разбить на множество мелких частей» после чего воспользоваться этим будет уже трудно. Целая коробка с нит ками вряд ли заменит вам пару носков1. • Связный код способен “линейно” отслеживать изменения требований. Посмотрите, много ли вам придется менять в ва шем коде, если вдруг потребуется небольшое функциональ ное изменение?2
Такую систему можно по праву назвать “Спагетти ОО". 2 Один из наших рецензентов рассказал о системе, в которую потребова лось добавить всего одно поле формы, и этой “проблемой” занималась команда из шестнадцати человек и шести менеджеров. Это явное свидетельство крайне низкого уровня связности системы.
31 Говорите, не спрашивайте "Не доверяй чужим объектам. В конце концов, они написа ны другими людьми или даже тобой — в прошлом месяце, когда ты еще многого не умел. Получи всю необходимую информацию отдругих, а потом сам производи все вычис ления и принимай решения. Не уступай контроль кому-то ещеГ
“Процедурно-ориентированный код получает информацию и принимает решения. Объектно-ориентированный код дает зада ния объектам”. Это наблюдение стоило Алеку Шарпу (Alec Sharp) [Sha97J немалых усилий. Но оно не ограничивается объектно-ори ентированной парадигмой. Любой гибкий код должен следовать этому принципу. Как вызывающая сторона вы не должны принимать решения, анализируя состояние объектов, и затем менять их состояние. Вам нужно реализовать такую логику, которая полагается на вызов объектов и выполнение ими своих обязанностей. Если вы сами принимаете решения снаружи вызываемых объектов, вы тем са мым нарушает е их инкапсуляцию и создаете благодатную среду для размножения ошибок. Дэвид Бок (David Bock) хорошо иллюстрирует это притчей про разносчика газет и бумажник1. Мальчик, доставляющий вам газе ты, приходит к вам за недельным вознаграждением. Вы поворачи ваетесь, даете мальчику вынуть бумажник из вашего заднего карма на, взять два доллара (как вы надеетесь) и положить бумажник назад. Мальчик уезжает на своем новеньком блестящем “Ягуаре”. Мальчик-газетчик, как вызывающий объект в этой праизакции, должен просто попросить заказчика заплатить ему 2 доллара. Ни каких вопросов о финансовом состоянии клиента или состоянии его кошелька на данный момент, никаких решений со стороны мальчика. Все эго входит в обязанности заказчика, а не мальчика. Гибкий код должен работать именно так. Отделяйте команды
Полезная сопутствующая техника, близкая по сути к концепции “Говорите, не спраши вайте”, известна как отделение команд от за просов |Меу97]. Ее смысл —все функции и методы распределить по двум категориям: ком ан ды и запросы, и описывать (документировать) их в качестве таковых в самом коде
ОТ запросов
1 http://www.javaguy org/papers/demeter. pdf
(это очень удобно, если они объединены в группы команд и запро сов соответственно). Процедура, работающая как команда, с высокой вероятностью меняет состояние объекта и может для удобства также возвращать некоторую полезную информацию. Запрос информирует вас о со стоянии объекта и не влияет па его состояние, видимое снаружи. Таким образом, запросы по должны иметь побочных эффектов, с точки зрения внешнего мира (вы можете при необходимости вы полнять предварительные вычисления или кэширование “за кули сами”, но получение значения X внутри объекта не должно приво дить к изменению переменной Y). Идея проектирования методов как команд лежит в основе кон цепции “Говорите, не спрашивайте”. В качестве дополнения к пей использование запросов, не имеющих побочного влияния, может быть полезной практикой: вы можете неограниченно применять их в модульных тестах, вызывать их внутри утверждений или из от ладчика без изменения текущего состояния приложения. Явное отделение запросов от команд, помимо всего прочего, за ставляет вас подумать, зачем вы открываете определенную часть данных. Насколько это необходимо? Что запрашивающая сторона будет с ними делать? Возможно, вместо запроса нужна соответст вующая команда. Щ К
Говорите, не спрашивайте~ Не берите на себя работу других объектов или компонентов. Просто говорите им, что нужно делать, а сами выполняйте свою работу.
Опасайтесь побочных эффектов Вы слышали когда-нибудь примерно следующее: “О да мы вызываем дан ный метод, только ради его побочных эффектов”. Это походит на оправда ние наличия неуместных элементов в архитектуре: “Ну, это так принято". Аргументы такого сорта явно свидетельствуют о хрупком, а отнюдь не о гибком дизайне. Ставка на побочные эффекты или следование запутанному дизайну, дале кому от реальности, — веский довод в пользу перепроектирования и ре факторинга кода.
На что это похоже В SmallTalk, вместо вызовов методов, используется концепция пе редачи сообщений (“message passing”). Практика “Говорите, не
спрашивайте похожа па то, как будто вы посылаете сообщения, а не вызываете функции.
Сохраняйте равновесие • Объекты, которые представляют собой гигантские вмести лища данных, подозрительны. Иногда без них не обойтись, по далеко не гак часто, как вам кажется. • Команда может при необходимости возвращать данные (хорошо бы иметь возможность получать те же самые данные отдельно). • Нехорошо, если невинный запрос меняет состояние объ екта.
32 Замените, сохраняя контракт “Иерархии классов с глубоким наследованием— это здоро во. Если нужна функциональность другого класса, унасле дуй ее. И не волнуйся, если класс-наследник что-нибудь сломает; пускай те, кто вызывает твои функции, переписы вают свои коды. Это их проблемы, а не твой.
Чтобы система оставалась всегда гибкой, новый код должен заменять собой существующий код таким образом, чтобы тот не чувствовал разницы. Например, вы собираетесь добавить новый способ ш ифрования данных в инфраструктуру связи или реали зовать более совершенный алгоритм поиска при сохранении прежнего интерфейса. Если интерфейс сохраняется, можно сво бодно менять реализацию, никак не затрагивая другие коды. Тем не менее, это всегда легче сказать, чем сделать, поэтому без надле жащего руководства здесь не обойтись. Для этого обратимся к теории. Принцип Подстановки Барбары Л исков (Barbara Liskov Substi tution Principle, [Lis88]) говорит о том, что “Объектлюбого произ водного класса должен быть заменяем везде, где работает объект базового класса, и пользователь не обязан этого знать”. Другими словами, код, который вызывает методы базового класса, должен иметь возможность обращаться к объектам производных классов, не требуя модификации. Что это означает конкретно? Предположим, у вас есть про стой метод класса, который сортирует список строк и возвраща ет указатель на новый список. Можно вызвать его следующим об разом: u tils = new B asicUtils<); sortedList = u tils .s o r t(a L is t);
А теперь вы создаете подкласс для класса BasicUti Is и в нем пи шете новый метод s o r t( ), который применяет лучший, более быст рый алгоритм сортировки: u tils = new F a s te rl)tils (); sortedList = u t ils .s o r t ( a lis t ) ;
Обратим внимание на то, что вызов метода s o r t( ) остался преж ним, т.е. объект класса PasterU tiIs прекрасно заменяет объект BasicUtils. Код, в котором вызывается метод u tils . s o r t( ), может
создавать объекты u tils обоих типов, и при этом все работает хо рошо. Но если вы создали подкласс базового класса B a s ic U tils таким образом, что смысл сортировки в нем меняется (например, сорти ровка идет в обратном порядке), тогда вы серьезно нарушили Принцип Подстановки. Чтобы соблюсти Принцип Подстановки, сервисы (методы) ва шего производного класса должны требовать не больше и обещать не меньше, чем соответствующие методы базового класса; они должны быть взаимозаменяемы без ограничений. Эго важный мо мент, который необходимо учитывать при проектировании иерар хий наследования классов. Концепцией наследования довольно часто злоупотребляют в объектно-ориентированном моделировании и программирова нии. Если вы нарушили Принцип Подстановки, ваша иерархия на следования вполне может обеспечить повторное использование кода, но не обеспечивает его расширяемости. Пользователь вашей иерархии классов теперь должен, вероятно, знать, объект какого конкретного класса нужно передать, чтобы все работало правиль но. По мере того как в код добавляются новые классы, его прихо дится все время пересматривать и переделывать. Это не есть гиб кий подход. Но помощь всегда под рукой. Ваш компилятор может посодейст вовать вам в соблюдении Принципа Подстановки, по крайней мере, до некоторой степени. Например, рассмотрим модификато ры доступа к методам. ВJava модификаторы доступа к переопреде ленным методам должны быть не менее открыты (или “терпимы”, lenient), чем модификаторы переопределяемых методов. То есть если базовый метод объявлен с модификатором protected (защи щенный), то производный переопределенный метод должен быть protected или public (общедоступный). В C# и VB.NET модифика торы доступа к обоим методам —базовому и переопределенному — должны иметь одинаковый уровень защиты. Рассмотрим класс Base, у которого есть метод f in d L a r g e s t( ) для обнаружения исключения IndexOutOfRangeException. Согласно до кументации, пользователь данного класса должен обеспечивать перехват данного исключения при его возникновении. Теперь предположим, что вы создаете класс Derived, являющийся наслед ником Base, и переопределяете метод fin d L a rg e s t (), и в этом но вом методе проверяете условие возникновения другого исключе ния. Если экземпляр Derived используется кодом, который на самом деле интересуется объектом класса Base, этот код может по лучить информацию о возникновении непредвиденного исклю-
чсния. Класс Derived не является замещаемым там, где использует ся класс Base. Java предотвращает данную проблему: здесь вы не можете посылать информацию о возникновении нового исключе ния производным (переопределенным) методом, если только эго исключение само не является производным от одного из классов исключений (e x c e p tio n classes), проверяемых в базовом (переопре деляемом) методе. (Разумеется, при возникновении непроверяе мых исключений типа RuntimeException компилятор вам не помо жет). К сожалению, Java нарушает Принцип Подстановки. Например, класс j ava. u t i l . Stack является производным no отношению к клас су Java, u til .Vector. Если вы ненамеренно передаете объект Stack методу, который ожидает объект типа Vector, элементы вашего стека могут вставляться и изыматься в порядке, который не согла суется с поведением вектора. Используйте наследование в ситуациях “является им”; используйте делегирование в случаях “имеет его” И “использует его”.
Если вы прибегаете к наследованию, выясните, является ли ваш производный класс замещаемым по отношению к базовому классу. Если нет, спросите себя, зачем вы используете наследова ние. Если оно требуется для того, что бы новый класс мог использовать код базового класса, тогда лучше, вероятно, использовать композицию. Композиция (c o m p o s itio n ) — это когда объект вашего класса содержит в себе и использует объект другого класса, передавая ему полномочия (эта техника также назы вается делегированием, d e le g a tio n ). На рис. 6.3 показаны отличия делегирования от наследования. П ри наследовании, если вызывается метод methodA() класса Called
Наследование
Рис. 6.3.
Отличие делегирования (передачи полномочий) от наследования
Class, то автоматически запускается (наследуется) метод methodA() базового класса Base Class. При делегировании вызов Called Class непосредственно передает управление классу-делегагу, запуская его метод methodA(). Когда лучше применять наследование, а когда—делегирование? • Если новый класс может использоваться вместо существую щего класса и его отношение к базовому классу можно опи сать как “является им”, тогда применяйте наследование. • Если новый класс собирается использовать существую щий класс, и его отношение к нему можно выразить как “имеет его” и “использует его”, то лучше применить делеги рование. Вы могли бы возразить, что в случае делегирования нужно пи сать множество крошечных методов, которые будут направлять вызовы методов объектам, входящим в вызываемый объект. При наследовании этого не требуется, поскольку обгцедоаупные (с мо дификатором public) методы всегда доступны производному клас су автоматически. По это не слишком веский довод в пользу приме нения наследования. Вы можете написать хороший сценарий либо симпатичный IDE-макрос для облегчения вставки нескольких лишних строк в код, или же использовать более подходящие язы ки/среды разработки, которые предлагают вам более автоматизирован ную форму делегирования (Ruby, например, делает это весьма не плохо).
Расширяйте систему заменой кода . Добавляйте и расши ряйте функции посредством замены классов, сохраняя кон тракт их интерфейса. Делегирование почти во всех случаях предпочтительнее, нежели наследование.
На что это похоже Это похоже на подлость; вы можете “исподтишка” заменить компо нент в кодовой базе, так что никто этого даже не заметит, а код по сле этого будет иметь новую улучшенную функциональность.
Сохраняйте равновесие • Делегирование обычно обеспечивает большую гибкос ть, не жели наследование. • Наследование само по себе не зло, просто им злоупотреб ляют. • Если вы не очень хорошо представляете, что обещает или т ребует4интерфейс, вам будет трудно написать для него реа лизацию.
У вас могло сяолситпься впечатление, что опытные плотники никогда не делают оши бок. Уверяю вас, это неправда Просто на стоящие профессионалы знают, как спасти положение. Джефф Миллер (Jeff Miller), изготовитель мебели и писатель
Глава 7
Гибкая отладка Даже в самых выдающихся гибких проектах что-нибудь работает не так. Ошибки различного рода, дефекты, неточности, неувяз ки —как бы вы их ни назвали —все равно случаются. Главная проблема, свойственная процессу отладки, заключает ся в том, что здесь невозможно ставить временных ограничений. Вы можете ограничить время рабочего совещания и поставить себе цель принять оптимальное решение до его окончания. Но когда вы начинаете отлаживать код, может пройти час, день, неделя — и вы обнаруживаете себя все там же, где были в самом начале. Разумеется, нельзя допускать, чтобы этот этап работы растянул ся на неопределенное время. Мы можем предложить вам некото рые полезные приемы, начиная с ведения оперативного журнала решений и заканчивая эффективными средствами решения возни кающих проблем. Чтобы использовать свой багаж знаний и опыта с наибольшей отдачей, Ведите журнал решений. О том, как это лучше делать, мы расскажем на с. 145. Если компилятор предупреждает вас о чемто, вам необходимо считать, что Предупреждения фактически явл яю тся ош ибками, и относиться к ним соответственно, т. е. ис ключать их (об этом читайте на с. 148). Бывает очень трудно, если вообще возможно, отследить про блемы, когда они сидят внутри целостной системы. У вас есть го раздо больше шансов выявить их, если Атаковать проблемы в изо ляции, как мы покажем на с. 152. Если что-то не получается,
никогда не скрывайте правды, как это любит делать правительст во. О чень важно С ообщ ать обо всех исклю чительны х ситуаци ях, как показано на с. 155. Наконец, когда вы сообщили о том, что что-то работает плохо, вам необходимо позаботиться о пользова телях и О б есп ечи ть содерж ательны е сообщ ения об ошибках; см. с. 158.
33 Ведите журнал решений ‘Часто ли у тебя возникает ощущение "дежа-вю" во время разработки? Часто ли у тебя возникает ощущение “дежавю" во время разработки? Это вполне нормально. Однажды ты уже справился с подобной проблемой. Значит, спра вишься и теперь".
Вся жизнь разработчика состоит из встреч с проблемами (и по иска пул ей их решения). Когда проблема только обнаруживается, хочется решить ее как можно быстрее. Если подобная проблема возникает опять, вам хочется вспомнить, как вы решали ее в про шлый раз, и избавиться от нее еще быстрее. К сожалению, зачас тую вы видите, что ситуация явно напоминает то, что уже было раньше, но не можете вспомнить метод ее решения. Это с нами слу чается постоянно. Нельзя ли просто поискать ответ в Интернете? В конце концов, его ресурсы разрослись до невообразимых масштабов, и всегда можно воспользоваться ими. Поиск ответа в Сети может быть, без условно, намного более эффективным занятием, нежели трата вре мени на собственные изыскания. Тем не менее, этот поиск тоже от нимает немало времени. Иногда вы находите ответ, который ищете. В других случаях вы находите только огромное количество мнений и идей, вместо подлинных решений. Это доставляет неко торое удовольствие —видеть, как много других разработчиков име ли дело с подобной проблемой, но вам все-таки более важно узнать ее решение. Не наступайте дважды на ОДНИ И те же грабли
Чтобы повысить свою продуктивность, ве дитс запись возникающих проблем и путей их решения. Когда возникает проблема, вме сто того, чтобы разводить руками и гово рить “Черт, это уже было, но я не помню, как я справился в этим в прошлый раз”, вы быстро приходите к решению, найденному вами раньше. Инженеры пользуются этим уже давно, у них это называет ся оперативным журналом (daylog). Вы можете выбрать любой формат, который вас устраивает. Вот некоторые пункты, которые хорошо включать в запись: • Дата возникновения проблемной ситуации • Короткое описание проблемы или вопроса • Подробное описание решения
• Ссылки на использованные ресурсы, где данный вопрос под робно рассматривается: статьи, URL (сетевой адрес) • Любые сегменты кода, настройки и экранные снимки диало гов, которые в дальнейшем могут помочь в решении пробле мы или просто лучше разобраться в ней Храните ваш журнал в формате, который обеспечивает возмож ность автоматического поиска. Тогда вы всегда може те быстро и легко просмотреть нужную информацию, задавая ключевое слово. На рис. 7.1 показан простой пример вывода записи из оперативно го журнала, который содержит гиперссылки на более подробную информацию.
(МЛН.'ДОЗЕ. Устанзвпбна нсезя здэдня 0^*п ^2 1.6| устранившая про&лему, в результате дооаой *=лиси в кэше ннкегда не удапягнсь
1Я‘2№0ЯБ: Если используется K0ED верст й или йолн ранн». необходимо переименовать базовую директора в _kqeo5. чт-айы изС^хать конфликта с знугр^нмей «сорной оислиотеком. Рис. 7.1.
Пример с гиперссылками из оперативного журнала решений
Если вы сталкиваетесь с проблемой, для которой вы не можете найти решение в своем журнале, не забудьте обновить журнал сра зу, как только вы найдете решение. Очень просто вести журнал хорошо, но еще лучше —вести его совместно с другими разработчиками команды. Сделайте его ча стью общедоступного сетевого ресурса. Или пользуйтесь средства ми Wiki; активно поощряйте использование совместного журнала решений и его пополнение другими разработчиками. Сохраняйте записи о проблемах и их решениях. Чтобы об легчить решение проблем, подробно записывайте их и поль зуйтесь этой информацией в дальнейшем.
На что это похоже Ваш журнал решений работает как часть вашего мозга. Вы можете найти в нем информацию по конкретным вопросам, а также нащу пать с его помощью пути решения других проблем, которые имеют сходство с уже известными вам.
Сохраняйте равновесие Нужно все-таки тратить больше времени на решение про блем, а не их документирование. Старайтесь сделать эту про цедуру легкой и простой. Здесь не требуется типографского качества. Очень важно обеспечить возможность поиска по журналу ре шений; используйте достаточное количество ключевых слов, которые помогут потом быстро найти нужную запись. Если в Интернете вы не найдете никого другого с той же про блемой, что возникла у вас, возможно, вы просто некоррект но используете что-то. Отслеживайте, в какой версии приложения, среды разработ ки или на какой платформе возникла конкретная проблема. Та же самая проблема может проявиться по-иному на других платформах или версиях. Записывайте, почему команда приняла то или иное важное решение. Эти подробности обычно забываются спустя шесть или девять месяцев, когда решения приходится пересматри вать, и воздух наполняется взаимными обвинениями.
34 Предупреждения фактически являются ошибками яПредупреждения компилятора предназначены для тех, кто излишне осторожен и педантичен. В конце концов, это про сто предупреждения . Если бы они были серьезны, они бы считались ошибками и ты не смог бы скомпилировать код. Так что просто игнорируй их и не задерживайся на них".
Если в вашей программе возникает ошибка компиляции, компи лятор или компоновщик отказывается создавать выполняемый файл. У вас нет выбора — вы должны устранить ошибку, чтобы идти дальше. С предупреждениями (warnings), к сожалению, все обстоит ина че. Вы можете, если хотите, запускать программу, которая получа ет предупреждения при компиляции. Что происходит, если вы иг норируете предупреждения и продолжаете разработку кода? Вы как будто сидите на бомбе замедленного действия, которая может сработать в самый неподходящий момент. Некоторые предупреждения можно отнести к несерьезным жа лобам излишне мнительного компилятора (или интерпретатора), но далеко не все. Например, сообщение о том, что какая-то пере менная не используется в коде, можно считать несерьезным, но оно может косвенно указывать на использование кодом какой-то другой, неправильной переменной. Недавно у одного клиента Венкат видел производственное при ложение, которое получало более 300 предупреждений. Одно из них, проигнорированное разработчиками было таким: Assignment in conditional expression is always constant: did you mean to use == instead of = ? (Присваивание константе в условном выражении; возможно, вы имеете в виду == вместо = ?)
А код, который вызывал данное предупреждение, выглядел так: i f (theTextBox.Visible = tru e)
Другими словами, такой оператор i f всегда будет считать усло вие истинным, независимо от реального состояния переменной t heText Box. Ужасно видеть ошибки такого рода в списке предупреж дений, которые игнорируются. Рассмотрим следующий код на С#:
public class Base
{ public v ir tu a l void foo()
{ Console.WriteLine(4,6ase. foo")\
) ) public class Derived : Base
{ public v ir tu a l void foo()
{ Console. W riteLine(“Denved. too" );
} } class Test
{ s ta tic void M ain(string[] args)
{ Derived d = new DerivedO; Base b = d; d .fo o (); b .fo o ();
} } Когда вы компилируете этот код в Visual Studio 2003, используя настройки по умолчанию, вы получите сообщение в самом низу окна вывода (Output window): “Build: 1 succeeded, 0 failed, 0 skipped” (“Компоновка: 1 успешно, 0 неудачно, 0 пропущено”). Когда вы за пускаете программу на выполнение, ее вывод выглядит так: D erived .fоо Base.foo
Но ведь вы ожидали совсем другого! Вы надеялись, что оба вызо ва метода foo() происходят в классе Derived (Производный). Что же не так? Если вы внимательно изучите окно вывода (Output window), то увидите предупреждение: Warning. Derived.foo hides inherited member Base.foo To make the current member override that implementation, add the override keyword. Otherwise, you’ d add the new keyword. (Предупреждение. Метод Derived, foo скрывает унаследованную функцию-член Base. foo. Чтобы используемый метод мог переопределить базовую реализацию, добавьте ключевое слово override или new.)
Это определенно должно считаться ошибкой —нужно было ис пользовать o v e rrid e вместо v i r t u a l в объявлении метода Гоо() для класса D erived 1. Только представьте себе, что будет, если вы систе матически будете игнорировать предупреждения такого рода в процессе разработки. Поведение вашего кода станет непредсказуе мым, его качество стремительно упадет. Вы могли бы возразить, что хорошие модульные тесты всегда обнаружат эти проблемы. Да, они, действительно, помогут (и вы должны всегда применять хорошие модульные тесты). По если компилятор в состоянии сам обнаруживать некоторые проблемы, почему не воспользоваться этим? Эго сэкономит вам время, а так же избавит от лишней головной боли. Заставьте компилятор трактовать предупреждения как ошибки. Если ваш компилятор имеет уровни тонкой настройки выдачи от чета о предупреждениях, тяните эту ручку вверх до упора, чтобы никакие предупреждения нельзя было игнорировать. Например, компиляторы GCC поддерживают флажок -We г го г, а в Visual Studio можно настроить проект так, что предупреждения рассматривают ся компилятором как ошибки. Это самое малое, что необходимо сделать в вашем проекте. К со жалению, если вы решили последовать по этому пути, вам придет ся делать все это для каждого создаваемого проекта приложения. Было бы хорошо сделать эту настройку более или менее глобаль ной (для всех проектов). Например, в Visual Studio вы можете вносить изменения в шаб лоны проектов (более подробно см. .NET Gotchas [Sub05]), так что любой проект, создаваемый на вашем компьютере, будет иметь указанный набор опций; в современной версии Eclipse есть воз можность менять эти настройки в Window -4 Preferences —»Java —> Compiler —» Errors/W arnings. Если вы используете другие языки или IDE, потратьте время на то, чтобы заставить их рассматривать предупреждения как ошибки. Изменяя настройки компиляции, не забудьте выставить те же самые опции для непрерывной интеграции приложения, которая выполняется на машине сборки. (Более подробно о непрерывной интеграции см. Практика 21, Различия имеют значение; с. 97). Это незначительйое изменение может сильно повлиять на качество кода, который ваша команда загоняет в систему контроля исход ных кодов. Нужно обеспечить соблюдение всех вышеуказанных правил уже с самого начала работы над проектом, иначе внезапный поворот в
1 И это коварная ловушка для программистов, имеющих опыт в C++: дан ная программа работает именно так, как ожидается в C++.
отношении предупреждений где-нибудь в середине проекта может завалить вас таким объемом работы, с которым вам будет тяжело справиться. Если ваш компилятор относится к предупреждениям не слишком серьезно, это не значит, что вы должны относиться к ним так же. Считайте предупреждения ошибками. Если после правки код получает предупреждения, это так же плохо, как если бы он получал сообщения об ошибках, или не проходил тестов. Любой код после вашей правки не должен получать ни одного предупреждения со стороны средств компоновки.
На что это похоже Предупреждения похожи на... да-да, именно на предупреждения. Они предостерегают вас о чем-то, что должно привлечь ваше внимание.
Сохраняйте равновесие • Мы все это время говорили о компилируемых языках; интер претируемые языки обычно позволяют установить флажок для вывода сообщений о предупреждениях при выполнении программы (run-time warnings). Устанавливайте этот флажок и выводите всю эту информацию так, чтобы можно было от следить и исключить все предупреждения. • От некоторых предупреждений невозможно избавиться, если они есть следствие ошибок в самом компиляторе или проблем с инструментами или кодом от сторонних разработ чиков. Если это нельзя поправить, не тратьте на это время. Но это не должно случаться часто. • Обычно можно заставить компилятор подавлять неустрани мые предупреждения, чтобы вам не приходилось всякий раз выуживать из этого потока реальные предупреждения и ошибки. • Не рекомендуемые методы являются таковыми в силу веских причин. Не используйте их. Как минимум, в пределах какойлибо итерации запланируйте избавить свой код от них (и от соответствующих предупреждений компилятора). • Если вы помечаете методы, написанные вами, как не реко мендуемые к использованию, опишите, чем можно их заме нить и когда эти методы будут изъяты окончательно.
35 Атакуйте проблемы в изоляции "Прочесывать массивный код строчка за строчкой — что может быть ужаснее? Единственный способ отладить серь езную проблему — это посмотреть на всю систему цели ком. На все сразу. В конце концов, ты же не знаешь, где скрывается сама проблема, и это единственный способ об наружить ее местонахождение”.
Одним из полезных побочных результатов модульного тестиро вания (см. главу 5, Гибкая обратная связь, с. 85) служит то обстоя тельство, что вы вынуждены разделить свой код на отдельные слои, или уровни (layers). Чтобы облегчить тестирование элемента кода, вам приходится каким-то образом отделить его от окруже ния. Если ваш код зависит от других модулей, вы задействуете мокобъекты (mock objects), чтобы изолировать код от реальных объек тов из других модулей. Помимо того, что ваш код станет заметно крепче, данный подход облегчит поиск и локализацию любых воз никающих проблем. В противном случае вам будет нелегко подступиться к решению любой проблемы. Можно попробовать отлаживать код шаг за ша гом, пытаясь изолировать проблему. Вам, возможно, придется пройтись по нескольким формам или диалогам, прежде чем вы на щупаете интересный участок, поэтому добраться до местонахожде ния проблемы бывает непросто. Вы как будто вступаете в борьбу со всей системой, и это только порождает стрессы и снижает продук тивность. Большие системы обычно очень сложны —их функционирова ние зависит от множества факторов. Поэтому, когда вы работаете с целой системой, бывает трудно из общей совокупности аспектов выделить именно те, которые имеют реальное отнош ение к вашей конкретной проблеме. Вывод очевиден: не нужно пытаться работать со всей системой сразу. Отделите проблемный компонент кода или модуль от ос тальной кодовой базы и займитесь его скрупулезной отладкой. Если у вас есть модульные тесты, у вас есть все необходимое. Если нет, вам придется проявить изобретательность. Один пример. В середине проекта, который предъявлял стро гие требования к срокам выполнения (как и все проекты), Фред и Джордж вдруг столкнулись с серьезной проблемой порчи данных. Потребовалось немало сил, чтобы понять, что работало непра вильно, поскольку их команда не отделила код, относящийся к базе данных, от всего остального кода приложения. У них не было воз-
можности сообщить поставщику о данной проблеме —не могли же они послать ему всю кодовую базу по электронной почте! Так что они разработали “маленький прототип, который де монстрировал тс же симптомы. Они послали его поставщику в качестве примера, запросив у поставщика экспертную оценку. Работа с прототипом помогла им лучше понять сложившуюся си туацию. Кроме того, если бы им не удалось воспроизвести проблему в своем прототипе, эта работа привела бы к созданию образцов кода, которые реально работают, что позволило бы выделить про блему из всего окружения. Используйте прототип для изоляции
Первый шаг в разрешении любой проблемной ситуации —это изолировать дефект от всей остальной системы. Никто Fie собира ется чинить самолетный двигатель во время полета, так что не стоит проводить диагностику серьезных дефек тов, выявленных в отдельном блоке или компоненте системы, во время ее работы. Двигатели легче ремонтировать отдельно от са м ол ет, на специально оборудованном рабочем месте. То же самое и с ремонтом кода — легче проводить его при отключении про блемного модуля от всей системы. Но многие приложения написаны таким образом, что выделить из них какой-то блок довольно сложно. Компоненты приложения могут быть переплетены между собой так тесно, что если вы пытае тесь вытащить какой-то отдельный элемент, то он увлекает за со бой и все остальные1. В этом случае лучше сначала потратить неко торое время на отсечение нужной части кода и создание испытательного стенда для работы. Устранение проблемы в режиме ее изоляции имеет ряд преиму ществ: отделяя дефектный узел от всего остального приложения, вы получаете возможность сосредоточиться на аспектах, непо средственно связанных с дефектом. Вы можете вносить сколько угодно изменений, докапываясь до самого дна проблемы, и при этом не нужно опасаться за жизненно важные функции остальной системы. Вы быстрее доберетесь до источника проблем, поскольку вы имеете дело с минимальным объемом кода. Отделение проблемы не должно примеЕ1Лться после поставки продукта потребителям. Этот прием наиболее эффективен на эта пе создания прототипов, отладки и тестирования.
1 Этот антипаттерн дизайна ласково называется “Большой ком грязи” ("Big Ball of Mud”).
Атакуйте проблемы в изоляции. Отделите код, содержащий проблему, от его окружения, и работайте с ним. Особенно это важно для крупных приложений.
На что это похоже Если вы сталкиваетесь с проблемой, которую нужно отделить от остального кода, это очень походит на то, как будто вы ищете игол ку в чайной чашке, а не в стоге сена.
Сохраняйте равновесие • Если вы отделяете код от его окружения и проблема при этом исчезает, значит, вы изолировали ее. • С другой стороны, если вы отделяете код от его окружения и проблема при этом не исчезает, это все равно означает, что вы изолировали ее. • Чтобы локализовать проблему, можно прибегнуть к делению пополам. Разделите весь код, содержащий проблему, на две части и определите, в какой из них она находится. Затем эту часть разделите пополам и т.д. • Перед тем как приступать к решению проблемы, прокон сультируйтесь со своим оперативным журналом (см. Практи ка 33, Ведите ж урнал реш ений, с: 145).
36 Сообщайте о всех исключительных ситуациях "Избавь того, кто вызывает твой код, отобработки непонят ных исключений. Это твоя работа — справляться с ними Сверни все свои вызовы и вместо них создай свое собст венное исключение, а лучше просто проглоти его".
В круг обязанностей программиста входит продумывание всех деталей работы создаваемой системы. По намного полезнее бывает заранее продумать ситуации, которые возникают в том случае, если что-то 7te работает или работает не так, как заплани ровано. Допустим, вы вызываете какой-то код, который генерирует ис ключение; в вашем собственном коде вы можете обработать эту ситуацию и продолжить работу, Это здорово, когда вы можете восстановиться и продолжить работу, а пользователь при этом даже не знает о существовании проблемы. Если вы не можете вос становить работу приложения, нужно проинформировать ваше го пользователя, что конкретно вызвало нарушение работы сис темы. Но это делается далеко не всегда. У Венката есть весьма неудач ный опыт использования одной популярной библиотеки opensource (мы не будем называть ее здесь). Когда он вызвал метод, ко торый должен создавать объект, тот вернул ему ссылку n u ll. Код был небольшим, изолированным и достаточно простым, так что такой беспорядок не мог произойти на уровне самого кода. Но не было никаких указаний на то, что могло случиться. К счастью, это все-таки был open-source, так что Венкат загрузил исходный текст и изучил подозрительный метод. Он, в свою оче редь, вызывал другой метод, который определял, что некоторые важные компоненты отсутствовали на компьютере Венката. Ме тод низкого уровня генерировал исключение и передавал подроб ности о нем. К сожалению, метод более высокого уровня просто за малчивал об этом с помощью пустого блока catch и возвращал null. Код Венката не имел возможности выяснить, что произошло; толь ко чтение исходного кода библиотеки позволило ему понять про блему и установить недостающий компонент. Проверяемые исключения, например в Java, должны быть пере хвачены вашим кодом или переданы далее. К сожалению, некото рые разработчики (возможно, лишь на какое-то время) захватыва ют и игнорируют исключения, чтобы успокоить компилятор. Это
опасно: про временные заглушки иногда забывают, и они гак и ос таются в коде, который запущен в эксплуатацию. Нужно обрабаты вать все исключения и восстанавливать работу кода везде, где это возможно. Если вы сами не можете этого сделать, передайте ин формацию коду, вызывающему ваш метод, чтобы он попытался об работать исключение, не обработанное вами (или, по крайней мере, деликатно сообщите пользователям о данной проблеме; см. Практика 37, Обеспечьте содержательные сообщения об ошибках на с. 158). Все это кажется очевидным, не так ли? Но эго далеко не так оче видно, как вы могли бы подумать. Недавно в новостях писали о серьезном сбое в работе системы бронирования авиабилетов. Сис тема упала, самолеты не взлетали, тысячи пассажиров были выса жены, беспорядок во всей системе воздушного транспорта длился несколько дней. Причина? Всего лишь одно непроверяемое SQL-исключе ние на сервере приложений.
Возможно, вам будет приятно, если о вас расскажет CNN, но только не в данном контексте.
ttfiv
Обрабатывайте или передавайте дальше все исключения.
Никогда не замалчивайте о них, даже временно. Пишите код так, чтобы он был готов к любому отказу.
На что это похоже Вы можете положиться на перехват исключений, если случается что-то нехорошее. У вас нет пустых обработчиков исключений.
Сохраняйте равновесие • Определение того, кто отвечает за управление исключения ми, является составной частью проектирования системы. • Не все ситуации являются исключительными. • Сообщайте о возникшем исключении, которое имеет смысл в контексте данного кода. Исключение типа NullPointerException звучит неплохо, но абсолютно бесполезно, подобно ссылке на объект null, описанной выше. • Если код во время выполнения записывает отладочную ин формацию, сделайте так, чтобы он записывал данные о пере-
хваченных или генерируемых исключениях; это существен но облегчае т контроль. Работа с проверяемыми исключениями бывает обремени тельна. Никто не хочет вызывать метод, который передает порядка тридцати различных проверяемых исключений. Это ошибка проектирования; исправьте ее, а не ставьте заплатки поверх нее. То, что вы не можете обработать сами, передавайте дальше.
37 Обеспечьте содержательные сообщения об ошибках “Не запугивай пользователей или даже других программи стов. Давай им чистые и опрятные сообщения об ошибках. Что-нибудь успокаивающее, типа "Ошибка Пользователя. Замените и Продолжайте1,,\
Поскольку приложения развертываются и используются в ре альном мире, время от времени что-нибудь отказывается работать. Например, может что-то случиться с вычислительным модулем или нарушиться связь с сервером базы данных. Когда вы не можете удовлетворить желание пользователя, нужно найти наиболее изящный выход из создавшегося положения. Когда возникает ошибка, будет ли достаточно пользователю увидеть вежливое сообщение с извинениями? Конечно, стандарт ное сообщение, информирующее пользователя об аварии, выгля дит куда лучше, нежели странное поведение или полное исчезнове ние приложения (которое оставляет пользователя в смущении и недоумении, что же все-таки произошло). Тем не менее, аккуратмое сообщение типа “что-то случилось не так” не поможет вашей команде диагностировать проблему. Когда пользователи звонят в вашу группу поддержки, сообщая о возникшей проблеме, вам хо чется услышать от них побольше полезной информации, которая позволит вам быстро понять ситуацию. Если у них возникает лишь общее сообщение об ошибке, они могуг вам рассказать совсем не много. Наиболее общепринятое решение данной проблемы состоит в ведении журнала событий (logging): если возникают неполадки, приложение записывает подробности в специальное место. В про стейшем случае это обычный текстовый файл. Но вы можете так же использовать для этого общесистемный журнал событий. Все гда можно воспользоваться средствами просмотра журналов, создавать RSS-ленту всех записей в журнале и т. д. Хотя ведение журнала записей прииосит эффект, этого все рав но недостаточно: он может дать информацию вам, разработчику приложения, но не может помочь несчастному пользователю. Если вы покажете ему что-нибудь в духе сообщения, показанного на рис 7.2, он окажется в глупом положении: он не знает, что он сделал неправильно, что делать с этим сообщением и о чем гово рить по телефону с инженером группы поддержки.
NttiVC
venkal
u sel
)
P AJSW'fj
Н е в о з м о ж н о вы -липм нь япврацжю
Dcго Рис. 7.2.
Сообщение об исключительной ситуации, которое не принесет пользы
Е с л и внимательно относиться к этому вопросу, то можно заме тить множество предупреждающих сигналов в течение всей разра ботки. Как разрабо тчику вам часто приходится играть роль пользо вателя во время испытаний новых функций приложения. Если сообщения об ошибках трудны для понимания или не слишком по могают локализовать проблемы, представьте, как трудно будет пользователям приложения и вашей группе поддержки. Допустим, интерфейс пользователя (UI) при входе в систему вызывает функции, располагающиеся на среднем уровне вашего приложения, который, в свою очередь, активизирует уровень управления базой данных. Уровень базы данных не может соеди ниться с базой данных, это исключение передастся наверх. Сред ний уровень сворачивает это исключение и отправляет его наверх уже в другой форме. Что должен делать уровень UI? По крайней мере, он должен проинформировать пользователя, что возникла системная ошибка, никак не связанная с пользовательским вводом. И так, пользователь звонит вам и жалуется на то, что не может войти в систему. Каким образом вы можете идентифицировать возникшую проблему? Журнал записи может содержать сотни со общений, и отыскать среди них наиболее существенные в данном случае довольно сложно. Вместо этого дайте больше деталей в самом сообщении, предна значенном для пользователя, например, какой именно SQL-запрос или процедура записи дала сбой. Это позволит вам искать пробле му более целенаправленно, а не тратить время на ее поиски всле пую. С другой стороны, слишком подробные сведения о причинах нарушения связи с базой данных не нужны пользователю приложе ния, которое уже запущено в эксплуатацию. Такая информация мо жет не на шутку испугать некоторых пользователей.
•
5 D ocum ent
Rle
Ldt
Ассде* System
Vfcflw
N am e
М о /Ш а FfrefoK
ficofrpwfci 1 l& W V l
P a s s w o rd
Вхад и
Рис. 7.3.
ннв1>1можен [гшдиобнии...;
Сообщение об исключительной ситуации, содержащее ссылку на более подробные сведения
С одной стороны, нужно предоставить пользователю ясное и доступное объяснение, где произошел сбой, так чтобы он понял, в чем проблема и, возможно, попытался найти обходные пути. С дру гой стороны, вам хочется иметь и всю низкоуровневую информа цию о возникшей ошибке, практические подробности, которые позволят вам идентифицировать источник проблемы и испра вить код. Вот один из способов решения этих абсолютно разных задач. На рис 7.3 показано сообщение высокого уровня, которое инф ор мирует об ошибке. Это сообщение об ошибке уже не является про стым текстом, а содержит гиперссылку. Пользователь, разработ чики, тестеры могут пойти по ней дальше, чтобы получить более подробную информацию (см. рис 7.4). Когда вы идете по ссылке, вы получаете подробную информа цию об исключении (и всех вложенных исключениях). В процессе разработки вам удобно сразу показывать всю эту информацию. Но когда приложение готовится к выпуску, вам наверняка захочется скрыть лишние подробности от пользователей и организовать для них ссылку, либо доступ к журналу событий. Ваша группа поддерж ки может попросить пользователя нажать на сообщение об ошибке и прочитать ссылку, которая напрямую покажет интересующую их запись в журнале событий. Если же приложение запущено на авто номной системе, нажатие на ссылку может использоваться для по сылки e-mail-сообщения вашей группе поддержки, в котором будут содержаться все необходимые сведения об ошибке. Информация, которая записывается в журнал событий, может содержать не только сведения о неполадках, но и моментальный
*■У D ocum ent Access System * MozKla Ftrefox 5*?
Vfcw
Ыдгл?
фр
tank
f- |
Heip
| Lcg
P a ssw o rd
Вайд u ш ш ы у невозможен [подробные. Ошибка. Ошибка првдещк паром a! LoginSe:vic^s.LogirKValii3iftHUsyrtString userNaim. Sl/ing password) hi Сл Jl*}ginSmic«^Login.cS;lme 33 a! Pa9fi2.LoififtBLl1on.ClicA(Ol)jec1 sandur EvemArgstH Вложанжге исключении Ошибка гюдкпючкнин к !ше данных
Оего Рис. 7.4.
Отображение полной информации для отладки
снимок состояния системы (например, текущее состояние пользо вательского сеанса в web-приложении)1. Используя эти сведения, ваша группа поддержки может воссоз дать ситуацию, которая вызвала проблему, что заметно облегчает работу по обнаружению и устранению неполадок. Сообщения об ошибках существенно влияют на продуктивность разработчика, а также на конечные затраты на техподдержку. Если в процессе разработки поиск и устранение проблем вызывают затруднения, значит, необходимо повысить информативность со общений об ошибках. Отладочная информация имеет слишком высокую ценность, чтобы пройти мимо нее. Никогда не пренебре гайте ею. Сообщения об ошибках должны быть полезны. Найдите наиболее простой механизм доступа к подробным данным об ошибках. Предоставьте максимально полную информацию о проблеме для облегчения техподдержки, но не акцентируйте внимание пользователя на деталях.
1 Некоторую секретную информацию запрещено отображать или просто даже записывать в журнал: это пароли, номера счетов и тл.
Типы ошибок Программные дефек ты. Это подлинные ошибки типа NullPointerException. пропущенные ключевые значения и т.д. Это не имеет отношения ни к пользователю, ни к системному администратору. Проблемы условий эксплуатации. К этой категории относятся неудачные попытки соединения с базой данных или с удаленным муеЬ^сервисом, нехватка места на диске, недостаточные права доступа и тому по добные неполадки. Здесь программист не может помочь, но пользо ватель может найти способ исправить ситуацию, а системный адми нистратор определенно исправит ее, если вы дадите ему достаточно подробные инструкции. Ошибка пользователя. Нет нужды беспокоить программиста или систем ного администратора, просто пользователю нужно повторить попыт ку, после того как вы ему объясните, что он сделал неправильно. Если вы постоянно следите за тем, к какому из этих типов относится ошиб ка, о которой вы сообщаете, вы сможете давать своей аудитории наиболее подходящие советы.
На что это похоже Сообщения об ошибках приносят реальную пользу и облегчают жизнь. Когда возникает проблема, вы можете ее устранить, по скольку точно знаете, что не сработало и где.
Сохраняйте равновесие • Сообщение об ошибке, которое говорит “Файл не найден”, само по себе бесполезно. Гораздо информативнее выглядит “Не могу открыть для чтения файл /andy/project/m ain. yaml". • Не обязательно ждать исключительной ситуации, чтобы оп ределить, верно ли работает код. Используйте проверку ут верждений в контрольных точках кода, чтобы убедиться, что все работает правильно. Если утверждение ложно, преду смотрите столь же детальный отчет, как и при возникнове нии исключения. • Предоставление подробной информации не должно нано сить ущерб безопасности, разглашать личные данные, корпо ративные интересы и любые другие секретные сведения (это особенно касается сетевых приложений). • Информация для пользователя может содержать некоторый ключ, который поможет вам быстро найти нужный раздел в файле записи событий или в контрольном журнале.
Я использую не только свои мозги, но и занимаю у других.
Вудро Вилсон (Woodrow Wilson), президент США
Глава 8
Гибкое сотрудничество Любой нетривиальный проект требует участия команды разработ чиков. Времена, когда один человек мог сидеть в своем гараже и создавать программный продукт от начала до конца, давно ушли в прошлое. По работа в команде сильно отличается от работы в оди ночку: все ваши действия вдруг начинают влиять на продуктив ность и результаты работы других членов команды, а также на ход выполнения всего проекта. Успех проекта зависит от того, насколько эффективно члены одной команды работают вместе, как они взаимодействуют между собой, как справляются со своими обязанностями. Деятельность каждого члена команды должна органично вписываться в контекст .выполнения всего проекта, и, в свою очередь, действия любого из членов команды могут серьезно влиять на этот контекст. Эффективное сотрудничество служит краеугольным камнем гибкой разработки, применение этих практик обеспечит активное включение каждого члена команды в работу и его движение в пра вильном направлении —вместе со всей командой. Первый шаг на этом пути —Наладить регулярное очное обще ние; см. с. 165. Тесное общение “лицом к лицу” остается самой эф фективной формой взаимодействия людей между собой, так что мы начнем именно с него. Далее вы хотите, чтобы никто не остал ся не у дел. Это прежде всего означает, что Архитекторы должны писать код; мы поговорим об этом на с. 169. Поскольку вы и ваша команда находитесь в одной лодке, вам приходится П рактиковать коллективную собственность; с. 172. Эта практика не позволит вам стать заложником какого-либо одного разработчика. Всегда нужно помнить, что ваше приложение —это общее достояние, ре зультат совместных усилий.
Эффективное сотрудничество имеет своей целью неч то боль шее, чем создание общего продукта. Каждый член команды заинте ресован в том, чтобы неуклонно повышать свой профессиональ ный уровень и продвигаться по службе. Даже если вы только начинаете работать, вы можете Б ы ть наставником, и мы увидим это на с. 174. Часто бывает гак, что вы знаете правильный ответ там, где ваш коллега может не знать. Вы можете помочь росту ва шей команды, если вы П озволите другим найти ответ, а как —мы покажем на с. 177. И, наконец, поскольку вы работаете вместе в одной команде, не обходимо скорректировать некоторые привычные для вас прие мы создания программ таким образом, чтобы приспособиться к стилю работы остальной команды. Для начинающих программи стов (и не только) вполне нормально Р егистрировать лиш ь гото вы й код; с. 179, чтобы не нагружать своих коллег полусырым, недо деланным кодом. Когда вы готовы, вам хочется П ровести ревизию кода вместе с другими членами команды; это мы обсудим на с. 182. По мере продвижения работы над проектом вы заканчи ваете одни задачи и беретесь за другие; вам необходимо И н ф орм и ровать других о своих успехах, о проблемах, обнаруженных вами в процессе работы, а также изящных решениях, примененных к ним. Эта практика завершает главу и рассматривается на с. 185.
38 Наладьте регулярное очное общение
\ Л.
•х
нНужно проводить много собраний — как можно больше. Мы планируем провести еще множество собраний, пока мы не определим причину отсутствия продвижения в работеf.
Вы лично можете ненавидеть всякого рода собрания, но обще ние играег ключевую роль в успехе любого проекта. Вам не только нужно время от времени разговаривать с клиентами, но и постоянно взаимодействовать со своими коллегами. Вам интересно знать, чем они заняты в данный момент; если ваш коллега Берни знает ре шение для вашей проблемы, вам хочется узнать о нем скорее рано, нежели поздно, не так ли? СборЫ'Липейки (stand-up meetings, введенные методологией Scrum и получившие наибольшее развитие в ХР) являются эффек тивным способом собрать всю команду вместе и держать всех в кур се текущих дел. Как следует уже из самого названия, все участники во время такого сбора не сидят, а стоят. Это делает подобные меро приятия короткими; когда вы сидите, вам слишком комфортно, и в результате собрания могут продолжаться бесконечно. Как-то Энди и Дэйву Томасу довелось участвовать в ежедневных сборах-линейках одного заказчика удаленно по громкой связи. Все шло очень хорошо, пока однажды собрание не затянулось вдвое дольше обычного. Как вы, наверное, догадались, что они перешли в конференц-зал и сели в удобные кресла. Сидячие собрания обычно длятся дольше; большинство людей не любят длинных дискуссий в стоячем положении. Чтобы собрание сохраняло свою нацеленность, каждый должен ограничить свое выступление ответом на следующие вопросы: • Чего я добился вчера? • Что я планирую сделать сегодня? • Каков дальнейший план работы? Каждому участнику дается на выступление короткое время (по рядка двух минут). Можно даже обзавестись 'таймером —специаль но для тех, кто имеет обыкновение говорить много. Если хочется обсудить что-то более подробно, можно собраться со всеми заинте ресованными людьми после линейки (вполне уместно во время своего выступления сказать “Мне нужно поговорить с Фредом и Вильмой о базе данных”, не вдаваясь в подробности). Обычно сборы-линейки проводятся в первой половине дня, когда все находятся на работе. Не планируйте это мероприятие с самого раннего утра: нужно дать людям возможность пробиться
сквозь пробки по пути на работу, выпить кофе, удалить послед ний спам и другие непристойные предложения. Нужно успевать заканчивать собрания тогда, когда до обеда еще остается доста точно много времени, чтобы успеть сделать некий объем рабо ты, но не настолько рано, чтобы лишать людей возможности выпить кофе и прийти в себя. Полчаса-час после номинального начала ра бочего дня —вполне разумное время сбора. Те, кто посещает сбор, должны следовать определенным прави лам, чтобы не сойти с колеи и сохранять назначение этого меро приятия: только члены команды — разработчики, собственник продукта и координатор —могут говорить (см. описание ролевых различий между “свиньями” и “курами” на врезке). Они должны от ветить на три вопроса и не должны затевать длинных дискуссий (но могут назначить отдельный разговор на другое время). Менед жер коротко записывает перечень вопросов, требующих решения, он не должен никоим образом влиять на ход обсуждения, а только следить, чтобы выступающие не отвлекались от трех основных пунктов выступления.
Свиньи и куры Согласно методологии Scrum, между настоящими членами команды и все ми остальными участниками проекта существуют ролевые отличия, как ме жду свиньями и курами. Члены команды горделиво именуются свиньями, все остальные (менеджеры, поддержка, ОТК, и т.д.) — курами. Аллегории заимствованы из басни про скотный двор, где животные решили открыть ресторан. Когда на завтрак подается яичница с беконом, куры вовлечены, а свиньи активно участвуют. Только свиньям и разрешается участвовать в сборе-линейке Scrum.
Сборы-линейки приносят много пользы: • Они задают начало рабочего дня, нацеливают всех на выпол нение конкретных задач. • Если у кого-либо из разработчиков есть проблемы, здесь они могут открыто сказать о них своим коллегам и попросить у них помощи. • Они помогают выявить области, требующие дополнитель ных трудовых ресурсов, и позволяют руководителям коман ды или менеджерам привлечь новых сотрудников или пере распределить имеющиеся ресурсы. • Они позволяют команде быть в курсе всего, что происходит в других областях работы над проектом.
• Они помогают понять, в каких направлениях имеются из лишки рабочей силы или где можно применить уже извест ные кому-то другому решения. • Они ускоряют процесс разработки, облегчая обмен идеями и кодом. • Они стимулируют движение вперед: каждый из нас, видя ус пехи других, стремится быть не хуже. Проведение сборов-линеек требует от руководителей последо вательности и активного участия. Тем не менее, запуск данной инициативы могут осуществить и ведущие разработчики команды. Если разработчики не могут привлечь руководство к участию, они могут проводить деловую пятиминутку сами, неофициально.
Проводите ежедневную деловую пятиминутку. Она подтя
гивает всю команду, должна быть короткой, интенсивной и иметь ясную цель.
На что это похоже Вы всегда ждете деловую пяти минутку с нетерпением. Вы хорошо представляете, над чем работают ваши коллеги, и можете легко по делиться с ними своими трудностями.
Используйте кухонный таймер Разработчик Нэнси Дэйвис рассказывает о своем опыте применения кухон ного таймера во время деловой пятиминутки своей команды. "Мы использовали кухонный таймер, который мне подарила моя сестра на прошлое Рождество. Он не тикал во время работы, а только звонил в са мом конце. Если кому-то времени не хватало, мы добавляли ему минуты две и переходили к следующему выступлению. Иногда мы забывали о тай мере и говорили столько, сколько считали нужным, но обычно мы строго придерживались регламента”.
Сохраняйте равновесие • Собрания отнимают драгоценное время от процесса разра ботки, поэтому нужно обеспечить максимальную отдачу от них. Деловая пятиминутка не должна отнимать более полу часа, десяти-пятнадцати минут должно вполне хватить.
Если вы проводите пятиминутку в конференц-зале, который необходимо резервировать заранее, закажите его на целый час. Это позволит вам проводить прямо гам небольшие дис куссии по окончании пятнадцатиминутной линейки. Большинству команд нужно встречаться ежедневно, но для небольших групп разработчиков это может оказаться черес чур много. Им достаточно встречаться через день или дваж ды в неделю. Следите за уровнем детализации отчетов. На линейке каж дый выступающий должен сообщать о реальном продвиже нии в работе, не вдаваясь в низкоуровневые детали. Напри мер, сказать “Я работаю над экраном входа в систему” недостаточно. ‘‘При входе в систему запрашивается имя поль зователя и пароль гостя, а завтра я буду подключать все к базе данных”, —это уже более подходящий уровень детализации. Чтобы собрание было, действительно, коротким, нужно на чинать его без промедления. Не тратьте время на раскачку. Если пятиминутка кажется вам напрасной тратой времени, возможно, причина в том, что вы работаете не как единая ко манда. Это не обязательно плохо, если вы только отдаете себе в этом отчет.
39 Архитекторы должны писать код "Н аш опытный архитектор Ф р е д спроектирует код, кото рый ты потом нап и ш еш ь . О н настоящий знаток своего д е л а , и его врем я обходится дорого, так что не отвлекай его свои м и глупы ми вопросам и или проблем ами р еал и зации".
В нашей отрасли сейчас многие люди пазывают себя архитектором ПО, Этот титул нам В PowerPoint не очень нравится и вот почему: архитек тор — это тот, проектирует и направляет рабопу, а больш инство людей, на визитной карточке которых кра суется “А рхитектор”, этого звания явно не заслуживают. Архитектор —это не тот, кто рисует красивые картинки, мастерски владеет профессиональным жаргоном и оперирует многочисленными шаблонами дизайна; от такого рода дизайнеров обычно очень мало толку. Как правило, эти люди возникают в самом начале работы над проектом, рисуют всевозможные диаграммы и исчезают еще до на чала серьезной программной реализации, “Архитекторов в PowerPoint” сегодня развелось довольно много, но они неэффек тивны по причине отсутствия обратной связи. Любой дизайн разрабатывается в соответствии с текущим пони манием задачи, а это понимание меняется по мере реализации дизайна в коде. Очень трудно заранее создать эффективный и дос таточно подробный дизайн (см. Практика 11, Дизайн должен на правлять рабо ту, а не диктовать; с. 52): контекст еще недостаточно проработан, обратная связь недостаточна или полностью отсутст вует, Дизайн со временем эволюционирует, вы не можете спроек тировать новую функцию или расширение системы без учета спе цифики фактического применения дизайна (его реализации в виде кода). Как конструк тор кода вы не можете быть сколько-нибудь эффек тивны, если не знаете будущую систему в мельчайших подробно стях. Когда вы работаете только с диаграммами высокого уровня, такая степень понимания вам попросту недоступна. Это все равно, что пытаться командовать сражением, сидя на большом расстоянии от него и глядя на карту; как только битва на чалась, планы должны корректироваться. Стратегические реше ния могут приниматься на большом расстоянии, а тактические ре~ Н евозможно Программировать
шспия, от которых фактически зависят победа или поражение, достоверного знания ситуации на иоле боя'.
Обратимость В книге “Программист-прагматик: Путь от подмастерья к мастеру ([НТОО]; изд-во "Лори", 2007) показано, что окончательных решений не существует. Ни одно решение, принятое вами, не должно бьпгь высечено из камня. От носитесь к любому вашему решению так, как будто это замок из песка на пляже и его придется, возможно, заменить впоследствии.
Designer of a new system (Дизайнер новой системы) Дональд Кнут (Donald Knuth) Дизайнер системы должен быть участником ее реализации.
По мнению Кнута, хороший дизайнер кода не боится засучить рукава и вымазаться в грязи; он без колебаний принимается за ко дирование. Настоящий архитектор будет протестовать, если его не сделают участии ком разработки. Тамильская пословица гласит: “Овощ на картинке не заменит хорошей приправы” (“A picture of vegetable doesnt make good curry”). Аналогично, дизайн на бумаге не создаст хорошего прило жения. Дизайн должен быть опробован на прототипах, экспери ментально проверен и обоснован, и он должен эволюционировать. Дизайнер (или архитектор) обязан предложить и реализовать дей ствительно работоспособный дизайн. Мартин Фаулер, в своей статье “Who Needs an Architect?” (“Кому нужен архитектор?”) пишет, что важнейшая роль настоящего ар хитектора заключается в том, чтобы “...наставлять команду разра ботчиков, повышать их уровень так, чтобы они учились решать все более сложные задачи”. Далее он пишет: “Я думаю, что среди важ нейших задач архитектора — отказ от архитектуры вообще, что становится возможным при исключении необратимости дизайна программного продукта”. Обратимость дизайна является ключе вым элементом прагматического подхода в программировании (см. врезку выше). Привлекайте ваших программистов к созданию дизайна систе мы. Ведущий программист может исполнять роль архитектора; на 1 Во время первой мировой войны битва на Сомме должна была стать решающим прорывом. Вместо этого, она превратилась в величайшую военную глупость двадцатого столетия, главным образом, из-за потери связи и в резуль тате ошибочных действий командования, которое настаивало на реализации своего плана, невзирая на реальные условия сражения. Подробности читайте на http://www.worldwar1.coni/Sfsomine. htm
самом деле, on может игран» несколько ролей. Он будет погружен в вопросы проектирования, но не в ущерб основному своему заня тию —программированию. Если разработчики неохотно принима ют на себя обязанности дизайнеров, дайте им в пару кого-либо, кто умеет это делать хорошо. Программист, который отказывается проектировать, —э го человек, отказывающийся думать. Хороший дизайн исходит от действующих программистов.
Настоящее понимание приходит в процессе живой работы над кодом. Не пользуйтесь услугами архитекторов, которые сами не пишут программ; они не могут спроектировать систему, по скольку на деле ничего не знают о ней.
На что это похоже Архитектура, дизайн, кодирование и тестирование —это различ ные аспекты одной и той же деятельности, а именно разработки программного продукта. Они не должны восприниматься как от дельные виды дея тельности.
Сохраняйте равновесие • Если у вас есть должность “старший архитектор”, он может не иметь достаточного времени на то, чтобы быть еще и пол ноценным разработчиком. В этом случае пусть он принимает участие в работе над кодом, но только не на самом ответст венном и не самом крупном участке. • Не позволяйте никому проект ировать код в изоляции, осо бенно себе.
40 Практикуйте коллективную собственность "Не беспокойся из-за этой ошибки: Джо на следующей не деле вернется из отпуска и исправит ее. До этого времени не обращай на нее внимания". Любое нетривиальное приложение требует объединенных уси лий команды разработчиков. В этом смысле нет никаких основа ний для установки территориальных ограничений виу-фи самого кода. Любой член команды, понимающий какой-либо участок кода, должен иметь возможность работать с ним напрямую. Если часть кода все время находится в руках одиого-единственного разрабо т чика, вы создаете опасную ситуацию. Решение'текущих проблем и движение навстречу ожиданиям пользователей приложения —все это гораздо более существенно, нежели выяснение, чья идея лучше или, коли на то пошло, чья реа лизация дурно пахнет. Когда над кодом работает много людей, код постоянно подвер гается проверке, рефакторингу, он поддерживается в рабочем со стоянии. Если нужно исправить ошибку, любой из разработчиков может выполнить эту задачу. Составление графика работы над проектом заметно облегчае тся, если несколько человек могут спо койно работать с различными частями кода. Общий уровень знаний и опыта команды неуклонно возрастает, если вы чередуете задания для каждого сотрудника, позволяя ему поочередно работать с различными частями приложения. Когда Джо принимается за код, написанный Салли, он может применить к нему рефакторинг, сгладив многие шероховатости. Пытаясь ра зобраться в коде, он будет задавать разные полезные вопросы, ко торы е помогут быстро вникнуть в суть проблем. С другой стороны, если вы знаете, что с вашим кодом будут рабо тать другие люди, вы будете более дисциплинированны на своем участке работы. Вы постараетесь действовать более осмотритель но, зная, что за вами наблюдают. Можно возразить, что, когда разработчик все время занят рабо той исключительно в одной области, он становится настоящим знатоком в этой сфере и в результате работает намного быстрее. Это верно, но в более отдаленной перспективе процесс разработки оказывается эффективнее, если множество глаз просматрива ют один и тот же код. Это значительно улучшает общее качество кода, он становится яснее и проще в обслуживании, а количество ошибок снижается.
у
Делайте акцент на принципе коллективной собственности.
Перебрасы вайте разработчиков с одного модуля на другой, с одной за д а ч и на другую меж ду различными частями разраба тываемой системы.
На что это похоже Вы спокойно работаете практически с любой частью системы, соз даваемой в рамках текущего проекта.
Сохраняйте равновесие • Не допускайте случайного снижения квалификации ваших коллег. Если кто-то из разработчиков является хорошим спе циалистом в своей области, лучше закрепить за ним этот уча сток, но все равно время от времени перебрасывать его на другие участки работы над системой. • В крупномасштабном проекте, если каждый будет случайным образом менять в коде все, что ему вздумается, сис тема может потонуть в хаосе. Коллективная собственность не дает нрава на дикое хакерство. • Вам не нужно знать каждую часть системы в деталях, но ника кая част ь сист емы не должна вас отпугивать. • Бывают ситуации, когда лучше не применять принцип кол лективной собственност и. Иногда код требует узкоспециаль ных знаний в какой-либо отрасли, например, жесткая систе ма управления в реальном масштабе времени. В подобных случаях у семи нянек дитя без глазу. • Все люди время от времени бросаются бежать впереди паро воза или внезапно становятся одержимы какой-либо иной на пастью, вроде соперничества между собой. Если вы не обме ниваетесь знаниями внутри команда, то вы рискуете, в конечном счете, вообще их потерять.
41
Будьте наставником
“Чтобы достигнуть того высокого уровня, на котором ты сейч ас находиш ься, тебе приш лось долго и много работать. Д е р ж и все свои знания при себе, чтобы производить впе чатление на окружаю щ их. Пользуйся своим преимущест вом, тогда твои коллеги будут испытывать благоговейный страх перед тобой".
Может наступить такой момент, когда вы вдруг понимаете, что в некоторых вещах вы разбираетесь лучше, чем другие члены вашей команды. Как можно использовать подобное преимущество? Ко нечно, можно критиковать других, смеяться над решениями, кото рые они принимают, и над кодом, который они пишут, —мы встре чали таких “экспертов”. Но есть и более достойная позиция: вы делитесь своим знанием с окружающими, повышая их уровень. Передавая знание,
“И неважно, сколько людей восприняли идею, она ОТ этого не пострадает. Если пони маю вашу идею, я обогащаюсь знанием, но вы при этом не становитесь беднее. Это про исходит так же, как если вы зажигаете от своей свечи мою. Я полу чаю от вас свет, но ваш собственный свет при этом не меркнет. Идеи подобны огню, они могут окружить весь земной шар, и при этом их интенсивность не ослабеет1”. Работа в команде приносит колоссальный опыт в плане обуче ния. Знание вообще имеет уникальные качества: например, если даете кому-то деньги, у вас остается их меньше, а у того их стано вится больше. Если же вы кого-то учите, ты вы оба приумножаете свои знания. Когда вы объясняете кому-то то, что знаете сами, вы сами начи наете лучше понимать то, что знали. Кроме того, когда вам задают вопросы, вы начинаете смотреть на предмет с различных точек зрения. Мы можете обнаружить новые аспекты и неожиданно ска зать: “А ведь я никогда прежде не смотрел на этот предмет под та ким углом зрения”. Если вы “возитесь” с остальными, вы побуждаете их к самосо вершенствованию, а это повышает общий профессиональный уро вень вашей команды. Если вы не можете ответить на какие-то во просы, они указывают вам на те области, где вы пока еще не так сильны; значит, вам нужно сосредоточить на них свое внимание, ВЫ приумножаете его
1 Томас Джефферсон (Thomas Jefferson).
чтобы расти дальше. Хороший наставник ведет записи, когда учит других. Вы время от времени останавливаетесь, чтобы записать вещи, которые вам хочется обдумать потом более детально. До бавьте эти записи в свой оперативный журнал (см. Практика 33, Ведите запись решений; с. 145). Быть наставником —не означает вести кого-то за руку все время и кормить его с ложечки (см. Практика 42, Позвольте другим най ти ответ; с. 177). Это не значит, что вы должны читать лекции или устраивать опросы. Вы можеге регулярно выступать на brown-bag совещаниях (обедах со своей едой), но главная забота наставни ка —помогать другим игрокам команды играть лучше и совершен ствовать свою собственную игру. Не обязательно ограничиваться пределами собственной коман ды. Заведите собственный блог и выложите на нем какой-нибудь фрагмент кода или обсудите технологию. Материал не должен быть большим, даже небольшие фрагменты кода с объяснениями могут быть полезны для кого-то. Быть наставником — значит делиться знаниями, а не накапли вать их. Это значит заботиться о том, чтобы другие учились и со вершенствовали свои навыки, привнося растущую ценность в свою команду. Не разрушение, а формирование окружения и себя самого —вот подлинный смысл наставничества. К сожалению, человеческой натуре, похоже, свойственно ка рабкаться вверх по лестнице и потом с пренебрежением взирать с высоты на тех, кто остался внизу. Барьеры в общении людей между собой создать очень легко, они возникают помимо нашей воли. Ваши коллеги могут начать бояться вас или стесняться подойти к вам с вопросом. Тогда обмен знаниями становится невозможен. Быть экспертом в этом случае —это все равно что иметь богатство и не пользоваться им по состоянию здоровья. Лучше стать учите лем, а не мучителем. Будьте наставником. Это подлинное наслаждение —делить
ся тем, что вы знаете. Отдавая, вы получаете сами. Вы побуж даете других к достижению лучших результатов. Вы повышае те общий уровень компетенции своей команды. .
На что это похоже Вы чувствуете, что обучение других—это способ научиться самому, а другие начинают верить, что вы можете оказать им помощь.
Сохраняйте равновесие • Если вам приходится преподавать различным людям один и тог же предмет, ведите записи, чтобы написать статыо или книгу по данному предмету. • Выть наставником —это замечательный способ повышения уровня своей команды (см. Практика 6, Повышайте уровень своей команды; с. 34). • Программирование парами (см. Практика 44, Проводите ре визию; с. 182) — естественная и эффективная форма обу чения. • Если вас постоянно отвлекают от работы те, кто просто из лени не хочет думать и искать ответ самостоятельно, см. Практика 42, Позвольте другим найти ответ; с. 177. • Установите временной интервал, в пределах которого каж дый должен думать о решении проблемы самостоятельно, не прибегая к вашей помощи. Один час можно считать приемле мым масштабом времени для самостоятельных усилий.
42 Позвольте другим найти ответ
ч-\ •х
"Ты такой умный; просто сообщ ай всем остальным свои изящ ные реш ения. Н е трать время на то, чтобы обучить других этому искусств/ .
“Дай человек)' рыбу —и он будет сыт весь день. Научи его ловить рыбу —и ом будет сыт всю жизнь”. И не будет беспокоить вас, по крайней мере, еще несколько недель. Хороший наставник учит своих коллег ловить рыбу, а не подбрасывает им по рыбке каждый день. После прочтения раздела “Будьте наставником" вам, вероятно, захоче тся быстро ответить на вопрос вашего коллеги и вернуться к своим задачам. А что если вы дади те ему только подсказку, гак что* бы он сам мог додуматься до ответа на вопрос? Это совсем не трудно. Вместо того чтобы дать простой ответ типа “42”, лучше спросите у коллеги: ”Вы смотрели, как взаимодей ствуют между собой администратор транзакций и обработчик блок и р о в ки 11р ил ож е н ия ?" Этот подход имеет следу ющие преимущества: • Вы помогаете вашему коллеге самому подойти к решению проблемы. • Он узнает больше, чем просто ответ на свой вопрос. • Он не будет приходить к вам с подобными вопросами вновь и вновь. • Вы помогае те ему самостоятельно рабо тать в те моменты, ко гда вы отсутствуете и не имеете возможность отвечать на во просы. • Он может вернуться к вам с решением или идеями, которые вы не рассматривали. Это приятный результат —вы гоже уз наете что-то новое для себя. Если же человек возвращается к вам с пустыми руками, вы все гда .можете дать ему дополнительные наводки (или сам ответ). Если он возвращается, имея несколько идей, вы можете помочь ему оценить плюсы и минусы каждой идеи. Если ои возвращается с решением, которое лучше ответа или решения, придуманного вами, вы можете научиться сами и поделиться с ним своими мысля ми. Это великий опыт познания для вас обоих. Как наставник вы указываете другим путь к решению, побуждая их самих к поиску ответов на вопросы и предоставляя им возмож ность думать и изучать подходы к решению различных проблем.
Прежде мы уже цитировали Аристотеля (см. главу 2): “Лишь по-на стоящему образованный ум способен поддержать мысль, не прини мая ее”. Вы поддерживаете мысли и точки зрения других людей, и это делает ваше собственное мышление шире. Когда вся команда воспримет это правило как норму, вы увиди те, что интеллектуальная мощь вашей команды стремительно вы растет и вы можете приступать к созданию чего-то великого. Дайте шанс другим решать проблемы . Укажите им верное направление, не сообщая само решение. В процессе поиска решения человек учится.
На что это похоже Вы, действительно, помогаете людям, хотя и не кормите никого с ложечки. Вы не говорите загадками, не даете уклончивых ответов, вы просто умеете подводить людей к тому, чтобы они самостоя тельно находили ответы на все вопросы.
Сохраняйте равновесие • Отвечайте на вопрос вопросом, который указывает правиль ное направление поиска ответа. • Если кто-то по-настоящему застрял, не мучайте его. Скажите ему ответ и объясните, почему он является таковым.
43 Регистрируйте лишь готовый код "Регистрируй весь свой код ка к можно чаще, независимо от того, готов он или нет, особенно когда ты собираешься от лучиться из офиса на день-другой ".
Вот вам загадка: что может быть хуже, чем вообще не пользо ваться системой контроля версий? Ответ: только пользоваться ею некорректно. То, как вы используете систему контроля версий, существенно влияег на вашу продуктивность, стабильность продукта, качество и режим работы. Например, частота, с которой вы регистрируете свой код и отдаете его в общий доступ, играет заметную роль. Вам нужно сдавать код в регистрацию сразу, как только вы за кончили выполнение какой-либо задачи, не нужно выдерживать его па своем компьютере долгое время. Какой прок от вашей рабо ты, если код, созданный вами, недоступен другим и его невозмож но интегрировать или использовать? Нужно отпустить его на волю, чтобы он начал сообщать вам обратную связь1. Очевидно, регистрировать версию каждую неделю или месяц бессмысленно —в этом случае система контроля версий использу ется не по назначению. Вы можете услышать самые разные оправ дания для подобной небрежности. Вам могут сказать, что разработ чики работают дома или за границей, а доступ к системе контроля версий слишком медленный. Это типичный пример сопротивления рабочей среды (environmental viscosity), когда легче сделать что-то неправильно, нежели правильно. Очевидно, что это несложная техническая проблема, и ее нужно решить. С другой стороны, можно ли сдавать код, если задача еще не вы полнена до конца? Допустим, вы работаете над некоторым ответст венным кодом. Вы собираетесь идти домой и продолжить работу дома, после ужина. Самый простой способ забрать его домой —э го загрузить его на работе в систем)' контроля версий и взять его отту да уже из дома. Но если вы зарегистрируете код, над которым вы все еще про должаете работать, это значит, вы отдали в репозиторий код в не рабочем состоянии. В нем могут оставаться ошибки компиляции, 1 К тому же, вы ведь не хотите держать единственную копию кода на жест ком диске, имеющем “трехмесячную гарантию*1, чересчур долго.
или в результа те внесенных вами изменений он может быть ме со вместим с остальным кодом системы. Ваши действия могуг серьез но сказаться на остальных разработчиках, если они захотят вос пользоваться последней версией вашего кода. Как правило, вы регис трируете группу файлов, которые имею т отношение к конкретной выполненной задаче или к исправлен ной вами ошибке. Вы загружаете их все вместе, сопровождая пояс нительным сообщением, которое в дальнейшем позволит помя ть, какие файлы изменились и, самое важное, почем)'. Такое крошеч ное сопровождение существенно облегчит вам жизнь, если вдруг потребуе тся дать обратный ход изменениям. Убедитесь в том, что все модульные тесты успешно пройдены еще до того, как вы отправили свой код на регистрацию. Один из прос тейших путей определить, насколько пригоден ваш код для за грузки в систему контроля, —это использовать непрерывную инте грацию. Надежно и без регистрации Если вам нужно перенести или просто сохранить исходный текст кода, ко торый еще не закончен, у вас есть следующие возможности: Используйте удаленный доступ. Не сдавайте полусырой код, чтобы по том забрать его из дома, оставьте его на работе и используйте уда ленный доступ к нему. Возьмите его с собой. Скопируйте его на флэшку, CD или DVD и унесите с собой. Используйте подключаемый ноутбук. Если эта проблема возникает у вас постоянно, вам лучше использовать ноутбук в паре с установоч ной станцией (laptop with a clocking station). В этом случае ваш код всегда будет при вас. Воспользуйтесь специальными функциями системы контроля версий. Microsoft Visual Team System 2005 имеет функцию “размещения на полках": некоторые продукты попадают в категорию, предполагаю щую общий доступ к ним, независимо от их загрузки в систему кон троля версий. А в системах CVS и Subvision есть возможность созда вать боковые ветви разработки, которые служат для хранения кода, не готового для магистрали разработки (см. [ТН03] и [Mas051).
Регистрируйте лишь готовый ко д . Никогда не выклады вайте в общий доступ код, который не готов к работе. Созна тельная регистрация кода, который не компилируется или не проходит модульных тестов, должна расцениваться как пре ступная халатность в отношении всего проекта.
На что это похоже Вы чувствуете, что по другую сторону системы контроля версий на ходится ваша команда. Вы знаете, что как только вы регистрируете свой код, он становится доступен всем.
Сохраняйте равновесие • Некоторые сист емы контроля версий различают “регистри руемые” и “общедоступные” версии кода. В этом случае вы мо жете осуществлять временную регистрацию версий (напри мер, на время пути с работ ы домой) без риска навлечь на себя гнев своих коллег. • Некот орые люди любят устраивать ревизию кода до того, как отправить его на регистрацию. Э го хорошо, если только это чрезмерно не задерживает регистрацию. Если какая-то часть , процесса разработки регулярно вызывает' задержку, нужно пересмотреть такой процесс. • Регистрировать версии нужно так часто, как только возмож но. Не прибегайт е к отговорке “не готов”, пытаясь уклонить ся от сдачи кода в регистрацию.
44 Проводите ревизию кода
\;N
"Из пользователей получаются великолепные тестеры. Не волнуйся: если что-то не так, они сообщат нам".
Ыаилучшее время для поиска проблем в коде —сразу, как только он написан. Если вы оставляете все как есть на некоторое время, он вряд ли будет лучше пахнуть. Ревизии кода и устранение дефектов Из книгиuОценка стоимости ПО* СEstimating Software Costs”) Каперса Джонса (Capers Jones) Формальные обследования примерно в два раза эффективнее, чем лю бая известная форма тестирования, по выявлению глубоко сидящих и ма лоприметных ошибок программирования и являются практически единст венным известным методом, на который приходится до 80% устраненных дефектов.
Как указывает Каперс Джонс, ревизии кода являются, возмож но, наилучшим методом локализации и решения проблем. К сожа лению, иногда бывает трудно убедить менеджеров и разработчи ков в пользе их проведения. Менеджеры обычно обеспокоены тем, что на ревизию кода ухо дит немало времени. Им не хочется, чтобы их команда прекратила разработку и проводила много времени на совещаниях по ревизии кода. Разработчики же опасаются ревизии кода: они воспринима ют проверку своего кода другими людьми, как некую угрозу для себя. Это задевает их самолюбие. Их беспокоит тот факт, что их могут незаслуженно обидеть. В проектах, где мы сами практиковали ревизии кода, результа ты превзошли все ожидания. Недавно Венкат участвовал в одном проекте, который был очень жестко спланирован по времени, причем в команде были и менее опытные разработчики. Они могли создать высококачест венный и стабильный код с помощью строжайших ревизий кода. Когда разработчик заканчивал реализацию и тестирование какойлибо задачи, его код подвергался тщательной проверке со стороны другого разработчика, перед тем как отправить его на регистра цию в систему контроля версий. Этот процесс помог выявить небольшое число проблем. Кста ти, ревизии должны проводиться не только для кода, написанного начинающими программистами; любой код, независимо от опыта его разработчика, должен подвергаться тщательной проверке.
Итак, как же проводить ревизию кода? Можно выбрать из сле дующих основных возможностей. Всенощ ное бдение (The all-nighter). Можно устраивать гранди озные ночные сессии для ревизии кода один раз в месяц, когда вся команда собирается вместе и заказывает пиццу. Но это не самый лучший (и не особенно гибкий) способ проведения ревизии. Массовые ревизионные собрания более склонны к обширным и за тянутым дискуссиям. Такая масштабная ревизия не только не явля ется необходимой, но может даже препятствовать успешной разра ботке. Мы не рекомендуем данный подход. П одхваты вание (The pick-up game). Как только некий код на писан, скомпилирован, протестирован и готов к регистрации, код подхватывается другим разработчиком для проверки. Эти опера тивные ревизии служат быстрым неформальным способом гаран тировать приемлемое качество кода перед его регистрацией. Чтобы исключить любые поведенческие перекосы, старайтесь че редовать разработчиков. Например, если в прошлый раз Джейн проверяла код Джоя, попросите на этот раз Марка проверить его. Эта техника может быть весьма "эффективной1. П арное программирование (Pair program m ing). В Экстремаль ном программировании (Extreme Programming, ХР) вы никогда не пишете код в одиночку. Код пишется парами: один человек сидит за клавиатурой, это драйвер (driver), а другой сидит за ним и исполняет роль навигатора (navigator). Время от времени они меняются места ми. Вторая пара глаз действует как непрерывная ревизия кода, и вам нет необходимости выделять специальное время для нее. На что нужно обращать внимание во время ревизии? Вы можете составить свой собственный список того, что нужно проверить (все обработчики исключений должны быть заполнены, все обра щения к базе данных происходят в пределах транзакции, и т.д.). Вот минимальный перечень того, с чего лучше начинать проверку: • Можно ли читать и понять это код? • Присутствуют ли в нем явные ошибки? • Может ли этот код влиять на другие части приложения неже лательным образом? • Присутствует ли дублирование кода (в пределах самого про веряемого фрагмента или в остальной части приложения)? • Есть ли возможность сделать код рациональнее или улучшить его с помощью рефакторинга? 1 Болес подробно об этой технике см. “Поставь продукт!" [RG05].
Дополнительно им можете использовать специальные инстру менты анализа кодов, такие как Similarity Analyzer или Jester. Если такого рода проверки приносят пользу, сделай те их частью автома тизированного процесса непрерывной сборки приложения. v 0
j
Ч н т
П роверяйт е в есь к о д . Р евизии кода служат замечательным средством повы ш ения качества кода и ум еньш ения общ его количества ош ибок в нем. П ри правильном подходе реви зи и превращаются в удобны й и эффективный инструмент. П роводите ревизию кода после вы полнения каж д о й задачи , привлекая различны х разработчиков .
На что это похоже Ревизии кода происходят небольшими порциями, постепенно. Они становятся частью текущей работы над проектом, а не отдель ным мероприятием, которого все боятся.
Сохраняйте равновесие • Ревизии кода, которые происходят механически, без осмыс ления, не приносят пользы. • Ревизии кода должны активно оценивать дизайн и ясность кода, а не то, насколько точно имена и структуры перемен ных соответствуют неким корпоративным стандартам. • Различные разработчики могут написать один и тот лее код соверш енно по-разному. Если код сделан по-другому, это во все не значит, что он хуже. Не критикуйте код, если вы не мо жете существенно улучшить его. • Ревизии кода не принесут пользы, если рекомендованные по правки не вступают в силу достаточно быстро. Нужно запла нировать рабочее совещание, контролирующее выполнение рекомендаций, или ввести систему аннотирования кода, за писывая все замечания к нему и отслеживая их постепенную реализацию. • Всегда замыкайте цикл на рецензентах кода: сообщ айте всем, какие шаги были предприняты в результате проведенной ре визии кода.
45 Информируйте других “М ен ед ж ер , ко м ан д а и з а ка з ч и ки ждут вы полнения в озл о ж енны х на тебя зад ач . Е сл и их интересует, в к а ко м состоя нии находится твоя работа, они с ам и спросят тебя. С иди, не высовывайся и работай спокойно".
Приняв задачу на исполнение, вы тем самым обязуетесь сделать ее в срок. П ег ничего необычного в том, что в процессе работы у вас возникают проблемы, которые приводят к задержкам па опре деленных этапах. Приближается срок завершения работы, и все от вас ждут демонстрации работающего кода. Что будет, если вы при дете па демонстрацию и сообщите всем, что вы еще не закончили работу? Кроме всеобщей неловкости, это может создать опреде ленную опасность для вашей карьеры. Если вы ждете до самого последнего момента, чтобы сообщить неприятное известие, вы вынуждаете своего менеджера и техниче ского руководителя контролировать вас на каждом шагу. Они будут опасаться таких сюрпризов в дальнейшем и начнут проверять вас по несколько раз вдень, чтобы убедиться, что ваша работа продви гается, а не стоит на месте. Ваша жизнь превращается в типичный сюжет комикса Дильберта. Предположим, вы находитесь примерно в середине задачи. Вам кажется, ч то технические трудности не позволят вам закончить вы полнение задачи в срок. Если вы предупреждаете всех остальных за ранее, вы даете им возможность подумать и найти выход из сложив шейся ситуации пока не поздно. Они могут попросить другого разработчика помочь вам. Они могут передать вашу задачу кому-то еще, кто более знаком с ней. Они могут конкретизировать объем ра боты для вас или скорректировать общий перечень работ, который выполнит команда в рамках текущей итерации. Ваш заказчик может охотно согласиться на досрочное выполнение другой, не менее важ ной задачи, вместо той, с которой возникла задержка. Если вы постоянно держите других в курсе, сюрпризы исключа ются и все чувствуют себя комфортнее, если знают о вашем про движении. Они знают, когда нужно протянуть вам руку помощи, и у них нет причин не доверять вам. Традиционные способы информировать людей — это послать им сообщение по e-mail, оставить записку или позвонить. Есть еще один способ, который Алистэр Кокберн (Alistair Cockburn) на зывает “информационными батареями” (“inform ation radiators”)1. 1 См. hUp://c2.coro/cgi-bin/wiki?InforiTiationfladiator.
Информационная батарея похожа плакат на степе, содержимое ко торого постоянно обновляется. Проходящие мимо люди воспри нимают информацию легко, без каких-либо усилий. Давая им ин формацию, вы избавляете их от необходимости задавать вам вопросы. Ваша информационней! батарея может сообщать о ходе выполнения ваших задач, а также давать любые другие сведения, которые, на ваш взгляд, могут бьггь интересны команде, менедже ру или заказчикам. Вы можете использовать плакат на стене, web-сайт или Wiki, блог или RSS-ленту. Главное —размещать свою информацию гам, где люди могут видеть ее регулярно. Вся команда может использовать информационные батареи для оповещения остальных о текущем состоянии дел, собственных проектах дизайна, новых интересных идеях и т.д. Теперь каждый может поумнеть, лишь пройдя мимо информационной батареи, и менеджер будет в курсе текущего состояния дел.
V h v
И н ф о р м и р уй т е д р у г и х . Публикуйте свой рабочий статус, собственные идеи и все, что кажется вам интересным. Н е д о жидайтесь, пока другие сам и спросят о ваш их успехах.
На что это похоже Менеджеры и сослуживцы не донимают вас постоянными расспро сами о состоянии вашей работы, вашего проекта дизайна или теку щих исследований.
Сохраняйте равновесие • Ежедневная деловая пятиминутка (см. Практика 38, Наладь те регулярное очное общение; с. 165) помогает держать всех в курсе на высоком уровне. • Представляя статус работ, обеспечьте тот уровень детализа ции, который подходит для конкретной аудитории. Напри мер, директора и владельцы компаний слабо интересуются деталями поведения абстрактных базовых классов. • Информирование других не должно отнимать у вас больше времени, чем выполнение ваших основных задач. • Держите голову высоко, не опускайте ее.
К а к всего одна лам па способна рассеять тысячи лет тьмы , т ак всего одна искра здравомыслия разрушает тысячи лет неве жества.
Хьюи-Ненг (Hui-Ncng)
Глава 9
Эпилог: Движение к гибкости Одна искра здравомыслия. Это все, что требуется. Мы надеемся, что вам понравилось наше изложение гибких практик и что, по крайней мере, одна или две из них помогут высечь искру здраво мыслия, от’ которой вам станет светлее. Независимо от того, какой опы т у вас за плечами —был ли ваш путь успешным или полон испытаний, — всего лишь одна новая практика способна сломать все преграды и изменить вашу карьеру и дальнейшую жизнь к лучшему. Можно применить некоторое под множество этих практик, чтобы быстро вытащить проект из труд ной ситуации, а можно постепенно и планомерно внедрять более полный их набор.
9.1
Еще одна новая практика
Возьмем прим ер из жизни — эта история про одного из прежних клиентов Энди. Команда программистов располагалась в много численных офисах, которы е следовали друг за другом, образуя изящную кривую вдоль внешней стены высокого стеклянного оф исного здания. Каждый сидел у огромного окна, и вся команда занимала прим ерно половину периметра здания. Н о там были серьезны е проблемы. Релизы выходили с опозданием, а ошибки размножались бесконтрольно. Как обычно, программисты-прагматики начали опрос команды с одного конца цепочки офисов, пытаясь узнать у разработчиков, над чем они работаю т, что получается хорошо, а что не очень. П ер вый опрош енны й рассказал, что они создают прилож ение типа "клиент-сервер” с предельно тонким клиентом и очень толстым
сервером, содержащим в себе всю бизнес-логику и доступ к базе данных. Но по мере того, как опрос продвигался вдоль длинной цепочки офисов, ответ на гот же самый вопрос становился другим. Взгляд на суть проекта слегка менялся от офиса к офису. Самый послед ний опрошенный в конце цепочки гордо заявил, что система будет иметь массивного клиента, содержащего в себе GUI и всю бизнеслогику, а на сервере останется только база данных! Стало понятно, что команда никогда не собиралась вмест е, что бы поговорить о проекте. Вмест о этого каждый из членов команды общался лишь со своим ближайшим соседом. Получалось, как в игре в “испорченный телефон”, когда любое сообщение неизбеж но портится и искажается в процессе передачи от одного человека к другому. Совет программистов-прагматиков? Немедленно начать прове дение сборов-линеек (см. Прак тика 38, Наладьте регулярное очное общение; с. 165). Результата были быстрыми и поразительными. Все вопросы, связанные с архитектурой, быстро разрешились, но это не главное. Команда начала становиться единым целым, ф ор мируя сплоченный коллектив, который работает сообща. Темпы роста числа ошибок значительно снизились, продукт стал более стабильным, а сроки завершения работы уже не наводили на всех смертел ьн ы й ужас. На это потребовалось совсем немного времени и усилий. Что бы приучить людей вовремя являться на линейку, нужна некото рая дисциплина, но и это скоро вошло в привычку. Всего лишь одна новая практика привела к грандиозным изменениям в рабо те команды.
9.2
Спасение проекта от провала
Если одна практика хороша, значит, все они вместе дадут еще луч ший результат, не так ли? В конечном счете, это так, но не следует вводить их все одновременно, особенно если проект уже находит ся в критическом состоянии. Одновременная замена всех дейст вующих практик разработки — лучший способ завалить весь проект. Воспользуемся аналогией из медицинской практики. Допустим, ваш пациент жалуется наболи грудной клетке. Понятно, что, если бы он регулярно занимался физкультурой и правильно питался, с ним бы этого не случилось. Но вы не може*ге ему сказать: “Ещьте хлеб без масла и займитесь бегом трусцой”. Это приведет к фаталь-
мым последствиям и определенно повысит размер вашей страхо вой премии от проф ессиональной небрежности. Необходимо стабилизировать состояние больного, используя минимальный (но не слишком малый) набор лекарств и процедур. Только когда состояние пациента нормализуется, можно пре/сла гать ему режим, которы й поможет ему укрепить здоровье. Когда проект находится в критическом состоянии, сначала нуж но применить к нему-пару-тройку практик, чтобы стабилизировать ситуацию. Н апример, однажды Венкату позвонил один из потен циальных клиентов: он в панике сообщил о том, что их проект на ходится в состоянии полного упадка. Они потратили уже половин)' отведенного срока, а выполнили не более 10% работы. М енеджер жаловался, что разработчики работаю т очель мед ленно, пишут очень мало кода. Разработчики жаловались на то, что менеджер их заставляет слишком много работать. Нужно ли им тратить весь остаток дня на поиск и исправление ошибок или же лучше добавлять новую функциональность в код? Несмотря на глу бину кризиса, вся команда была искренне заинтересована в успехе своей работы. Но они не знали, как этого добиться. Все, что они пытались делать, лишь о тбрасывало их назад еще дальше. О ни чув ствовали нависшую над ними опасность и боялись принимать ре шения. Венкат не пытался реш ить все проблемы сразу, он сначала ре шил стабилизировать состояние пациента путем внедрения прак тик, ориентированны х на общение и сотрудничество внутри кол лектива, таких как П рактика 3, Критикуйте идеи, а не людей (с. 19); П рактика 38, Наладьте регулярное очное общение (с. 165); Прак тика 43, Регистрируйте лиш ь готовый код (с. 179); П рактика 45, И нф ормируйте других (с. 185). После того как это было восприня то, был предпринят следующий шаг — наведение порядка в рели зах; это П рактика 13, Д ерж ите все наготове (с. 60) и П рактика 14, И нтегрируйте сразу, интегрируйте часто (с. 64). И, наконец, они начали повыш ать свою культуру написания кода: П рактика 34, Предупреждения ф актически являю тся ошибками (с. 148); Прак тика 35, Атакуйте проблемы в изоляции (с. 152). Этого оказалось достаточно, чтобы предотвратить кризис; проект был закончен на две недели раньш е срока и был признан руководителями высшего звена исклю чительно успешным. Это прим ерная модель выхода из аварийной ситуации. Если все не так м рачно, можно прим енить комплексный и более взве ш енный подход к введению гибких практик. У нас есть еще не сколько реком ендаций для вас, эти реком ендации зависят о того, к какой категории вы себя относите: являетесь ли вы руководите-
лем или лидером команды либо или вы обычный программист, который пытается внедрить передовой опыт в пределах своей ор ганизации.
9.3
Введение гибкости: Руководство для руководителя
Если вы являетесь менеджером или лидером команды, прежде всего вам нужно заручиться поддержкой команды. Объясните своим сотрудникам, что гибкая разработка облегчает жизнь самим разработчикам. В первую очередь, именно они выигрывают (в ко нечном счете, выиграют также заказчики и сама организация). Если жизнь разработчиков не облегчается, значит, что-то делает ся неправильно. Не торопитесь. Помните, что любое слабое движение руководи теля со временем усиливается и бьет по команде намного ощути мее1. Когда вы знакомите команду с этой концепцией гибкости, сде лайте упор на основополагающих принципах гибкого проекта, ко торые рассмотрены в главе 2 (Начала гибкости; с. 11). Каждый дол жен понять, что именно так будет происходить выполнение проекта. Начните с проведения сбора-линейки (см. Практика 38, Наладь те регулярное очное общение; с. 165). Это позволит команде об щаться друг с другом и координировать усилия по важнейшим во просам. Включите всех архитекторов в группу разработчиков, и пусть они засучат рукава и участвуют в разработке (см. Практика 39, Архитекторы должны писать код; с. 169). Начните проводить не формальные ревизии кода (Практика 44, Проводите ревизию кода; с. 182) и привлеките заказчиков/пользователей к сотрудничеству (Практика 10, Позвольте заказчику принимать решения; с. 49). Далее необходимо привести в порядок инфраструктуру, сопро вождающую процесс разработки. Это предполагает принятие (или улучшение) следующих основополагающих практик из серии Starter Kit: • Контроль версий • Модульное тестирование • Автоматизация сборки Контроль версий необходимо обеспечить в первую очередь. Это самый первый элемент инфраструктуры, который мы уста 1 Замечательная книга о том, как управлять командой разработчиков и от тачивать свой стиль руководства. - “За закрытой дверью” [RD05].
навливаем для любого проекта. Когда ом установлен, нужно орга низовать постоянный процесс локальной сборки у каждого разра ботчика, в сценарий которого включен запуск всех доступных модульных тестов. Так как все это доступно в оперативном режи ме, вы можете начать создавать модульные тесты для всего нового кода, который находится в разработке, а также добавлять новые тесты для уже написанного кода. Наконец, запустите в фоновом режиме машину непрерывной сборки приложения как “упор” для быстрого перехвата проблем, оставшихся без внимания. Если эта территория вам не слишком хорошо знакома, сбегайте в ближайший книжный магазин (или зайдите на ww w .Pragm aticBookshelf. com) и приобретите книгу “Поставь П ро дукт! Практическое руководство по успешным программным про ектам” |RG05]. Она поможет вам соорудить и заставить работать весь этот механизм. Серия Starter Kit поможет вам подробнее уз нать о контроле версий, модульном тестировании и инструмен тах автоматизации для различных конфигураций. Когда вся инфраструктура готова, нужно настроиться на некий ритм. Перечитайте главу 4 (Делать, как хотят пользователи; с. 47), чтобы почувствовать пульс и ритм проекта. Теперь, когда заложен прочный фундамент, нужно выполнить более тонкую работу, отрегулировав гибкие практики таким обра зом, чтобы получить от них наибольший эффект для всей коман ды. Просмотрите еще раз главу 5 (Гибкая обратная связь; с. 85) для выполнения настройки, а также главы б (Гибкое программирова ние; с. 108) и 7 (Гибкая отладка; с. 143), в которых обсуждаются под ходы к решению текущих ежедневных задач. Последним по порядку, но не по значению, шагом должно стать введение сессий brown-bag (обед, на который все приносят еду сами) и других практик из главы 3 (Взращивание гибкости; с. 28). Использование практик из главы 8 (Гибкое сотрудничество; с. 163) поможет вашей команде работать, как единое целое. Время от времени, например в конце каждой итерации или каж дого релиза, проводите ретроспективное совещание по проекту. Получайте обратную связь от вашей команды: что работает хоро шо, что требует корректировки, и от чего лучше вообще отказать ся. Если какая-то из практик не приносит ожидаемого эффекта, просмотрите соответствующие разделы “На что это похоже” и “Со храняйте равновесие”: возможно, в каком-то из аспектов ее реали зации вы перегнули палку, но все еще можно исправить.
9.4
Введение гибкости: Руководство программиста
Если вы ие руководите работой команды, но желаете наставить ва ших коллег на пугь гибкост и, вам придется преодолеть определен ные препятствия. Нужно выполнить все, что перечислено в преды дущем разделе; при этом у вас есть только одна возможность: воспитывать людей личным примером, а ие заставлять их делать что-то по принуждению. Как говорит старая пословица, “Вы можете привест и лошадь на водопой... но не сможете заставить ее использовать ваш люби мый редактор кода”. Конечно, до тех пор, пока его преимущества не будут очевидны для вашег о коллеги. Как только польза от его применения станет ясной для окружающих, они сами устремятся за вами. Например, очень хорошо начинать с модульных тестов. Вы мо жете начать с их использования на примере собственного кода. Уже спустя короткий промежуток времени (несколько недель или даже меньше) вы увидите, что ваш код стал заметно лучше —снизи лось общее число ошибок, улучшилось качество, возросла стабиль ность. Вы теперь уходите домой ровно в пять часов, все ваши зада чи выполняются в срок, и вам не звонят по ночам отчаявшиеся клиенты, умоляющие вас исправить новые ошибки. Разработчик, который сидит с вами по соседству, начинает интересоваться, что вы делаете иначе, и вы делитесь своими “секретами*'. Вместо того чтобы старат ься убедить других в чем-то полезном» вы сделали гак, что они сами стремятся перенять у вас передовой опыт. Если вы твердо намерены повести свою команду на освоение но* вых территорий, вы поступаете честно, если идете туда первым. Так что начинайте с тех практик, которые вам доступны прямо сей час. Большинство практик из главы 2 (Начала гибкости; с. 11) обес печат вам удачный старт, после которого следует перейти к прак тикам, ориентированным на культуру программирования, таким как Практика 19, Посадите ангелов себе на плечи (с. 87) и Практи ка 20, Используйте код до сборки (с. 92), а также практики из глав 6 (Гибкое программирование; с. 108) и 7 (Гибкая отладка; с. 143). Вы можете запускать непрерывную сборку на своем компьютере и уз навать о любых проблемах сразу, по мере их возникновения. Ваши коллеги могут заподозрить в вас способности к ясновидению. Через какое-то время вы можете начать проводить неформаль ные brown-bag сессии (Практика 6, Повышайте уровень своей команды; с. 108) и рассказывать своим коллегам о ритмах гибкого проекта (Практика 9, Почувствуйте ритм; с. 43) и других интерес ных вещах.
9.5
Конец?
И так, мы подошли к концу книги. Что произойдет дальше, полно стью зависит от вас самих. Вы можете применить эти практики для себя и получить от них эф ф ект для себя лично, или же вы можете взять с собой всю команду и начать сообща создавать более качест венные программные продукты —быстрее и легче, чем раньше. Пожалуйста, посетите наш сайтмш . pragma ticp год rammer. com, где можно найти блоги авторов этой книги и другую информацию, а также ссылки на другие полезные ресурсы. Спасибо за чтение,
Приложение А
Ресурсы А.1 Интернет-ресурсы Блог Энди (/\ndy*s Blog) h t t p : //to o ls h e d /c o m /b lo g
Блог Энди Ханта» охватывает множество тем; в частности, слегка касается разработки программного обеспечения. Гибкая разработка (Agile Developer) h ttp ://w w w .a g ile d e v elo p e r.co m /d o w n lo a d .a s p x
Страница гибкой разработки, откуда можно скачать статьи и пре зентации Венката Субраманиама. Группы Google (Google Groups) h t t p ://g r o u p s . g oo gle. com
Web-сайт для доступа к обсуждению групп новостей. Дизайн умер? (Is Design Dead?) h ttp ://w w w .m a rtin fo w le r.c o m /a rtic le s /d e s ig n D e a d .h tm l
Превосходная статья Мартина Фаулера, в которой обсуждается значение и роль дизайна в гибкой разработке Информационная батарея (Information Radiator) h ttp ://c 2 .c o m /c g i-b in /w ik i? In fo r m a tio n R a d ia to r
Обсуждаются информационные батареи Алистера Кокберна (Alistair Cockburn) Искусство программирования для Unix (The Art of U nix Programming) h t t p ://w w w . fa g s . o rg /d o c s /a rtu /c h 0 4 s 0 2 . html
Отрывки из книги Эрика Стивена Реймонда “Искусство Програм мирования для Unix” (Eric Steven Raymond, “The Art of Unix Program m ing”)
Конец эпохи программного инжиниринга и начало эры рационального моделирования (The End of Software Engineering and the Start of Economic-Cooperative Gaming) h t t p : //a lis t a ir .c o c k b u r n .u s /c r y s t a l/a r t ic le s /e u s e a ts o e c g /t h r r n d o f softwareen g in e e rin g .h tm
Ллистср Кокбсрн размышляет о том, следует ли считать разработ ку программных продуктов софтверным инжинирингом и предла гает новую модель. Муравейник(Ап&П1) h ttp ://w w w . urbancode. c o m /p r o je c ts /a n th ill./d e fa u lt. jsp
Инструмент, который позволяет организовать управляемый про цесс сборки {непрерывного интегрирования) и коллективный дос туп к информации в пределах организации. Непрерывная интеграция (Continuous Integration) h ttp ://w w w .m a rtin fo w le r.c o m /a rtic le s /c o n y in u o u s In te g ra tio n .html
Статья, в которой обсуждаются преимущества непрерывной инте грации. Объектно-реляционные отображения (Object-Relational Mapping) http: //w w w .n e w a rd .n e t/te d /w e b lo g /in d e x .j sp?date=200410 0 3 \tt1096871 540048
Тед Ныоард размышляет об инфраструктурах; здесь вы встретите его слова о том, что “объектнореляционные отображения — это Вьетнам компьютерных наук”. Открыто-замкнутый принцип (Open-Closed Principle) h ttp ://w w w .o b ie c tfn e n to r.c o n i/re s o u rc e s /a rtic le s /o c p . pdf
Описание открыто-замкнутого принципа с примерами и ограниче ниями. Открыто-замкнутый принцип: Короткое введение (Open-Closed Principle: Short Introduction) h t t p :/ / c 2 . com /cgi/w iki?O penC losedPrinciple
Обсуждение открыто-замкнутого принципа, аргументы “за” и “про тив”. Почему ваш код столь ужасен (Why Your Code Sucks) h ttp ://w w w .artim a.co m /w eb lo g s/view p o st.jsp ? th read =71730
Блог Дейва Астелса (Dave Astels), размышляющего о качестве кода. Прагматическое программирование (Pragmatic Programming) h t t p ://www.pragmaticprogramm er. com
Домашняя страница программиста-прагматика (The Pragmatic Programmer), где размещены ссылки на другие издания серии
Pragmatic Bookshelf (включая и эту книгу), а также информация для разработчиков и менеджеров. Практика управления проектом разрабо тки программного обеспечения: Провал или успех (Software Project Management Practices: Failure versus Success) http://www.stsc.hillaf.mil/crosstalk/2004/l0/04i0Jones.html
Каперс Джонс анализирует 250 софтверных проект ов —успешных и неудачных. Принцип инверсии зависимости (Dependency Inversion Principle) http://c2.com/cgi/wiki9DependencyInversionPrinciple
Короткая статья, посвященная принципу инверсии зависимости (Dependency Inversion Principle). Принцип одиночной ответственности (Single Responsibility Principle) http://c2.com/cgi-bun/wiki7SingleResponsibilityPrinciple
Описание принципа одиночной ответственности, ссылки на ста тьи и обсуждения по данной теме. Принципы объектно-ориентированного дизайна (Object-Oriented Design Principles) h t t p ://c2.com/cgi/wiki?PrinciplesOfObjectOrientedDesign
Хорошая подборка различных объектноюриентироваиных прин ципов проектирования кодов. Разработка на базе тестов (Test Driven Development) htt p ://c 2 .com/cgi/wiki?TestDrivenDevelopment
Введение в разработку на базе тестов. Регулирование скорости движения (CruiseControl) http://cruisecontrol.sourceforge.net
Средство непрерывной интеграции, предназначенное главным образом для Java- приложений. То же самое средство для C# на платформе .NET, CruiseConirol.NET, доступно на странице htt p ://sourceforge.net/projects/ccnet.
Среда интегрированного тестирования (Framework for Integration Testing) http://fit.c2.com
Средство коллективной работы, позволяющее в автоматическом режиме сравнивать ожидания заказчиков с фактическими резуль татами. Т ебе это не понадобится (You Aren't Gonna N eed It) h t t p :/ / c 2 .com/cgi/wiki?YouArentGonnaNeedIt
Обсуждение принципа YAGNI, доводы “за” и “против”.
Трагедия на Сомме: Вторая Балаклава (Tragedy on the Somme: A Second Balaclava) http://www,wo r1dwa r1.сош/s fsomme.htm
Обсуждаются последствия битвы на Сомме во время первой миро вой войны. Устранение повреждений (Damage Control) http://dev.buildpa tterns.com/trac/wiki/DamageCont rol
Средство непрерывной интеграции, написанное на Ruby on Rails. Draco.NET http://draconet.sourceforge.net
Средство непрерывной интеграции для .NET, реализованное как служба Windows. JUnit http://www.junit.org
Сайт посвящен разработке ПО с использованием среды JU nit или любой другой среды тестирования из семейства XUnit. JUnitPerf h t.Ip://www.clarkware.com/software/JUnitPerf.html
Коллекция декораторов тестов для JUnit, позволяющих измерять производительность и масштабируемость функциональности, за ключенной в уже работающих тестах, созданных на базе JUnit. NUnit ht tp://sou reefогде.net/projects/nun it
Сайт посвящен разработке ПО с использованием NUnit. XProgramming.com http://www.хргодramming.com/software.htm
Коллекция полезных ресурсов, среди которых ссылки на инстру менты тестирования.
А.2
Библиография
[BecOO] [Cla04]
[FBB+99]
Kent Beck. Extreme Programming Explained: Embrace Change. Addison- Wesley, Reading, MA, 2000. Mike Clark. Pragmatic Project Automation: How to Build, Deploy, and Monitor Java Applications. The Pragmatic Programmers, LLC, Raleigh, NC, and Dallas, TX, 2004. Martin Fowler, Rent Beck, John Brant, William Opdyke, and Don Roberts. Refactoring: Improving the Design of Existing- Code. Addison Wesley Longman, Reading, MA, 1999.
[Fow05]
[GHJV95]
[HT00]
f НТОЗ]
[HT04]
|Jon98] [Knu92]
[Lar04] [LC01]
[Lis88] [Mar02]
[Mas05]
[Mey97]
[MR84]
Chad Fowler. M y Job Went to India: 52 Ways to Save Your Job. Pragmatic Programmers, LC, Raleigh, NC, and Dallas, TX, 2005. Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Patters: Elements of Reusable ObjectOriented Software. Addison-Wesley, Reading, МЛ, 1995. Andrew H unt and David Thomas. The Pragmatic Programmer: From Journeyman to Master. Addison-Wesley, Reading, MA, 2000. (Эндрю Хант и Дэвид Томас, Программист-прагматик: Путь от подмастерья к мастеру, изд-во “Л ори”, 2007) Andrew H unt and David Thomas. Pragmatic U nit Testing in Java with JVnit. The Pragmatic Programmers, LLC, Raleigh. NC, and Dallas, TX, 2003. Andrew H unt and David Thomas. Pragmatic Unit Testing in C# with N U nit, The Pragmatic Program m ers, LLC, Raleigh. NC, and Dallas, TX, 2004. Capers Jones. Estimating Software Costs. McGraw Hill, 1998. Donald Ervin Knuth. Literate Programming. Center of the Study of Language and Inform ation, Stanford, CA, 1992. Craig Larman. Agile and Iterative Development: A Manager's Guide. Addison-Wesley, Reading, MA, 2004. Bo Leuf and Ward Cunningham , The Wiki Way: Collaboration and Sharing on the Internet. Addison-Wesley, Reading, MA, 2001. Barbara Liskov. Data abstraction and hierarchy. SIGPLANNotices, 23(5), May 1988. Robert C. Martin, Agile Software Development, Principles, Patterns, and Practices. Prentice Hall, Englewood Cliffs, NJ, 2002. Mike Mason. Pragmatic Version Control Using Subversion. The Pragmatic Programmers, LLC, Raleigh, NC, and Dallas, TX, 2005. Bertrand Mayer. Object-Oriented Software Construction. Prentice Hall, Englewood Cliffs, NJ, second edition, 1997. William A. Maiden and Kyle Y. Rone. Design, development, integration: space shuttle primary flight software system. Communications o f the A C M , 7(9):914925, 1984.
[Rai04]
[RD05]
[RG05]
[ Roy70]
[Sch04] [Sen90 ]
[Sha97] [Sub05] [TH01]
[TH03]
[TH05]
[You99]
J.B. Rainsberger. [U n it Recipes; Practical Methods fo r Programmer Testing, Manning Publications Co., Greenwich, CT, 2004. Johanna Rothman and Esther Derby. Behind Closed Doors: Secrets of Great Management. The Pragmatic Programmers, LLC, Raleigh, NC, and Dallas, T X, 2005. Jared Richardson and Will Gwaltney. Skip It! A Practical Guide to Successful Software Projects. The Pragmatic Programmers, LLC, Raleigh, NC, and Dallas, TX, 2005. Winston W. Royce. Managing the development of large software systems. Proceedings. IE E E W ECON, pages 1-9, August 1970. Ken Schwaber. Agile Project Management with Scrum. Microsoft Press, Redmond, WA, 2004. Peter Senge, The Fifth Discipline: The Art and Practice of the Learning Organization. Currency/Doubleday. New York, NY, 1990. Alec Sharp. Smalltalk by Example: The Developer's Guide. McGraw-Hill, New York, NY, 1997. Venkat Subramaniam, .N E T Gotchas, O ’Reilly & Associates, Inc., Sebastopol, CA, 2005. David Thomas and Andrew Hunt. Programming Ruby: The Pragmatic Programmer's Guide. Addison-Wesley, Reading, MA, 2001. David Thomas and Andrew Hunt. Pragmatic Version Control Using CVS. The Pragmatic Programmers, LLC, Raleigh, NC, and Dallas, TX, 2003. David Thomas and David Heinemeir Hansson. Agile Web Development with Rails. The Pragmatic Programmers, LLC, Raleigh, NC, and Dallas, TX, 2005. Edward Yourdon. Death March: The Complete Software Developer's Guide to Surviving uMission Impossible” Projects. Prentice Hall, Englewood Cliffs, NJ, 1999 (Эдвард Йордон, Путь камикадзе: Как разработчик)' про граммного обеспечения выжить в безнадежном про екте, изд-во “Лори”, 2003).