Анализ и оценка методов разработки программного обеспечения (Agile)

Принципы agile

Показывать лекцию целиком

4.1 Что есть принцип?

Для прояснения методологического контекста полезно квалифицировать, что же является принципом, а что нет. Настоящий принцип обладает двумя свойствами – абстрактность и нетривиальность. Абстрактность отличает принцип от практик, нетривиальность – от банальностей.

Абстрактность означает, что принцип должен быть общим правилом, а не специфической практикой. "Создавайте прочную финансовую основу для будущего" – это принцип. "Откладывайте ежемесячно 10 % заработка на сберегательный счет" – это практика. Часто, как в этом примере и как в случае с agile практиками, рассматриваемыми в этой главе, многие практики существуют для поддержки принципов.

Нетривиальность означает, что разумный человек может не соглашаться с принципом. Рассмотрим такое правило, как "заботьтесь о качестве ПО". Это правило нельзя считать принципом. Ни один разумный человек не может с ним не соглашаться и считать, что не следует заботиться о качестве ПО. Утверждение справедливое, но тривиальное. Для того чтобы правило было принципом, вы должны быть способны независимо от вашего собственного мнения ссылаться на кого-то, кто не согласен с этим правилом. Правило "вначале тест" отвечает этому критерию, являясь принципом. Вполне можно полагать, что вначале следует писать программы, а затем их тестировать, или полагать, что вначале должны быть спецификации и именно они должны предшествовать созданию программ.

Принципы, рассматриваемые в этой главе, удовлетворяют предлагаемым критериям. Практики также важны, и они будут рассмотрены в последующих главах. Банальности, которых также хватает в литературе по agile, будут игнорироваться в данном рассмотрении.

Помимо указанных критериев, принципы должны быть не описательными, а предписывающими. Они не описывают некоторое свойство, а дают руководство к действию или накладывают запрет на него ("Не возжелай жены ближнего"). Это требование императивности не всегда справедливо для принципов в нетехнической области. "Лучшее – враг хорошего" является принципом, хотя и не носит предписывающий характер. Для принципов, применяемых в методологии построения ПО, императивный характер представляется полезной идеей. Он будет использован для принципов, представленных в этой главе.

4.2 Официальные принципы

Как отмечалось в первой обзорной главе, в agile-манифесте перечисляются двенадцать принципов, которые следует проанализировать в первую очередь, поскольку они представляют официальную точку зрения.

Официальные agile принципы

А1 Наивысшим приоритетом для нас является удовлетворение потребностей клиента благодаря регулярной и ранней поставке ценного программного обеспечения.

А2 Изменение требований приветствуется даже на поздних стадиях разработки. Agile процессы позволяют использовать изменения, обеспечивая клиенту конкурентное преимущество.

А3 Работающий продукт следует выпускать как можно чаще, с периодичностью от пары недель до пары месяцев.

А4 Ежедневно на протяжении всего проекта разработчики и представители бизнеса должны работать совместно.

А5 Над проектом должны работать мотивированные профессионалы. Чтобы работа была сделана, создайте для них условия, обеспечьте поддержку и полностью доверьтесь им.

А6 Непосредственное общение является наиболее практичным и эффективным способом обмена информацией как с самой командой, так и внутри команды.

А7 Работающий продукт – основной показатель прогресса.

А8 Инвесторы, разработчики и пользователи должны иметь возможность поддерживать постоянный ритм работы. Agile помогает наладить такой устойчивый процесс разработки.

А9 Постоянное внимание к техническому совершенству и качеству проектирования повышает гибкость проекта.

А10 Простота (искусство минимизации лишней работы) крайне необходима.

А11 Лучшие требования, лучшие архитектурные и технические решения рождаются у самоорганизующихся команд.

А12 Команда должна систематически анализировать возможные способы улучшения эффективности и соответственно корректировать стиль своей работы.

Этот список полезен для понимания. Хотя он получен из первоисточника, мы не можем непосредственно работать с ним. Нашей первой задачей будет формирование более аккуратного, пригодного для использования множества принципов. Официальный список не отвечает роли по следующим причинам:

  • Некоторые из перечисленных пунктов являются практиками, а не принципами – А6, А12.
  • Другие являются банальностями: А9 и А5 – кто стал бы поддерживать построение системы немотивированными разработчиками.
  • Некоторые пункты выражены в форме утверждений, а не предписаний. В этом нет ничего страшного, если они допускают простое перефразирование. Например, пункт А7 можно сформулировать следующим образом: "Используйте работающий продукт в качестве основной меры измерения прогресса разработки".
  • Для некоторых пунктов возникают проблемы при переходе к императивному стилю. В пункте 10 утверждается, что простота означает минимизацию лишней работы. Но "стройте как можно более простую систему" и "минимизируйте лишнюю работу" – это два разных принципа. У них есть существенное отличие, что позже будет рассмотрено подробнее. Переход к императивному стилю принципов позволяет избежать неточностей.
  • Хотелось бы иметь независимые правила. Те, что были перечислены, частично пересекаются. Частота поставки упоминается в А1 и А3, важность работающего ПО – в А3 и А7.
  • С другой стороны, правила явно неполны. Ни одно из них не упоминает тестирование. Упор на тестирование для достижения качества является одним из центральных свойств agile.
  • 4.3 Используемый список

    Для замены официального списка используем классификацию agile принципов, введенную в обзорной главе. Для простоты приведем этот список повторно:

    Agile принципы

    Организационные

  • Поставить клиента в центр.
  • Разрешать самоорганизацию команды.
  • Сохранять устойчивый темп разработки.
  • Разрабатывать минимально необходимое ПО:
  • Создавать минимальную функциональность.
  • Создавать только запрашиваемый продукт.
  • Разрабатывать только код и тесты.
  • Допускать изменения.
  • Технические
  • Вести разработку итеративно:
  • Выполнять частые рабочие итерации.
  • Замораживать требования на время итерации.
  • Рассматривать тесты как ключевой ресурс: .
  • Никакую новую разработку не начинать, пока не пройдут все тесты.
  • Вначале тесты.
  • Выражать требования через сценарии.
  • Рассмотрим вначале организационные принципы, затем технические, имеющие программную специфику.

    4.4 Организационные принципы

    Организационные принципы воздействуют на управление проектом, расписание работ, организацию команды.

    4.4.1 Поставить клиента в центр

    "Все мы ориентированы на клиента" – банальное утверждение. Agile разработка придает этой идее некоторую серьезность, требуя включать представителей бизнеса непосредственно в процесс разработки.

    Во многих традиционных подходах взаимодействие с клиентом (заказчиком) осуществляется на определенных этапах. Клиенты участвуют в разработке требований. На этом этапе, как часть строго управляемого процесса, проводятся интервью или организуются специальные семинары. Клиенты появляются на заключительном этапе при проведении тестовой приемки (приемо-сдаточное тестирование). Некоторые организации запрещают контакты разработчиков и клиентов между этими этапами, хотя в большинстве случаев такого экстремизма не наблюдается.

    В agile подходах взаимодействие осуществляется в течение всего проекта. В терминах Бека [Beck 2005]:

    Вы придете к лучшим результатам с реальными клиентами. Они, те, кого вы пытаетесь удовлетворить. Отсутствие клиентов или ориентировка на воображаемого клиента ведет к тому, что вы разрабатываете функции, которые не будут использоваться, специфицируете тесты, которые не отражают критерии приемки, и теряете шанс выстроить настоящие отношения между людьми с наиболее разнообразными видениями перспектив проекта.

    Клиенты в agile проектах приглашаются на регулярные встречи, могут свободно взаимодействовать с разработчиками и имеют возможность проведения испытаний проекта в процессе непрерывно выпускаемых версий. Некоторые подходы идут дальше и рекомендуют включать представителей бизнеса в состав команды разработчиков.

    Если клиенты не вовлечены в проект, это рассматривается как одна из наиболее серьезных угроз проекту – построение программной системы, не отвечающей потребностям пользователя. Еще в 1981 году в классической работе Боема [Boehm 1981] "Экономика программной инженерии" приводились примеры систем, в которых все было правильно – надежность, производительность, за исключением маленькой детали – они решали не ту задачу, которая была нужна пользователям. Лутц [Lutz 1993], анализируя ошибки в программных проектах NASA, пишет:

    Первичными причинами функциональных дефектов, связанных с безопасностью, являются ошибки в обнаружении и понимании требований (62 % для Вояджер, 79 % для Галилео). Дефекты, возникающие из-за нарушения условий, почти всегда связаны с непониманием требований.

    Стоит отметить, что причиной в данных ситуациях может быть то, что написанию точных и ясных требований уделялось недостаточно внимания, как раз то, c чем яростно борются сторонники agile.

    Вовлеченность клиентов – это важный вклад agile. Проблема возникает тогда, когда взаимодействие с клиентами заменяет требования. Такой предлагаемый agile подход опасен хотя бы потому, что нет единой сущности – "клиент". Любой серьезный проект включает различные категории сопричастников (термин более общий, чем клиент):

  • пользователи будущей системы, которые тоже неоднородны и могут быть разных видов – онлайновая система резервирования, штатные сотрудники системы, владельцы кинотеатров, артисты, агенты, продюсеры;
  • исполнительные директора, также работающие в компании, но сосредоточенные на согласовании системы с общей политикой компании, заботящиеся о будущем развитии системы;
  • агенты по снабжению, юристы и так далее.
  • Все эти группы могут иметь смешанные, а иногда и противоречивые интересы и приоритеты. Роль процесса выработки хороших требований состоит в том, чтобы как можно ранее высветить эти противоречия и разрешить их. Компромиссное решение принимает ответственное лицо – "владелец продукта". Вот почему семинары для разрешения требований являются хорошим дополнением к индивидуальному сбору требований.

    Если этот формализованный процесс заменить разговорами с сопричастниками, то возникает риск принятия точки зрения тех, кто в данный момент участвует в обсуждении. Возможно, это будет не лучший источник. Зачастую, те, кто действительно владеет предметом, являются занятыми людьми, они могут принять участие в семинаре, но не держат дверь открытой для постоянных обсуждений. Есть большая вероятность того, что люди, включенные в команду, имеют время для этого потому, что в компании они не играют важной роли. Если руководство согласилось выделить в команду эксперта, отрывая его от решения важных задач, то стоит задаться вопросом, действительно ли это человек является наиболее квалифицированным экспертом.

    Беку [Beck 2005] знаком риск работы с единственным экспертом:

    Возражение, которое я слышал, что включение клиента в команду приводит к тому, что система отвечает требованиям этого клиента, но не подходит никому другому.

    Его ответ на это возражение состоит в следующем:

    Проще обобщить успешную систему, чем специализировать систему, которая не решает никакой конкретной задачи.

    Это спорный аргумент. "Успешная система" может быть так настроена на специфические потребности, что потребуется значительная работа по ее переделке для учета других потребностей. С другой стороны, "система, которая не решает никакой конкретной задачи", может иметь прочную основу и, например, плохой интерфейс, который может быть улучшен. Даже если встать на точку зрения Бека, то это не объясняет, почему беседы с одним экспертом в процессе разработки исключают попытки сбора точек зрения всех сопричастников на определенном этапе проекта. Отрицание такой необходимости довольно безответственно.

    4.4.2 Разрешать самоорганизацию команды

    В agile подходе у менеджеров отбирается традиционная роль распределения задач и назначения их разработчикам. Здесь верят в способность команды организовать свою собственную работу. Это особенно характерно для подхода Scrum, при котором традиционное понятие менеджера проекта заменяется мощной командой, принимающей собственные решения под контролем "владельца продукта", задающего функциональность продукта, и Scrum-мастера, отвечающего за координацию и устраняющего возникающие препятствия. Подробнее эти роли будут изучаться в следующей главе.

    Для многих разработчиков, пострадавших в свое время от плохих менеджеров, этот аспект метода является наиболее привлекательным. В ответ на длинную критическую статью с критикой agile метода один из защитников XP и Scrum пишет:

    Наиболее важный аспект этих методов в том, чтобы поставить менеджмент на положенное ему место. На первом месте должны быть люди, фактически делающие работу. Когда они скажут, что будет сделано и когда, то можно верить, что проект действительно будет выполнен в указанное время.

    Необходимость в менеджерах, конечно, остается, по-скольку так работают компании и это соответствует словам создателей оригинального метода Scrum (его непрограммной версии): "умный контроль согласуется с самоорганизованным характером команды проекта".

    Швабер и Сазерленд [Schwaber 2012], создатели программного Scrum, также придают особое значение умному контролю:

    Контроль со стороны коллег, равных по статусу членов группы, контроль "по любви" является основой умного контроля. Динамика работы команды сглаживает негласное (неосознаваемое) знание группы и создает явное знание в форме ПО.

    Слова на самом деле обидные. Вы представляли, что работаете в самоорганизуемой команде, но неожиданно узнаете, что фактически находитесь под контролем, использующим негласные и неосознаваемые методы. Возможно, слова о любви могут вас утешить, а возможно, и нет.

    Может быть, это дело вкуса. Лично я, если уж довелось быть управляемым, то хотел бы знать своего босса, чем считаться самоорганизуемым, а на практике находиться под контролем, использующим тайные методы. Фактически роль менеджеров слабо освещена в agile литературе. Комментарии на эту тему обычно даются в скандально отрицательном стиле: "существует общее неверное представление о их роли в agile проектах…", которое, возможно, истинно, но ничего не говорит нам, почему "неверное представление" ставится на первое место. Более важное рассмотрение – соответствующая роль менеджеров – остается в тени. Швабер и Сазерленд пишут:

    Хотя команды, разрабатывающие проект, действуют по собственному усмотрению, они не являются неуправляемыми.

    Им вторит Кон:

    Общее неверное представление о менеджменте в agile проектах состоит в том, что по причине самоорганизованной команды лидеры играют малую роль или вообще не играют роли. Ничего не может быть далее от истины. В "Биологии Бизнеса" Филипп Андерсон опровергает это ошибочное представление: "Самоорганизация не означает, что работники вместо менеджеров начинают заниматься управлением. Это не означает, что работникам позволительно делать то, что они хотят делать. Это означает, что менеджмент берет на себя обязанность охраны эволюции поведения, возникающего в процессе общения с независимыми агентами, вместо заранее заданной спецификации поведения".

    Другими словами, agile менеджеры "управляют", за тем исключением, что они делают не слишком много или делают "несмотря на…".

    Agile тексты полны рассказами о предполагаемом балансе между "слишком много" и "слишком мало", но менеджер, желающий понять, каковы общие принципы, может найти только то, что не следует делать. Например, ему не следует решать, какие функции должна выполнять система (эту роль играет владелец продукта). Он не должен решать, что и когда должен делать член команды (это роль команды).

    Утверждение Кона, что самоорганизация не означает, что "работникам позволительно делать то, что они хотят делать", вызывает недоумение. Если здесь есть разница, то она слишком тонкая. Дерби в статье, посвященной все тем же "неверным представлениям", уверяет, что "самоорганизуемая" не означает "самоорганизованная":

    Это потому, что самоорганизация представляет процесс и характерное свойство, а не нечто такое, что сделано однажды и навсегда. Самоорганизуемая, с точки зрения социальных систем, означает, что команда может создавать новые подходы и адаптировать их при встрече с новыми вызовами.

    "Создавать новые подходы и адаптировать их при встрече с новыми вызовами" не может не вызывать восхищения. Но что мешает в нормально ведущихся проектах, включая традиционные проекты во главе с менеджером, стиль которого "командуй и контролируй", создавать "новые подходы"? "Самоорганизуемые команды, видимо, более амбициозны. Миттал [Mittal 2013] пишет, что самоорганизуемая команда все же требует существования наставника и тренера, но не требует "команд и контроля".

    Хотя наставники и тренеры и в самом деле играют важную роль в agile, как будет видно в следующей главе, негативный характер этого наблюдения снова разочаровывает: "командуй и контролируй" – это то, что традиционно делают менеджеры. Как указывает Кон, они все же должны делать что-то подобное, но полезно было бы знать, что именно.

    Хотя ответ нельзя найти в agile литературе, в конечном итоге его можно вывести, пользуясь здравым смыслом. Большинство проектов нуждается в менеджере, исповедующем стиль "командуй и контролируй". Недостаток такой военизированной схемы, где один человек играет эту роль, в том, что при этом сдерживается креативность членов команды. В качестве другого крайнего случая можно рассматривать талантливую команду, которая может быть полностью самоорганизуемой и не нуждается ни в менторах, ни в тренерах.

    (рис 4.1) Камерный ансамбль без дирижера I Musici

    В мире музыки известным примером является легендарный камерный оркестр I Musici, существующий с 1952 года – один из лучших камерных оркестров мира.

    Как о нем сказано в Википедии: "I Musici представляет ансамбль без дирижера. Но отношения между музыкантами таковы, что они создают великолепную гармонию в создании музыки". И действительно! Если собрать вместе группу первоклассных программистов, то они вполне могут работать подобно I Musici. Достаточно глупо пытаться втиснуть их в какие-то рамки. С другой стороны, попросите группу неопытных начинающих музыкантов сыграть вместе – ничего хорошего из этого не получится. Даже профессионалы, собирающиеся для разового выступления, не могут работать таким образом. Вот почему большинство оркестров, даже небольшие ансамбли, нуждаются в дирижере. Большинству команд программистов-разработчиков требуется менеджер проекта.

    Уроки, которые можно извлечь из agile подхода к самоорганизующейся команде:

  • в ряде случаев опытные и тесно связанные команды ("I Programmatori") могут работать без менеджера, большинство команд должны его иметь;
  • некоторые из традиционных для менеджера обязанностей, такие как выбор задач для очередной итерации, могут быть переданы другим членам команды;
  • менеджер должен поощрять инициативу членов команды и постепенно двигаться к частичной или полной степени самоорганизованности.
  • 4.4.3 Поддерживайте устойчивый темп

    Agile методы настаивают на центральной роли программистов, на необходимости создать им такие условия работы, которые позволили бы полностью раскрыть их потенциал. Из этого, в частности, следует отказ от того, что Эд Жордан в популярной и полезной книге [Yourdon 2003] называет "маршами смерти": практика, при которой менеджмент соглашается на недостижимые, нереализуемые условия – проект с расплывчатыми и растущими требованиями, жесткими контрольными сроками. В результате менеджер пытается давить на команду разработчиков, как следствие – авралы, бессонные ночи, работа без выходных и праздников.

    Большое влияние оказала и книга Де Марко и Листера "People Ware" [DeMarco 1999], впервые опубликованная в 1987 году, которая в простых и ясных терминах объясняла, как работают программисты и как важно обеспечить им достойные условия работы.

    Кокбурн [Cockburn 2005], в частности, продвигает принцип "персональной безопасности" разработчиков, включающий свободу высказываний. Он заявляет:

    Персональная безопасность – это способность говорить нечто, что вас беспокоит, без страха репрессий. Сюда входит возможность сказать менеджеру, что сроки нереалистичны, сказать коллеге, что ее проект нуждается в улучшении и даже то, что ей следует чаще принимать душ. При персональной безопасности команда способна обнаруживать и устранять свои слабости. Без этого люди не будут высказываться, слабости будут накапливаться, что может принести вред команде.

    В духе PeopleWarePeopleWare – известная книга Тома Демарко и Тимоти Листера "Человеческий фактор: успешные проекты и команды". agilism настаивает на уважении труда программистов, на необходимости обеспечения их хорошими условиями работы. Эти идеи перемежаются с другими аспектами метода: предпочтение устных коммуникаций письменным документам; советом использовать открытые пространства, а не замкнутые офисные кабинки. Шваббер [Schwaber 2004a] описывает, как открытое пространство влияет на атмосферу в компании, рассматривая ситуацию до и после:

    Первое знакомство произвело удручающее впечатление. Люди либо находились в комнатах за закрытыми дверями, либо в отдельных кабинках… Не было бесед, делового шума, никакого ощущения, что здесь работают люди, занятые общим делом.

    Компания прислушалась к его советам, и во время следующего спринта:

    Все обстояло совершенно по-другому. Люди беседовали, раздавался смех, дружественное общение наполняло рабочее пространство.

    Социологические аспекты такого подхода представляются интересными. Мы уже отмечали в первой главе, что agile идеи имеют социальную интерпретацию: "революция офисных работников". Движение agile отражает стремление программистов к самоутверждению, установление примата, верховенство кода над артефактами "Джилберт босса" – планами, моделями и документами.

    Дебаты эти не новы. Еще в 1977 году в книге Филиппа Крафта [Kraft 1977], насыщенной марксистским анализом, осуждались предшественники сегодняшних методов "предваряющего анализа" (что включало даже структурное программирование) как попытка менеджмента применять капиталистические методы при производстве программных продуктов и превратить программистов в безгласных пролетариев. Марксистский анализ ушел, сторонники agile беззастенчиво вводят ROI и другие капиталистические штучки, но проталкивание пролетариев-программистов на первые роли остается.

    Есть нюансы между различными школами agile. Все они стоят за расширение полномочий программистов, но, возможно, по разным причинам. Четыре подхода, рассматриваемые подробно в этой книге, можно разделить на две категории:

  • XP и Crystall – это движение за истинную программистскую гордость. Цитируемое выше утверждение Кокбурна типично для этих подходов, фокусируясь на восстановлении чувства собственного достоинства программистов в противовес менеджерам.
  • Дух Scrum и Lean отличен. Их методы корнями уходят в индустриальные методы производства; их авторы склонны ссылаться на "Тойоту", превознося производительность и борясь с ненужными затратами.
  • Примером подхода второй школы может служить рассказ Швабера [Schwaber 2004a], когда, будучи Scrum-мастером, он обнаружил, что ключевой программист исчез, взяв впервые за два года отпуск, и как он наивно полагал, отправившись в Йеллоустонский парк, что стал недоступным для контактов (попытался бы он найти подобное место в Европе). Беззастенчивый Scrum-мастер из-за того, что срывались контрольные сроки проекта, нанял детектива и отловил программиста (попробовал бы он это сделать в Европе), сумев спасти сроки и выдержать темпы проекта. Возможно, такой способ управления можно отнести к "тонким методам управления", но возникает чувство ностальгии по старым добрым временам, когда были менеджеры с их правами, но и установленными границами этих прав. Кажется, в намерение автора входило не только похвастаться бесстрашным стилем управления, но и дать некоторый общий урок. Одурманенному читателю остается гадать, как примирить принципы устойчивого темпа и персональную безопасность Crystal.

    Примером приема достижения устойчивости является рекомендуемая XP практика создания запасок (slack), как отмечается в еще одной книге Де Марко [DeMarco 2001]. Бек [Beck 2000] пишет:

    В любой план включайте несколько небольших задач, которые можно опустить, если не выдерживаются сроки.

    Далее:

    Вы можете создавать запаски разными способами. Одна неделя из восьми может быть "Geek WeekGeek Week – неделя на увлечения, хобби, не занятая основным делом. Geek (гик) – программист, увлеченный своим делом." неделей. Двадцать процентов еженедельного бюджета может отводиться для задач, выбираемых самим программистом.

    Допуск в двадцать процентов – это известная практика в Google.

    4.4.4 Разрабатывайте минимальный продукт

    Методы agile настаивают на простоте. Целью является получение скорейшей обратной связи с пользователем. Продукт поставляется в короткие сроки, даже если он покрывает лишь часть задуманной функциональности.

    Дух минимализма в agile выражается в нескольких формах: производить только запрашиваемый продукт, создавать только код и тесты. Давайте проэкзаменуем их, а затем оценим достоинства минимализма.

    Создавать минимальную функциональность

    Общая точка зрения agile состоит в том, что многие программные системы страдают избыточностью: функциональностью, в которой нет необходимости или которая нужна только немногим пользователям. Во время разработки системы тратится напрасно время, которое можно было бы использовать для улучшения основной функциональности или сокращения времени выпуска очередного релиза системы. Избыточность наносит вред фокусировке программистов на реальных проблемах, вредит сопровождению системы, может ограничить эволюцию системы.

    Лозунг, популярный в экстремальном программировании, – "Тебе Это Не Понадобится" (YAGNI – You Ain't Gonna It), который напоминает нам:

    всегда работать над историей пользователя, которую мы имеем, а не над тем, что мы думаем, что это нам понадобится. Даже если мы собираемся это использовать. [Jeffries 2001]

    Поппендиксы [Poppendieck 2010] пишут:

    Наши программные системы содержат значительно больше свойств, чем это нужно. Лишь часть свойств будет действительно использована. Избыточные свойства увеличивают сложность системы, приводят к нелинейному росту стоимости. Даже если половина кода не является необходимой (консервативная оценка), то стоимость не просто удваивается, вероятно, она возрастает раз в десять.

    (Я не знаю, коэффициент 10 является литературным приемом или точной оценкой: в любом случае мне неизвестны опубликованные данные, дающие точные эмпирические оценки на основе проведенных исследований.)

    Далее они добавляют:

    Прекрасная возможность улучшить производительность – прекратить создавать свойства, в которых нет абсолютной необходимости.

    Если код сегодня не требуется, то помещать его в систему – расточительство. Сопротивляйтесь таким искушениям.

    Производить только запрашиваемый продукт

    Мудрость программной инженерии учит программистов стремиться к достижению двух качеств продукта, способствующих долговременным преимуществам в противовес сиюминутным выгодам:

  • расширяемость: создавайте архитектуру, поддерживающую будущие расширения, в частности, будущие потребности пользователя;
  • повторное использование: создавайте программные элементы настолько общими, насколько это возможно, помимо той роли, которую они играют в текущем проекте, чтобы их было можно использовать как в этом проекте, так и в будущих разработках (когда это происходит, они превращаются в программные компоненты).
  • Для методов agile эти цели не считаются важными и, более того, вообще не считаются целями. Целью является разработка продукта, работающего здесь и сейчас. Вот типичные высказывания, иллюстрирующие отрицание всего, что не связано с текущими потребностями. Вард Каннингхэм [Ward Cunningham 2004] пишет:

    Нас всегда учили делать настолько много, насколько это возможно. Всегда выполнять проверки. Всегда заботиться об исключениях. Всегда ориентироваться на наиболее общий случай. Всегда давать пользователям лучшие подсказки. Всегда выводить осмысленные и полные сообщения об ошибке. Всегда то, всегда это. Вам приходится делать так много фоновых вещей, что не остается времени на размышления. Я говорю, выбросите все это из головы, и спросите себя: "Какова простейшая вещь, которая может еще выполнять работу?"

    Эта фраза: "Делайте простейшую вещь, которая может еще выполнять работу" стала подобно YAGNI (Это Тебе Не Понадобится) еще одной мантрой agile. Рон Джефрис [Jeffries 2001], объясняя, почему не следует заботиться о повторном использовании, пишет:

    Если только проект не делается той же командой, то трудно получить эффект от повторного использования. Большая разница между некоторой частью проекта, которую я могу повторно использовать, и упаковки этой части в такую форму, чтобы и другие могли ее использовать. Необходимо упаковать эту часть, что я для себя не делаю, подготовить документ, сделать часть более защищенной, удаляя источники, с которыми я автоматически связывался, ответить на вопросы и провести тренировку будущих пользователей. Если делать все эти вещи, то это накладно для меня, если не делать, то трудно будет другим и немногим им поможет.

    Я строю абстракции, когда они мне необходимы. Если абстракция понадобится мне снова в другом контексте, я приспособлю ее. Но моей целью не является построение вещей для других проектов. Я не пытаюсь зря тратить время и деньги на построения для других проектов.

    Такие заявления дают повод, чтобы волосы встали дыбом. (В этой книге мы неоднократно сталкиваемся с подобными утверждениями, так что книгу следовало бы снабжать специальным купоном на льготное восстановление волос!)

    Как и во многих подобных случаях, все начинается совершенно корректно, даже с приведения впечатляющих наблюдений. Проектирование для будущего может отличаться от решения проблемы здесь и сейчас. Проектирование для повторного использования трудное дело. Показательным примером является проектирование класса, описывающего точку в двумерном пространстве. Как можно его обобщить? Можно думать о точках в $$n$$-мерном пространстве, можно рассматривать любые объекты с двумя координатами (точки, вектора, комплексные числа…) или о любых фигурах в двумерном пространстве (точки, линии, многоугольники). Существует много способов обобщения, и нет способа понять, какое из обобщений будет полезным. В этом случае лучше не пытаться догадаться, каким будет будущее, а дождаться, когда это будущее настанет.

    Но из такого наблюдения, имеющего общий смысл, можно ли делать вывод о том, что мы можем забыть об обобщении, "проверках", "исключениях" и повторном использовании? Такие запреты лишь поддерживают плохую практику разработки. Простейшим примером является использование встроенных констант. Вы создаете продукт для небольшой компании, и требуется структура данных, представляющая список сотрудников. Массив из 1000 элементов вполне достаточен, не правда ли? Но компания выросла, и неожиданно ваш продукт загадочным образом перестал работать. Исторические катастрофы, стоящие миллиарды долларов, являлись следствием подходов "давайте делать то, что нам нужно теперь", – ограничение памяти в МS DOS 640 K, Миллениум 2000, начальный размер IP-адресов.

    Недальновидный совет, приводимый выше, – заботьтесь только о том, что нужно здесь и сейчас, – губителен для вашего процесса разработки программных продуктов. Вне зависимости от того, что вы найдете полезным в остальных канонах agile, этот совет лучше проигнорировать.

    Разрабатывайте только код и тесты

    Один из наиболее радикальных принципов agile отрицает необходимость всех стандартных продуктов, относящихся к процессу разработки, в частности, отрицает необходимость документов – документов требований, документов этапа проектирования, планов, программной документации, полагая, что они только отвлекают разработчика от основной работы – создания работающего кода и тестов. Вот что можно прочитать [Poppendick 2001]:

    Документы, диаграммы, модели, создаваемые как часть разработки программного проекта, являются часто расходными материалами, полезными при создании системы, но вовсе не являющимися необходимой частью финального продукта. Когда работающая система поставляется пользователю, то ему не важно, какие расходные материалы были потрачены на ее создание. Принципы Lean (экономичного программирова-ния) предполагают, что необходимость каждого расходного материала должна быть предметом тщательного анализа. Необходимо доказать, что данный артефакт не только увели-чивает ценность конечного продукта – его использование дает наиболее эффективный способ увеличения ценности продукта.

    Под расходными материалами понимается все, что не поставляется пользователю. Все, что не является кодом и тестами, все традиционные артефакты, создаваемые в процессе разработки: технико-экономическое обоснование проекта, записи и видео интервью и семинаров при выработке требований к проекту, документ требований, документы этапа проектирования, презентации Power Point по будущему развитию системы, электронная переписка, UML-диаграммы…

    Вот что говорит Бек [Beck 2005], описывая роль архитекторов:

    Архитекторы в команде XP отыскивают и выполняют многомасштабный рефакторинг системы, создают тесты на уровне системы, отражающие ее архитектуру, реализуют истории пользователя.

    Это высказывание совершенно четко является провокационным, поскольку перечисленные задачи имеют весьма слабое отношение к тому, чем традиционно занимаются архитекторы, – определяют архитектуру системы. Здесь изначально предполагается, что строится "простейшая вещь, которая может еще выполнять работу", после чего архитекторы должны выполнить рефакторинг (другими словами, улучшить архитектуру, если она post facto оказалась неудовлетворительной), провести тестирование и подобно любому другому члену команды реализовать пользовательские истории.

    Некоторые авторы, хотя и с неохотой, но признают, что поставляться могут не только тесты. Кокбурн [Kockburn 2005], например, пишет, какие артефакты могут приносить выгоду разработчикам:

    Вы не получите выгоду ни от каких элементов, которые не связаны с работающим кодом и тестами. "Okay", все же можно получить выгоду в финальной поставке материалов для тренировки и документации с описанием системы, ее развертывания.

    Заметьте, сдержанное "Okay" – некоторые исключения могут быть, но только код и тесты остаются элементами проекта, представляющими истинный интерес.

    Минимализм. Оценка

    Настаивание на минимальном продукте в трех вышеописанных формах приводит к наиболее абсурдному и вредному вкладу agile методов.

    Как всегда, есть некоторая правда в критике традиционных проектов, в данном случае их склонности к разрастанию. Проекты и продукты имеют тенденцию включать слишком много свойств. Критика бумажной работы и создания документов, в которых нет большой необходимости, также частично справедлива. Многие документы, производимые в компаниях, устаревают ко дню поставки или малозначимы. Правда и то, что в конечном итоге приходится считаться с кодом, а не с диаграммами Ганта или UML.

    Ничто из этого не оправдывает отказ от предваряющего планирования в целом.

    Прежде всего многие из проблем, связанных с разрастанием, являются результатом плохого менеджмента. Компетентный менеджер проекта знает, как сражаться с избыточной функциональностью и постоянно спрашивает, действительно ли необходимо то или иное свойство. Вопрос, проверенный временем: "Вы действительно хотите все или вы хотите это теперь?" Хотя этот вопрос немного напоминает шантаж, но работает превосходно.

    Классический анализ требований, который, к ужасу аджилистов, выполняется в самом начале проекта и позволяет убедиться, что мы прежде думаем, а потом стреляем, выступает в роли арбитра между потребностями многочисленных сопричастников проекта и устанавливаемыми свойствами. Часто приходится сталкиваться с длинным списком свойств, для каждого из которых сопричастники убеждают, что данное свойство абсолютно необходимо. Хороший выход из этой ситуации дает известный прием: предложите каждому из сопричастников 100 виртуальных долларов и попросите распределить их между свойствами в зависимости от их важности. Истинно критические свойства немедленно проявляются.

    Обратная сторона стратегии: "стройте простейшую вещь, которая может еще выполнять работу", состоит в том, что она предпочитает срывать на каждом этапе легкодоступные плоды – свойства, которые могут быть реализованы достаточно просто, что создает положительный демо-эффект. Проекты, использующие эту стратегию, хорошо работают, пока не наступает финальная стадия. В процессе разработки все счастливы, разработчики поставляют хорошо выглядящие демо, пользователи убеждаются в их работе, но в конце выясняется, что совершенно необходимые, но трудно реализуемые свойства были отодвинуты в сторону, так что невозможно поставить удовлетворяющий результат.

    Когда 1 октября 2013 года начал действовать закон президента Обамы "О доступном здравоохранении", то, к радости его противников, те, кто им воспользовался, стали жертвами успеха закона. Практически никто не мог им воспользоваться, более того, не мог даже приобрести страховку. Вину списали на высокий уровень активности, но это лишь жалкая отговорка. В конце концов, коммерческие сайты регулярно обрабатывают гораздо большие объемы данных и сталкиваются с большими сложностями. Если только не предположить полную некомпетентность разработчиков, то, похоже, что в процессе разработки и тестирования пользовательских историй все, казалось, хорошо работало. Просто здесь недостаточно хорошо была продумана архитектура и на раннем этапе разработки не убедились, что система хорошо масштабируется.

    Еще одной иллюстрацией угроз от такого плохо согласованного подхода может служить эпизод с Боингом. Пример соответствует духу примеров agile – из областей, не связанных с программированием. Исходное развертывание флагмана Боинга 787 Dreamliner было приостановлено из-за угроз, связанных с батареями. Полеты были отменены на несколько месяцев. Джеймс Суровицки так комментировал эту ситуацию в газете "New Yorker":

    Решив быстрее поставить свои самолеты пользователям, Боинг построил много экземпляров, не дожидаясь их сертификации Федеральным Авиационным Агентством, разрешающим полеты. Ему пришлось вернуться к модернизации самолетов в соответствии с требованиями ФАА. Если говорят "семь раз отмерь – один отрежь", то в данном случае "дважды строили, один раз проверяли" (как признался мне аналитик из корпорации). Спешка и погоня за снижением стоимости – лучший рецепт нарваться на трудности.

    Это только примеры. Но они подтверждают, как наивно ожидать, что рефакторинг может решить все остающиеся проблемы, если то, что уже сделано, работает лишь частично. Эти проблемы могут быть очень трудными, например, если источником проблемы является производительность, то зачастую ее решение требует полного перепроектирования системы. Эмпирические свидетельства подтверждают это предположение. Боем и Тернер [Boehm 2004] пишут:

    Эксперименты показывают, что на низкостоимостной рефакторинг нельзя рассчитывать при масштабировании проекта.

    Единственными источниками, которыми мы располагаем, пришли от разработчиков с небольшим опытом, которые обнаружили, что даже для небольших приложений процент рефакторинга и усилия по устранению дефектов увеличиваются с ростом размера требований.

    В программировании, как и в любой инженерной деятельности, экспериментирование с различными подходами можно только приветствовать, но критически важно при любом подходе привлекать подходящий стиль предваряющего анализа, позволяющий избежать серьезных ошибок на начальном этапе разработки.

    Некоторые из худших катастроф проектов, которые мне доводилось наблюдать, это те, в которых заказчик или менеджер требовал показать ему нечто работающее прямо сейчас ("не имеет значения, что еще не все работает, просто покажите мне работающий пример!"). Он мог сурово упрекать разработчиков инфраструктуры, которая не создает впечатления непосредственно видимого результата, другими словами, делающими свою работу ответственных профессионалов. Менеджеры получали свои демо, но в конечном итоге ничего более, так как, сосредоточиваясь на визуально доступной функциональности, трудные проблемы всякий раз отодвигались в сторону. Каждый раз, когда добавлялся новый уровень функциональности или масштабирования системы, команде приходилось начинать с проектирования, так как ранее они не уделили должного внимания инфраструктуре. К нежизнеспособному, терпящему бедствие проекту заказчики теряли интерес, и раньше или позже крах проекта был неизбежен.

    Аргументация за получение в ходе разработки результатов, допускающих визуализацию, справедлива, но только в том случае, если визуализация не достигается за счет нарушения фундаментальных концепций программной инженерии: управление рисками. Признаком качества хорошо управляемого проекта является то, что он на раннем этапе определяет задачи на критическом пути, те, которые погубят проект, если они не будут сделаны качественно. Некоторая задача с высоким риском может отражать фундаментальную функциональность, допускающую демонстрацию на ранних этапах. Требование масштабируемости проекта проявляется на поздних стадиях и не может быть продемонстрировано на начальном этапе. Фокусироваться на визуализации, игнорируя основы разработки, безответственно.

    (рис 4.2) Итальянская лазанья – иллюстрация аддитивной сложности

    Аддитивная и мультипликативная сложность: лазанья или лингвини

    Несмотря на аргументы в пользу того, что начинать нужно с построения прочного основания, agile методы продолжают продвигать подход "дайте сразу нечто, уже работающее". Типичным примером может служить видео, знакомящее со Scrum, где одетый с иголочки менеджер говорит разработчику, носящему красный шарф и очки пилота:

    Я могу жить с чем-то достаточно простым, лишь бы оно работало подходящим образом. Сложность может появиться и позднее.

    (рис 4.3) Итальянское лингвини – иллюстрация мультипликативной сложности

    Что можно сказать по этому поводу? Прекрасно, если вы в это верите, то удачи вам. Такая удача может и состояться, если сложность представима в виде деталей, добавляемых поочередно, одна за другой. Такой вид сложности встречается. Мы можем назвать эту ситуацию аддитивной сложностью. Она существует, когда исходная проблема проста. Пусть, например, требуется рассчитать скидки как процент от цены. При расчетах необходимо учитывать несколько специальных случаев, которые можно просто добавлять по одному.

    Но есть и другой вид сложности, которую можно назвать мультипликативной. Она существует, когда проблема изначально сложна и нельзя получить приемлемого решения, пока не будут учтены все ключевые элементы. Напомню пример, который уже приводился ранее: спроектировать интерфейс пользователя с изначальной поддержкой многоязычности достаточно просто, добавить такую поддержку в интерфейс, ориентированный на один конкретный язык, значительно труднее.

    Во всех случаях сложность связана с взаимодействием различных функций системы. Разница лишь в том, как они взаимодействуют друг с другом (если при чтении этих строк приближается время обеда, то, я надеюсь, картинки, приводимые ниже, не только разожгут ваш аппетит, но и визуализируют проблему аддитивной и мультипликативной сложности).

  • При аддитивной сложности различный функционал накладывается друг на друга подобно слоям на блюде с лазаньей. Вполне справедливо приняться вначале за первый слой и переходить к очередному слою в процессе еды.
  • При мультипликативной сложности свойства различных функционалов переплетены подобно макаронинам в блюде лингвини (или спагетти).
  • Памела Зэйв [Zave FAQ] из ATT, посвятившая большую часть своей профессиональной деятельности изучению свойств взаимодействия, начиная с телекоммуникационного ПО, пишет:

    Исторически разработчики телекоммуникационного ПО не имели эффективных способов понимания и управления взаимодействием различных его функций (feature). В результате взаимодействие функций было постоянным источником возникающих проблем, программных багов, превышения стоимости и расписания проекта, накопления неудачного опыта пользователя. Разработчики других программных систем также начали осознавать, что и у них существует проблема взаимодействия функций.

    Она приводит типичный пример:

    Рассмотрим проблему в телефонии: "вызов занятого абонента". Есть несколько способов действий в данной ситуации: переадресовать вызов, прервать разговор, попытаться позвонить позже, предложить вызывающему оставить голосовое сообщение. Предположим, что у нас есть язык описания функций, в котором проблема занятости специфицируется описанием предпринимаемого действия, условием его включения и приоритетом. Предположим также, что разработан специальный оператор, который в момент возникновения ситуации включает действие с наиболее высоким приоритетом.

    А теперь рассмотрим ситуацию, когда во время разговора двух абонентов, В1 и В2, им обоим пришли вызовы. Если у В2 приоритет выше, чем у В1, то будет выполнено действие, предусмотренное В2, а действие В1 не будет выполняться, хотя, следуя независимому описанию В1, оно должно выполняться.

    Такие случаи типичны, вот почему мы не можем просто предполагать, что мы будем "делать простейшую вещь, которая может еще выполнять работу", а затем добавлять необходимые функцию. Если так поступать, то придем к коллизиям с ранними разработками, и нам придется все переделывать. Представьте стандартный agile подход к проблеме, основанный на пользовательских историях. Истории пользователей в рекомендуемом agile стиле могли бы быть следующими:

    (История # 1) Как исполнительный директор я хочу в случае занятости переадресации вызова моему секретарю.

    Немного позже мы вспоминаем о приоритетах и формулируем еще одну историю:

    (#2) Как системный администратор я хочу иметь возможность задавать приоритеты различным действиям.

    Проходит время и появляется история:

    (#3) Как отвечающий за продажи, я хочу в случае, когда меня вызывает потенциальный покупатель, прервать мой текущий разговор, чтобы я мог сразу же ответить клиенту.

    (#4) Как ответственный исполнитель я хочу быть уверенным, что по окончании моего разговора я буду связан с вызывающим меня абонентом.

    Другие истории могут появиться позже. Все они разумны, и, как указывает Зэйв, их нельзя рассматривать независимо. Вот некоторые сценарии из четырнадцати (!) возможных, которые она приводит:

    У Боба включено свойство "переадресации вызова" – все его вызовы направляются Кэрол. Кэрол установила свойство "не тревожить". Алиса звонит Бобу, вызов направляется Кэрол, ее телефон звонит, несмотря на установленный ею запрет на звонки.

    Алиса звонит в группу продаж. Согласно установленной функциональности, вызов попадает к Бобу, телефон которого занят, абоненту предлагается оставить голосовое сообщение. Было бы намного лучше переадресовать в данной ситуации вызов другому члену группы продаж.

    Новый мобильный сервис предложен офисным работникам. Поскольку Алиса подписалась на сервис, ее офисный номер переадресуется в мобильную службу. При получении вызова Алисы мобильная служба переадресует вызов в соответствии с данными, указанными Алисой. Однако, когда данные указывают, что Алиса находится в офисе, входящий звонок впадает в бесконечный цикл.

    Это типичные примеры того, почему простой итеративный подход, начинающийся с базисной функционирующей системы, в которую последовательно добавляется новый функционал, может приводить к краху системы. И все же эта мантра остается в agile, выраженная, например, в одной из глав книги Кона [Cohn 2006] цитатой из Поппендик:

    В наши дни мы не создаем программную систему модуль за модулем. Мы создаем ее, добавляя функциональность за функциональностью.

    Для продуктов типа "все перемелется" – возможно, для сложных систем мультипликативного вида, необходим систематический подход. Такой подход включает предвидение, вероятно, дальнее предвидение, и, нравится это или нет, предваряющий анализ.

    Вера agile, что всегда можно программировать с нарастающей функциональностью, не работает для сложных систем. В этом состоит одно из главных ограничений agile подхода.

    Роль документов

    Поппендик пишет:

    Пользователя мало заботят промежуточные расходные материалы, когда ему поставляется работающая система.

    Понимая это утверждение буквально, его следует считать простой банальностью. Тинэйджера, посылающего сообщение по смартфону, "мало волнуют" документы требований и анализа, создаваемые в процессе разработки системы, позволяющей посылать такие сообщения. Но это так же справедливо и по отношению к другим артефактам, включая и сам программный код! Точно так же можно утверждать, что владельца автомобиля или собственника дома мало волнуют те расходные материалы, которые использовались в процессе создания изделия. Но из этого вовсе не следует, что эти артефакты являлись бесполезными. Вопрос не в том, что имеет значение для пользователя, вопрос в том, что значимо для разработчика, например, того, кто занимается сопровождением системы.

    Кто-то может задаться вопросом, почему Поппендик использует термин "пользователь", а не более общий – "потребитель". В данном случае это не играет большой роли. Релевантную группу составляют разработчики.

    Критика документов должна основываться на лучших аргументах. Фактически ключевым фактором является изменение. Программный продукт, как нам напоминает Agile Манифест, подвержен изменениям. Если в проекте создаются документы требований и документы проектирования, то трудно сохранить их синхронизацию с артефактом, за которым остается последнее слово, с кодом! У программных продуктов есть важная особенность, отличающая их от всех других продуктов, его уникальность связана с той скоростью, с которой он может изменяться, и с отсутствием издержек при создании копий продукта. Создатели автомобиля не могут работать без планов и документов. Разработав проект автомобиля, они запускают производство, изготавливая многочисленные копии – сами автомобили. Изменение проекта является важным решением, но не самым дорогим. Изменения в изготовлении автомобилей обходятся дороже, так как приходится изменять производственный процесс. Программные системы недаром называются софтом (soft – мягкий). Внести изменения в код нетрудно. Трудно обновлять документы синхронно с изменениями кода. И в этом заключается главная проблема документов требований, проектирования и других документов – они могут не отражать изменения, внесенные в код.

    Современная технология дает ответ на этот вызов, такой как "Принцип Единого Продукта", который (в соответствии с замыслом этой книги я избегаю обсуждать мои собственные работы) не будет здесь рассматриваться. Но и без этой техники риск изменений, приводящих к несогласованию, еще не является причиной отказа от документов.

    Что есть простота?

    Еще одна мантра, заслуживающая анализа, – это простота. В предыдущих разделах мы уже изучали специфические последствия поиска простоты: разрабатывать минимальную функциональность не более той, чем это требуют текущие запросы, разрабатывать только код и тесты. Мы видели постоянное акцентирование на "Делайте простейшую вещь, которая может еще выполнять работу" и "Это Тебе Не Понадобится". В заключение этого обзора эджайловского минимализма полезно понять, что концепция простоты может существенно отличаться от ее толкования в agile, где простота понимается как "искусство максимизации работы, которую не следует делать" [A10].

    Всякому известно, что, когда решаешь сложную задачу, первое найденное решение может быть довольно сложным и приходится потратить много сил на поиск более простого решения. Достижение простоты – это добавление работы, и иногда существенное.

    В интервью Стива Джобса, которое он дал в 1998 году Business Week, эта мысль хорошо выражена:

    Это была одна из моих мантр – фокус и простота. Просто может быть труднее, чем сложно: вы должны много поработать, чтобы мышление прояснилось, и вы нашли простое решение. Но в конечном итоге это стоит делать, поскольку, достигнув результата, вы чувствуете, что можете свернуть горы.

    Эту же мысль о совершенстве простоты прекрасно выразил Антуан де Сент-Экзюпери в "Планете людей":

    Кажется, будто все труды человека – создателя машин, все его расчеты, все бессонные ночи над чертежами только и проявляются в простоте. Словно нужен был опыт многих поколений, чтобы все стройней и чеканней становились колонна, киль корабля или фюзеляж самолета, пока не обрели первозданную чистоту и плавность линий груди или плеча. Кажется, будто работа инженеров, чертежников, конструкторов к тому и сводится, чтобы облегчить и упростить механизм крепления, уравновесить крыло, сделать его незаметным – уже не крыло, прикрепленное к фюзеляжу, но некое совершенство форм, естественно развившееся из почки, таинственно слитное и гармоническое единство, которое сродни прекрасному стихотворению.

    Как видно, совершенство достигается не тогда, когда уже нечего прибавить, но когда уже ничего нельзя отнять.

    Если бы Микеланджело согласился с тем, что простота – это максимизация работы, которую не нужно делать, он мог бы оставить нетронутой ту глыбу мрамора, из которой он высек своего Давида.

    "В каждой глыбе мрамора я вижу статую так же ясно, как если бы она стояла передо мной, стройная и совершенная в покое и движении. Я должен только разрушить стены тюрьмы, чтобы прекрасное творение предстало перед вашими глазами таким, как вижу его я".

    Прекрасно, я перестаю цитировать известных людей в разные века, выражающих одну и ту же мысль. Иначе вы обвините меня в осуждавшемся ранее приеме "доказательства, основанного на цитатах". Заметьте, однако, все эти авторы выражают фундаментальное наблюдение: достижение простоты не означает минимизацию работы. И то и другое являются достойными целями программной инженерии, но они возникают в разных контекстах и руководствуются разными принципами:

  • простота в программировании давно пропагандируется сторонниками строгих элегантных методов такими известными людьми, как Дейкстра, Вирт, Хоар, Грис Парнас; для них поиск простоты часто эквивалентен поиску простых математических моделей программы, что в корне отличается от подхода agile авторов;
  • избегать работы, которая не является необходимой, – ключевая тема в agile литературе, как мы видели; в подходе Lean это сводится к принципам: "Исключайте ненужные затраты" и "Откладывайте решение настолько, насколько это возможно".
  • Обе точки зрения хорошо известны, но не всегда изложены так, как этого хотелось бы авторам agile. В 1995 году Вирт опубликовал статью "Призыв к экономному программированию" (Plea for Lean Software). Заметьте, он использовал слово "экономный" (lean), как и авторы подхода agile. Вирт также критиковал тенденцию, характерную для ряда современных программных систем, аккумулировать бесполезные свойства и рекомендовал писать небольшие когерентные системы, отвечающие ее духу. Но какой ценой достигается простота? Вирт писал:

    Искушенный инженер знает, что бесплатный сыр бывает только в мышеловке, поэтому он спросит, что стоит за этой экономией? Ответ прост – ясный концептуальный базис и хорошо продуманная подходящая структура система.

    Если ядро или любой другой модуль должен допускать успешное расширение, то его проектировщик должен понимать, как он будет использоваться. На самом деле наиболее сложный аспект проектирования системы состоит в декомпозиции на модули. Каждый модуль является частью точно спроектированного интерфейса, специфицирующего импорт и экспорт модуля.

    Другими словами: следует все хорошо продумать и постараться сделать это как можно раньше.

    4.4.5 Принятие изменений

    Мир и наше восприятие мира меняются; это же справедливо и по отношению к требованиям к системе. Непосредственное включение потребителей в проект ведет даже к большим запросам на изменение.

    Манифест Agile не только допускает изменения, но и приветствует их. Это преувеличение. Справедливо, что изменения – это нормальная практика в разработке ПО, другое дело – приветствовать появление изменений. Для сравнения рассмотрим службу резервации мест в гостиницах booking.com, которая позволяет клиентам изменять сроки резервирования, не штрафуя их. Но трудно себе представить, чтобы служащие компании, приходя по утрам на работу, с нетерпением ожидали бы, чтобы как можно больше клиентов изменили сроки проживания. Разумная политика допускает изменения, но каждое изменение становится причиной дополнительных забот.

    Точным признаком того, что методы agile принимают, но не "приветствуют" изменения, как сказано в манифесте, является то, что на практике изменения ограничиваются. Scrum, например, имеет строгое правило (будем называть его правилом закрытого окна) – запрет для владельца продукта и, тем более, для любого другого лица вносить изменения в период спринта – проведения сессии разработки.

    При всех абсурдных нападках на традиционные методы и "водопад" в данном случае Scrum ведет себя в полном согласии со стандартной мудростью программной инженерии. В противоположность карикатурному изображению, которое можно найти в agile текстах, в литературе по программной инженерии давно отдается должное необходимости изменений в процессе жизненного цикла. Просто утверждается, что изменениями следует управлять подходящим образом. Только наивная команда может принимать, или даже приветствовать неограниченные изменения в любое время – в такой ситуации вряд ли удастся поставить систему.

    Энтузиазм принятия изменений во многом объясняется обратной реакцией – освежающим уходом от закостенелой ментальности ряда менеджеров, признающих строгое следование предписанному процессу, для которых хорошие требования – это замороженные требования и которые рассматривают запросы на изменение как неприятность и нарушение порядка. Распространение идей agile сыграло значительную роль в изменении этой точки зрения.

    Говорят, что программная система, которая может легко изменяться, обладает свойством расширяемости. Вряд ли это свойство можно назвать новой концепцией. Фактически это одна из центральных тем, обсуждаемая в течение десятилетий. Манифест прав, внедряя в сознание важность и полезность этого свойства. Но проблема эта не столько психологическая, сколько техническая. Какие методы можем мы применять, чтобы быть уверенным, что нам не придется откатываться назад, начиная все с нуля, когда потребитель изменит свой взгляд или изменятся некоторые свойства проблемной области?

    Подход agile выдвигает одну важную идею, дающую вклад в продвижение расширяемости в практику. Такой идеей является правило XP – экстремального программирования, которое говорит, что каждая часть функциональности должна иметь ассоциированный с ней тестовый случай. Одна из неприятностей изменений в том, что велик риск нарушения корректной работы других функций системы, особенно, когда проблема обнаруживается поздно. Когда же после каждого изменения появляется возможность проведения регрессионного тестирования, риск существенно уменьшается.

    Однако, помимо этого правила, методы agile мало помогают в решении проблемы расширяемости, фактически продвигая приемы, которые затрудняют ее решение. В своих намерениях аджилисты гордо говорят "добро пожаловать" изменениям, но когда дело доходит до технических решений, они часто с пренебрежением относятся к идеям, помогающим производить расширяемое ПО.

    Примером такой идеи, фактически полноценного метода разработки ПО, спроектированного для поддержки расширяемости, является объектно-ориентированное программирование. Его инструменты – абстракция, скрытие информации, наследование, универсализм, полиморфизм, динамическое связывание и другие конкретные механизмы непосредственно спроектированы для облегчения изменений ПО. Поппендик [Poppendieck 2010] утверждает, что ООП этой цели не достигает:

    В то время как в теории ОО разработка создает код, который просто изменять, на практике ОО системы также трудно менять, как и любые другие, особенно тогда, когда скрытие информации плохо понимается и неэффективно используется.

    ООП критикуют, но ничего лучшего не предлагают. Сам комментарий вызывает недоумение: о каком виде ОО разработки идет речь без подходящего использования скрытия информации – одной из важнейших характеристик метода? Любой подход даст плохие результаты, если его используют люди, глубоко не понимающие его основ или неэффективно его применяющие. Можно ли отвергать идею использования автомобилей, ссылаясь на существование плохих водителей? Или можно ли отрицать метод Lean, предлагаемый авторами, если кто-то будет неправильно его применять. Еще один пример странной agile логики.

    Проблема agile не ограничивается такими снисходительными и алогичными комментариями. Некоторые agile практики и принципы напрямую вредят расширяемости. Наиболее поразительный пример – это кампания за минимизацию: стройте только необходимый продукт. Мантра YAGNI (Это вам не потребуется) призывает не включать код, который прямо сейчас не нужен – это лишние траты. Вас убеждают, что не следует пытаться рассматривать наиболее общий случай, а вместо этого программировать здесь и сейчас. Этот подход несовместим с поддержкой изменений. Разработчики, заботящиеся об изменениях, должны пытаться предвидеть развитие и всюду, где возможно, строить больше, чем то, что предлагается сию минуту, предвосхищая будущие изменения.

    Аджилисты, кажется, не замечают противоречий между почетными амбициями поддержки изменений и принципами и практиками, препятствующими этой поддержке.

    Как и в других случаях, agile критика некоторых общих практик корректна: программистам не следует стремиться включать необоснованные и непроверенные обобщения. Но отсюда не следует, что программисты в профессиональной практике не должны стремиться рассматривать если не наиболее общий случай, то хотя бы случай более общий, чем тот, что отвечает сегодняшним потребностям.

    Помимо негативного эффекта подобных советов, не учитывается главное – природа изменений зачастую носит архитектурный характер, что требуется проектировать архитектуру, допускающую изменения. Хорошие учебники учат, как это делать, но стиль предваряющего анализа – это то, что отвергается аджилистами.

    Защита изменений, провозглашенная в Манифесте agile, – это правильная цель, но только цель.

    4.5 Технические принципы

    Мы переходим к рассмотрению программно-специфических приемов, составляющих суть agile подходов.

    4.5.1 Вести разработку итеративно

    Разработка agile – это итеративная разработка. Аджилистам не хватает терпения на процессы, характерные для "водопадного" стиля – практикуемого или воображаемого, когда недели или даже месяцы отводятся на разработку требований и проектирование, прежде чем начнется создание кода. С точки зрения agile доказательством пудинга является сам пудинг – то есть код. Его нужно поставлять часто и как можно раньше.

    Выполнять частые рабочие итерации

    (рис 4.4) Итеративный подход – вертикальная декомпозиция

    Доводы в пользу итеративной разработки в литературе по программной инженерии приводятся давно, начиная со статьи Базили [Basili 1975]. Такая разработка может принимать различные формы. Итеративный процесс может, например, производить последовательные подсистемы или кластеры будущего продукта, каждый из которых фокусируется на технологическом слое, входящем в финальную систему. Здесь могут быть кластер живучести (база данных), сетевой кластер, кластер бизнес-логики, кластер пользовательского интерфейса. Такой итеративный подход можно называть "вертикальной декомпозицией".

    (рис 4.5) Итеративный подход – горизонтальная декомпозиция

    Но это не совпадает с понятием agile об итеративной разработке. Декомпозиция agile "горизонтальная" – каждая итерация должна представлять работающую систему.

    Система может предлагать, особенно вначале, только небольшое подмножество полных механизмов, например, часть базы данных может быть примитивной, даже просто "заглушкой" (держатель места – модуль, моделирующий будущую функциональность). Но в противоположность вертикальной декомпозиции это должна быть функционирующая система, которая обеспечивает результат, отвечающий опыту конечного пользователя, так чтобы представители потребителей, включенные в команду, как можно раньше могли установить обратную связь.

    Различие между горизонтальной и вертикальной декомпозицией связано с различиями между мультипликативной и аддитивной формами сложности. В присутствии мультипликативной сложности (лингвини) разумно вначале установить архитектурный базис, общий для всех функций, который поможет распутать зависимости, возникающие между функциями. Для аддитивной сложности (лазанья) подходит горизонтальный процесс, при котором функциональность добавляется поочередно. Такая схема продвигается в agile разработке.

    Длительность итерации

    Во всех agile подходах предполагается, что итерации должны быть короткими, типично – несколько недель. Они отличаются лишь конкретными сроками. Scrum называет итерации спринтами и часто (хотя это не носит универсальный характер) отводит четыре недели на спринт.

    Я счел полезным в собственной разработке следовать рекомендациям Scrum, но в качестве срока спринта выбрал календарный месяц. Говорить "октябрьский релиз", указывая конкретный ключевой срок, просто и понятно – конец месяца. Разница в днях между месяцами несущественна. Фактическое время разработки всегда короче, поскольку требуется оставить время в начале и конце месяца на планирование спринта и подведение итогов – обзор спринта.

    Такая итеративная разработка состоит из временных блоков: длительность итерации жестко фиксируется и не может изменяться. Если в конце отведенного времени окажется, что запланированная функциональность не полностью реализована – не соответствует критерию "сделано", то она либо переносится на следующую итерацию, либо удаляется, но контрольные сроки не изменяются.

    Принцип временных блоков более важен, чем точная длина итерации. Так как изменение контрольных сроков характерно для мира программных разработок, может понадобиться время, чтобы убедить команду, что контрольные сроки жесткие и, если что-то приходится менять, то это должна быть функциональность, а не контрольный срок. Как только это правило принимается всерьез, оздоровляющий эффект распространяется на весь проект. Прогнозы становятся более реалистичными, так как разработчики знают, что у них нет времени в запасе, а потребители понимают, что нет смысла выдвигать нереализуемые цели.

    Мой опыт показывает, что это правило имеет эффект стимулирования команды. Хотя в принципе возможно удаление функциональности, но на практике этого не происходило. Поскольку удаление функционала создает плохое впечатление, разработчики, будучи уверенными, что план должен выполняться, прилагают все силы для своевременной реализации обещанных функций.

    Аджилисты иногда рассматривают природу временных блоков итераций как повод для отказа от одновременного выдерживания сроков и поставки требуемой функциональности в разворачиваемом релизе системы. Конечно, это не повод – внешние требования пользователя по-прежнему остаются. Анализ ошибочного мнения "либо что, либо когда" будет рассматриваться при обсуждении вопроса о переходе к agile.

    Замораживание требований на период итераций

    Методы agile, как мы знаем, выступают за "изменения", но в любом реалистичном подходе изменениями следует управлять. Здесь мы сошлемся на строгое правило Scrum: функциональность может быть добавлена только на фазе планирования спринта. Как только спринт стартовал и команда приступила к реализации выбранных функций, ничто не может меняться до окончания спринта. Запрет строгий, применяется ко всем в проекте и вне его, включая менеджеров.

    Эта идея встроена в виде правила "закрытого окна", которое мы еще будем рассматривать при детальном изучении понятия "спринт". Это один из наиболее интересных вкладов Scrum.

    Итеративная разработка. Оценка

    Следует отдельно оценить каждую из двух рассмотренных идей – частоту итераций и требование замораживания спринта. Наиболее важное свойство частых работающих итераций в том, что они частые. За прошедшие десятилетия индустрия ПО уже поняла, что подход "Большого Взрыва", когда различные команды работают над своими раздельными частями проекта, а спустя несколько месяцев пытаются объединиться, не работает. Расхождения трудно фиксируются; люди делают несогласованные предположения об остальных частях системы, и позже их с трудом удается примирить. По этой причине Майкрософт ввела режим ежедневного построения системы, когда компиляция и запуск системы производятся каждую ночь (Build должен работать!). Если кто-то внес изменения, приводящие к ошибке, изменения отвергаются, а виновник, как правило, не прекращает работу, пока не исправит ошибку. Цикл разработки, основанный на разработке итераций, выпускаемых каждые несколько недель, становится повседневной нормой. В этом немалая заслуга подхода agile, популяризирующего эту идею.

    Что можно сказать по поводу того, что частые итерации должны быть работающими? Здесь есть нюансы. Ранее в этой главе мы говорили о негативных последствиях требования существования работающей системы на каждом шаге и отказа от итераций, целью которых является построение инфраструктуры. Хорошая инженерия требует прочного основания. Компетентный менеджер иногда просто отказывается смотреть на то, что уже работает, или делает вид, что работает. Вместо этого он будет строить технологическое ядро, которое обеспечит эффективность и масштабируемость всего проекта. Настаивание на работающей системе на каждом шаге может быть напрасной тратой ресурсов и безответственной политикой.

    Когда идет строительство дома, то долгое время застройщики мало что могут продемонстрировать будущим жильцам. Они работают над фундаментом, прокладкой коммуникаций, над всем тем, что гарантирует устойчивую жизнь дома. Каждое утро вы проезжаете мимо и думаете: "Черт возьми, что они делают все это время? Я совсем ничего не вижу!" Затем в один из дней вы замечаете нечто похожее на начало строительства дома, и с этого момента процесс идет удивительно быстро, поскольку настоящая основа была заложена.

    Представьте себе, что строительство шло бы в духе agile. Сразу же, с первой итерации пользователю нужно было бы показать нечто, напоминающее дом. Нам нужны были бы пол, потолок и стены. Возможно, и крыша. Правда, если строительство шло летом, то крышу можно было бы отнести на следующий спринт. Ах, да, не работает канализация – исправим. Нет электричества – проведем. Забыли про фундамент – ничего, сдвинем постройку на метр, выкопаем яму, зальем фундамент и поставим дом на место.

    Если все это происходит в Южной Калифорнии, следует ли думать о сейсмоустойчивости дома или отложим эту проблему для рефакторинга? Тем более что "пользователи" – будущие жильцы – о землетрясениях и не думали, последнее большое землетрясение было сто лет назад! Согласятся ли они с лозунгом YAGNI – "Вам Это Не Понадобится"?

    Все знают, что программная инженерия отличается от инженерии материальных конструкций. Существуют отличия, но есть и сходство! Предвидеть и анализировать, заботиться об инфраструктуре показательно для всех видов инженерии. Стремление поставлять работающую систему на каждом шаге может приводить к тяжелым последствиям.

    Снова мы видим важность понимания agile угроз, связанных с неограниченным обобщением. Понимание того, что разработчики могут увлечься деталями технологии и забудут про картину в целом, забудут о потребителе, который подписывался не на технологию, а на решения. Забудут, что потребителю нужна система. Всегда есть компромисс между тем, как много может делать система и когда она станет доступной. Система, которая появляется рано, но не масштабируется должным образом, плоха. Но это справедливо и для системы, обещающей совершенство, но всегда только обещающей.

    Порядок задач

    При любой итеративной разработке возникает вопрос, как в рамках итерации команда определяет порядок выполнения работ. XP был первым, кто дал ответ в agile: начинайте с "простейшей вещи, которая может еще работать".

    Все agile подходы продвигают подобную идею. Кокбурн [Cockburn 2005], критикуя стратегию "Сложную Вещь – Первой", пишет:

    [1] Если команда терпит неудачу при поставке системы, то у спонсора нет понимания причины неудачи. То ли команда не способна выполнить этот проект, то ли выбрана неверная технология, то ли процесс ошибочен? В добавление к этому члены команды могут начать спорить друг с другом и впасть в депрессию.

    Он предлагает начинающим и опытным командам соответственно следующую стратегию:

    [2] Команде, которая не работала вместе, столкнулась с новой задачей, с новой технологией, я предлагаю "Простейшую Вещь – Первой, Самую Трудную – Второй". Команда и спонсоры узнают вкус ранней победы. Если самая трудная задача превосходит возможности команды, то второй задачей должна быть трудная задача, с которой может еще справиться команда.

    [3] Когда риск и вероятность технических ошибок уменьшится, то лучшей стратегией является "Важнейшую по Значимости – Первой".

    Рекомендация [2] понятна. На ПО переносится очевидное наблюдение, что команда начинающих альпинистов не может начинать с Эвереста, а новый оркестр с "Весны Священной" Стравинского. Но выгоды предлагаемой стратегии относятся к команде, а не к проекту. Что если самая трудная вещь, отложенная вначале, вне возможностей команды? Тогда ранние успехи окажутся напрасной тратой сил, и ранний успех создаст обманчивое представление. В этом случае можно привести те же аргументы, что приведены в [1]:

    Если команда успешна в поставке "Простейшей Вещи" или "Труднейшей Вещи, с которой она еще может справиться", то у спонсора нет оснований судить, на чем основан успех. То ли команда достаточно хороша, чтобы выполнять подобные вещи, то ли технология приспособлена для решения подобных задач, то ли процесс допускает масштабирование. В добавление к этому члены команды могут возгордиться и начнут поздравлять друг друга, не осознавая, что истинные вызовы еще впереди.

    Для сплоченной команды Кокбурн рекомендует стратегию [3] – начинать с наиболее значимой задачи. Это обычная agile рекомендация, характерная, в частности, и для Scrum. Такие рассуждения могут приветствоваться многими менеджерами, но стратегия может оказаться безответственной. Продукт успешен, если он предлагает не одно важное свойство, а совокупность поддерживающих свойств. За элементом с высочайшим приоритетом следует элемент с высоким приоритетом, и эта череда приоритетов продолжается. Что если первый по приоритету реализован великолепно, а остальные плохо? Начальное восхищение скоро сменится раздражением и недовольством.

    Снова нет единого решения. Следует заметить, что условия часто различаются в начале проекта и в последующих фазах. Эти наблюдения привели к идее дуальной разработки.

    Дуальная разработка

    Разрешение компромисса между инфраструктурой и функциями, допускающими понятную пользователю визуализацию, является одной из центральных проблем разработки ПО. Здесь нет простых рецептов, таких как "поставляйте на каждой итерации работающую систему, обобщение и прочные основы придут со временем". Однако и другой экстремальный рецепт – "вначале стройте совершенное ядро системы" – не является лучшим во всех ситуациях. Единственная политика, которая, как я видел, работает, учитывает различия между ранними и поздними фазами проекта.

  • Ранняя фаза – инфраструктура является ключом. В то время как макеты, опытные образцы, прототипы могут быть полезными для моделирования опыта конечного пользователя, главными остаются глубокий анализ фундаментальных ограничений системы, разработка проектных решений, гарантирующих конечный успех, а не только успех первых поставок. На этих фазах нужно заложить основы расширяемой и масштабируемой системы, архитектура которой допускает рост и адаптацию. Все остальное следует рассматривать как отвлечение.
  • Если на этом этапе консультант скажет вам, что невозможно принимать такие решения, поскольку agile методы требуют начинать построение с "простейшей вещи, которая еще работает", то единственная реакция, которая имеет смысл, – "следует покончить с консультантом". Макет может построить любитель. Профессионал – это тот, кто знает, как находить фундаментальные решения в условиях неполной информации, знает, какие решения имеют право "первого времени".

    (рис 4.6) Плакат заключительных этапов дуальной разработки
  • Поздняя фаза – ключевые решения уже приняты, и инфраструктура в основном построена. В этой фазе риск, отмечаемый сторонниками agile, становится заметным. Проект может превратиться в стерильную бесплодную разработку, фокусирующуюся на совершенстве внутренних деталей, а не на предоставлении ценностей, важных для пользователя. Пришло время, когда следует сосредоточиться на регулярной поставке работающих систем. Начать нужно с того, что в комнате разработчиков повесить большой плакат:
  • Ежемесячные спринты, построенные на основе инфраструктуры, разработанной на начальных фазах, теперь поставляют работающие экземпляры системы. В обычном распорядке дня – тесные контакты с представителями пользователей, информирование их о том, что они еще могут получить, и выслушивание их пожеланий.

    Эти два подхода вовсе необязательно применять последовательно, они могут использоваться и параллельно. Если у вас нервный заказчик, тревожащийся и желающий увидеть все как можно раньше, то часть команды может строить фундаментальную архитектуру, а другая – поставлять функциональность, которая непосредственно работает. Эту работу можно рассматривать как создание прототипов и проведение экспериментов для проверки различных возможных решений. Две части снабжают информацией друг друга. Из экспериментов можно извлечь полезные уроки для построения фундаментальной архитектуры. Из архитектуры могут быть взяты полезные куски, которые можно использовать в специальных функциях. Конечно, такая политика в какой-то мере избыточна, команда должна быть готова отбросить прочь неудовлетворительные пробы функциональности, когда появляются более совершенные разработки. Но это дает возможность с самого начала получать результаты, допускающие визуализацию, не принося в жертву системную целостность, расширяемость и масштабируемость.

    Такую комбинацию подходов – последовательную или параллельную – мы называем дуальной разработкой.

    4.5.2 Рассматривать тесты как ключевой ресурс

    Стремление к созданию качественного продукта – сердце программной инженерии. Достаточно просто приводить доводы в пользу качества, значительно труднее найти способы, обеспечивающие это качество. Есть разные приемы в борьбе за качество, например, подход CMMI направлен на улучшение управления проектом. Другие способы являются техническими и включают, например, формальные (математические) методы спецификации и верификации. Для большинства в индустрии и для agile методов принципиальным техническим способом обеспечения качества является тестирование.

    В компьютерном сообществе тестирование имеет особый статус. Почти все знают высказывание Эдсгера Дейкстры: "Тестирование позволяет доказать существование ошибки, но оно не может гарантировать отсутствие ошибок". Как следствие, кажется, что тестирование не столь уж и полезно. Но, с другой стороны, все проводят тестирование своих продуктов, и почти каждый не применяет других приемов верификации.

    Дейкстра имел в виду, что тестирование не может быть исчерпывающим средством, фактически оно может покрывать лишь малую часть всех возможных случаев.

    В качестве примера он приводил умножение 32-разрядных целых. Чтобы проверить корректность выполнения операции умножения, полная система тестов включает 264 варианта, что выходит за пределы возможностей не только человека, но и компьютера.

    С другой стороны, нормальная реакция, по крайней мере, моя реакция, когда я слышу, что предлагается средство обнаружения ошибок: "Да! Ого! Дайте мне его скорее!" Конечно, мы хотим найти все "ошибки", до которых могут дотянуться наши руки.

    Методы agile рассматривают тесты как основной ресурс каждого проекта. Ресурс, содержащий набор регрессионных тестов. В этот набор включаются все тесты, которые обнаруживали ошибку в некоторой точке проекта. Как следует из названия теста, целью является предотвращение феномена, известного как "регрессия", когда найденная и, казалось бы, исправленная ошибка вновь появляется в процессе дальнейших модификаций системы. Старые "баги" вновь появляются. Причины этого явления могут быть различными. Чаще всего исправление ошибки устраняет симптом, а не причину болезни. Пока причина болезни не будет устранена, ее симптомы могут проявляться снова и снова. Как бы там ни было, риск регрессии – одна из причин, по которой каждый проект хранит набор регрессионных тестов, включая в него каждый тест, обнаруживающий ошибку в любой точке проекта.

    Развитие этой идеи поддерживается современным инструментарием, позволяющим программисту:

  • описывать каждый тест простым сценарием, указывая конфигурацию тестирования, входные данные, утверждение – оракул, описывающее ожидаемый результат прохождения или краха теста;
  • выполнять некоторое подмножество или полный набор регрессионных тестов автоматически.
  • Эта комбинация функциональностей обычно называется "автоматическим тестированием". Данный термин является некоторым преувеличением, так как такое тестирование не покрывает наиболее тонкие и требующие временных затрат процессы генерирования тестов, описания оракула. Тем не менее, соответствующие инструменты, пионером среди которых был инструментарий JUnit, изменили практику программной разработки и сделали возможным акцент agile на необходимость включения в проект набора регрессионных тестов и запуска набора по нажатию одной кнопки.

    Следующие два принципа расширяют эту фундаментальную роль тестов в мире agile.

    4.5.3 Не начинайте новую разработку, пока не пройдут все тесты

    Наиболее конкретным результатом того внимания, которое в agile уделяется качеству, является то, что предпочтение отдается целостности того, что уже создано, а не добавлению новой функциональности, – качество важнее функциональности.

    Дилемма, знакомая каждому менеджеру, – список задач большой и только растет, но не все, что реализовано, корректно работает. Где взять ресурсы? В проектах agile решение принимает группа, а не отдельное лицо, но проблема все равно остается. Подход agile ясен: не идти дальше, пока не пройдут все тесты.

    Подход похвальный, но жизнь иногда заставляет вносить коррективы в простые схемы. В частности, баг багу рознь (все жучки различны). Тест, который ломает систему, может выявлять серьезную проблему, так что дисциплина agile права: двигаться дальше не следует до решения проблемы. Но иногда в случае бага речь может идти о нарушении функциональности, не играющей важную роль в текущем состоянии. Удаление функциональности решает проблему, но удаление – деликатная задача, не только требующая времени, но и порой приводящая к появлению новых багов. Можно схитрить, изменить спецификацию и сказать, что это не баг, а функция, которая работает должным образом. Это не помогает в долгосрочном плане, так как корректная версия все равно понадобится.

    Любой серьезный проект по этим причинам имеет классификацию багов, разделяя их на категории: блокирующие систему, серьезные ошибки, незначительные помехи. В серьезном проекте должна быть задана политика поведения, когда регрессионный тест не проходит. Обычно при блокирующих или серьезных ошибках новые разработки не включаются, при незначительных продвижение возможно.

    4.5.4 Вначале создайте тест

    Наиболее спорным принципом является идея создания тестов, предшествующих разработке, ассоциируемая с XP и применяемыми ключевыми практиками этого подхода – разработка, управляемая тестами, и разработка с предшествующим тестом. Мы еще подробно обсудим эти практики, а пока рассмотрим ключевую идею.

    Идея принципа очевидна и следует из буквально понимаемых слов: "вначале создайте тест", то есть не пишите код, пока не напишете тесты, проверяющие корректность работы кода. В этом подходе тесты заменяют написание точных спецификаций – идея довольно естественная. Но в экстремальном программировании принцип "вначале тест" понимается более широко. Бек [Beck 2005] описывает это так:

    Напишите падающий автоматический тест перед изменением любого кода.

    Некоторая функциональность еще не представлена, и вы хотите добавить ее. Вместо того чтобы поступать так, как принято в классическом подходе, – определять требования, напишите тест к коду и, в этом состоит удивительная часть реализации принципа, запустите тест, добавив его в набор регрессионных тестов. Поскольку тест есть, а кода нет, то тест рухнет. После этого напишите код и добейтесь прохождения теста. Таким образом, создаваемые тесты автоматически попадают в регрессионный набор.

    Наиболее важный аргумент в пользу программирования с предшествующим тестом (вновь по словам Бека) – такой подход позволяет избежать расползания системы, производство кода, реализующего функциональность, которая то ли понадобится, то ли нет. Помните принцип YAGNI. Подход "вначале тест" увеличивает стоимость создания нового кода, так как теперь предварительно нужно написать еще тест, придумать сценарий использования новой функциональности. Если это трудно, то, возможно, вы решите, что эта функциональность не так уж и необходима, и не станете создавать код сомнительного качества.

    Принцип "вначале тест", играющий важную роль в XP, является предметом серьезной критики, поскольку тесты заменяют спецификации, с чем не все согласны. Но хочется отметить важный вклад этой идеи agile: никакая функциональность не добавляется без предварительного написания теста, поэтому отсюда вытекает важное свойство таких проектов – нет кода без тестов.

    4.5.5 Выражайте требования через сценарии

    Agile отрицает необходимость "предваряющего анализа". Но разработка нуждается в требованиях и в подходах agile подчеркивается необходимость создания продукта, отвечающего пользовательским ожиданиям, поставляющего определенные ценности бизнесу.

    В предыдущих разделах мы видели часть ответа agile на требования – интеграция постоянного тестирования в цикл разработки. Так как тесты не могут полностью заменить требования, необходимо нечто большее. Основной прием, заменяющий требования в подходах agile, представляет более абстрактную версию тестов – использование вариантов и пользовательских историй. Оба элемента описывают типичные сценарии взаимодействия пользователей и системы.

    Вариант (case) обычно описывает полный проход через систему, например сценарий заказа продукта на сайте. Варианты обладают высокой степенью гранулярности.

    Пользовательская история описывает элементарную единицу взаимодействия – с меньшей степенью гранулярности. Стандартная схема описания пользовательских историй (примеры появлялись ранее в этой главе), принятая в мире agile:

    Как <роль я хочу <действие> для достижения <цель>.

    Последняя часть может быть опущена. Например, при создании графической игры:

    Как игрок я хочу, чтобы все части фигуры, приносящей выигрыш, мигали или подсвечивались, чтобы я мог видеть эту фигуру.

    Далее будем использовать термин "сценарий" как для вариантов, так и для пользовательских историй.

    Принцип использования сценариев для спецификации требований является широко используемой практикой agile и одной из наиболее опасных практик. Спецификации задают общее описание, варианты и пользовательские истории, как и тесты, специфичны. Десять пользовательских историй – это десять случаев, в которых отсутствует абстракция, характерная для спецификации. Если я скажу вам, что моя функция на входе 1 возвращает значение 1, на входе 2 возвращает значение 4, на входе 3 возвращает значение 9, на входе 4 возвращает значение 16, то я ничего не сказал о других значениях.

    У меня был интересный опыт общения с графической программой, строящей по заданным значениям функцию, наиболее приближенную к введенным значениям. Помимо указанных значений, я задал еще точку (5, 25). К моему удивлению, помимо ожидаемого значения 36 для точки 6, возможными были значения 34 и 35,6. Подробности можно посмотреть в моем блоге [Meyer 2012].

    С другой стороны, если бы я сказал вам, что моя функция $$f(x)$$ это $$x2$$, то это была бы точная спецификация, снимающая все вопросы.

    Угроза использования сценариев вместо спецификации требований понятна. Все хорошо, когда пользователи не выходят за пределы предусмотренных заранее вариантов, но все ломается, если возникает непредусмотренный вариант.

    Типичный пример, с которым я недавно столкнулся, связан с владелицей небольшого бизнеса, которая общалась с пенсионной системой. Система позволяла построить пенсионный план либо для членов, либо для администраторов, а она была и тем и другим, что не предусматривалось сценариями.

    Традиционная выработка требований заставляет вас идти от специфического к общему, абстрагируясь от частных случаев. Конечно, нет гарантии, что будут охвачены все случаи, но само понятие написания спецификации требований заставляет вас, по крайней мере, попытаться описать проблему и рамки ее решения или, используя лучшую терминологию Джексона, задать машину и область (смотри раздел 3.2.5).

    Вернуться к учебному плану