Данная статья является переводом статьи SAFe «Story». Оригинал, на мой взгляд, содержит достаточно полное описание того, что же представляет из себя пользовательская история (user story) и зачем SAFe вводит такую сущность, как вспомогательная история (enabler story). Фактически это такой концентрат знаний о этой сущности фреймворка SAFe.
Однако работа в разных командах показала, что для правильного усвоения этого концентрата нужны дополнительные знания, такие как теория управления требованиями, архитектурные принципы и подходы и т. д. Поэтому помимо переводя я решил дать свои комментарии, поясняющие некоторые спорные моменты использования этого элемента бэклога. Эти спорные моменты выявлены на основании опыта и связанны именно с разработкой программного обеспечения. Надеюсь, перевод и мои пояснения к нему позволят командам разработки более осмысленно пользоваться этим инструментом.
Для понимания, где перевод, а где мои измышления, я буду использовать разные визуальное представление текста. Вот так выглядят примечания переводчика.
А так — перевод оригинальной статьи.
N.B. Мои комментарии касательно историй относятся только к области разработки программного обеспечения. Скажем так, та история, про которую я говорю в этой статье, является наследницей (в терминах ООП) более общей истории, про которую говорит SAFe. Соответственно, она более специфична, чем история SAFe и многие критерии, которые я применяю к истории-наследнице, будут неприменимы к истории-родительнице, но при этом все правила, относящиеся к базовой истории, полностью применимы к истории-наследнице (фактически, рассказал про принцип подстановки Лисков). Но они помогают незрелым командам более подробно разобраться с понятием история и начать её применять в работе.
Истории ведут себя как «пиджин»,
позволяя обеим сторонам
договориться в достаточной мере,
чтобы эффективно работать вместе.
Билл Уэйк, соавтор экстремального программирования
Определение: Истории — это короткие описания небольшой части требуемой функциональности, написанные с точки зрения пользователя.
Agile-команды реализуют истории в виде небольших вертикальных фрагментов функциональности системы, которые можно завершить не более чем за несколько дней.
Истории — это основной артефакт, используемый для определения поведения системы в Agile. Это короткие, простые описания функциональности, рассказанные с точки зрения пользователя и написанные на его языке. Каждая история реализует небольшой вертикальный срез поведения системы.
Истории предоставляют достаточно информации для представителей бизнеса и технических специалистов, чтобы понять общую суть изменений. Детали откладываются до того времени, когда история будет взята в реализацию. Благодаря критериям приемки и приемочным тестам истории становятся более конкретными, помогая обеспечить качество системы.
Пользовательские истории (user story) описывают функциональность, используемую непосредственно конечным пользователем. Вспомогательные истории (enabler story) позволяют вытащить на свет работы, необходимые для обеспечения исследований, построения и развития архитектуры, создания и развития инфраструктуры и соответствия регуляторным и отраслевым требованиям.
Предлагаю по частям разобрать определение термина история. Начнем с того, что это описание, данное с точки зрения пользователя. Что это означает?
Часто термин пользователь понимается буквально — это человек, который пользуется функциональностью системы через пользовательский интерфейс (UI).
SAFe является фреймворком масштабирования гибких принципов на большие команды, а большие команды обычно создают большие решения. В составе таких решений могут быть системы, у которых вообще может не быть пользователей-людей, а их функциональностью пользуются только другие системы. Можно ли для фиксации описания функциональности такой системы использовать пользовательскую историю?
Разумеется, можно. Теория работы с требованиями гласит, что пользователями системы могут быть как пользователи-люди, так и другие системы, и даже какие-либо аппаратные устройства.
Фактически, используя термин «пользователь» мы определяем границу нашей системы. «Пользователь» — это любая сущность, находящаяся за пределами нашей системы и использующая её для решения своих задач.
Таким образом мы применяем пользовательскую историю для описания любого взаимодействия извне с нашей системой.
Далее рассмотрим следующую часть формулировки — описание требуемой функциональности. Следует ли это понимать, что история — это требование к функциональности системы? И да, и нет. Да — потому что мы действительно фиксируем информацию о том, что должна делать система по мнению пользователя — это требование уровня пользователя. Нет — потому что детали пользовательского взаимодействия — как именно пользователь получит ожидаемый результат, — отсутствуют в формулировке истории, т. е. мы нарушаем такой критерий качества требования, как полнота.
Также мы нарушаем другой критерий качества требования — атомарность. Часто реализация требования целиком может занять более одной итерации разработки, тогда как ограничение на срок реализации истории — спринт. Для того чтобы попасть в прокрустово ложе временных ограничений реализации функциональное требование уровня пользователя может быть разделено на несколько пользовательских историй, которые мы можем реализовать в течение заданного срока.
Поэтому, хотя пользовательская история и фиксирует потребность в создании/изменении функциональности системы, т. е. имеет определенные черты требования, требованием в классическом понимании она не является.
Хотелось обратить внимание еще на один момент — история есть вертикальный срез функциональности системы. Это означает, что вносимые изменения проходят через все архитектурные слои системы так, чтобы история представляла собой законченную реализацию и могла нести ценность для пользователя. Например, есть форма заказа в интернет-магазине из трех страниц, где на первой мы подтверждаем список выбранных товаров и их количество, на второй — указываем адрес и выбираем способ доставки, а на третий — выбираем способ оплаты и оплачиваем покупку. Нельзя механически разделить эту историю на три, по числу страниц, как это иногда предлагают коучи по Скраму. Если вы бездумно сверстаете одну страницу подтверждения списка товаров в корзине, она не будет иметь реализации оформления заказа и, как следствие, не будет нести ценности для клиента магазина. Но если вы сделаете заказ на одной странице, где укажете номер телефона клиента, чтобы с ним мог связаться менеджер магазина и помог бы оформить доставку и сделать оплату — это был бы возможный вариант декомпозиции. Обязательным требованием является законченная реализация функциональности в рамках системы.
И последний момент. По тексту я всегда искусственно ограничиваю действие пользовательской истории контекстом одной системы. Это удобно для организации работы команд. Ранее я подчеркивал, что SAFe ориентирован на команды, которые могут создавать решения, границы которых больше одной системы. В таких решениях возникает ситуация, когда пользовательская ценность доступна только тогда, когда изменения сделаны в нескольких системах. Как-будто, соблюдая предыдущее замечание о законченности функциональности, мы должны продлить историю на все решение, на несколько систем. Но в данном случае мы нарушаем принцип снижения связанности или зацепления (coupling) компонентов решения и порождаем зависимость команд разработки друг от друга. Удобным в работе, на мой взгляд, является ограничение границ истории одной системой, что в большинстве случаев позволяет реализовать её силами одной команды в рамках одной итерации разработки, не создавая преград для движения команды и не накапливая в бэклоге нереализованные элементы. Итак, примем, что пользовательская история ограничена границами системы.
Детали
Для начала расскажем про концепцию контракта.
Определение: Контракт — формальная, точная и верифицируемая спецификация ожидаемого поведения компонента ИТ-инфраструктуры, включающая предусловия, сигнатуры доступных действий, описание результата, постусловия, инварианты, а также описание реакции на различные нештатные ситуации. Контракт фиксирует ожидание стороны-клиента и обязательства стороны-сервера при организации взаимодействия (интеграции) компонентов ИТ-инфраструктуры. Контракт управляет взаимодействием компонента ИТ-инфраструктуры с внешним миром.
Под компонентом ИТ-инфраструктуры в контексте данной статьи будем понимать Информационную систему.
Простыми словами контракт — это договор между пользователем системы и системой на предоставление системой определенных возможностей пользователю, при соблюдении пользователем требуемых условий.
N.B. Хочется обратить внимание, что понятие контракта также включает в себя поведение системы, описываемое в договоре. Почему-то многие разработчики отделяют поведение от самого договора, проводя аналогию между интерфейсом и реализацией этого интерфейса. Но даже в программировании мы говорим, что реализация интерфейса должна отвечать требованиям интерфейса. Интерфейс определяет, что нужно сделать, а вот как это будет сделано — это уже зона ответственности разработчика. Иначе говоря, если у нас есть контракт на поставку электроэнергии, в котором оговаривается наличие переменного напряжения в 230 вольт с частотой 50 Гц и мощностью до 1,5 кВт, то не важно,каким именно способом вы этот контракт реализуете: подключите одну фазу с трансформатора, поставите дизель или бензогенератор, подключите ветряк. При этом поведение контракта должно оставаться одним и тем же: 230 вольт, 50 Гц, до 1,5 кВт. Более близкий пример: у нас есть контракт, по которому система обязуется перевести деньги пользователя с одного счета на другой. Какая бы реализация этого контракта не была написана разработчиками, пользователь ожидает одно поведение системы: система проверяет наличие переводимой суммы на счете-источнике, замораживает нужную сумму на этом счете, начисляет эту сумму на счет-получатель, списывает нужную сумму со счета-источника. Любая другая реализация, формально соответствующая контракту - указать номер счета-источника, номер счета-получателя и переводимую сумму, - является нарушением контракта.
Это определение мы будем использовать в дальнейшем при описании сущности истории. Разумеется, это ограничивает зону применимости подходов SAFe только разработкой программных продуктов, но моя цель сейчас состоит именно в том, чтобы помочь командам разработки.
SAFe описывает четырехуровневую иерархию артефактов, описывающих функциональное поведение системы: Эпики (Epic), Возможности (Capability), Функции (Feature) и Истории (Story)1. В совокупности эти артефакты используются для описания требуемого поведения решения (solution). Непосредственная работа по реализации решения определяется через истории (stories), которые составляют Бэклог команды (Team backlog). Некоторые истории возникают из бизнес фич (business features) и вспомогательных фич (enabler features) из Бэклога ARTа (ART Backlog), в то время как другие исходят из внутреннего контекста команды.
Обратите внимание на последнее предложение: некоторый истории возникают из фич из Бэклога ART, а некоторые исходят из внутреннего контекста команды. Тут SAFe явно говорит, что в бэклоге команды могут присутствовать истории, не связанные с фичами из бэклога АРТа. Они могут быть порождены необходимостью решения каких-то задач в контексте самой команды, являться результатом ретроспектив, представлять собой технический долг, и т.п. Этот момент следует учитывать при планировании, поскольку такие локальные истории съедают емкость команды и могут привести к тому, что ожидания стейкхолдеров не сбудутся.
Каждая история — это описание небольшого, независимого (в том смысле, что его реализация не должна зависеть от реализации прочих вариантов поведения системы) поведения системы, которое может быть реализовано пошагово, и которое представляет некоторую ценность для пользователя или Решения. Это вертикальный срез функциональности, гарантирующий, что каждая итерация будет приносить новую ценность. Истории должны быть небольшими и должны завершаться за одну итерацию (см. Раздел о разделении историй).
Здесь стоит подчеркнуть следующий момент: история — описание поведения системы, несущее ценность для пользователя или Решения. Здесь явно указано, что реализация истории не должна обязательно приводить к доставке ценности для пользователя. Это может иметь ценность только внутри решения, которая не влияет никоим образом на поведение системы в целом. То-есть, долгий спор о том, что каждый спринт должен оканчиваться поставкой версии решения в среду промышленной эксплуатации, можно считать закрытым.
Часто истории сначала записываются на каталожных карточках или на стикерах. Физическая природа карточки создает ощутимую связь между командой, историей и пользователем: она помогает вовлечь всю команду в написание истории. Стикеры имеют и другие преимущества: они помогают визуализировать работу, их можно легко разместить на стене или на столе, менять последовательность размещения и даже передать карточку другой команде при необходимости. Истории позволяют лучше понять объем работ и прогресс:
• «Вау, посмотрите на все эти истории, на которые мы собираемся подписаться» (объем работ)
• «Посмотрите на все истории, которые мы реализовали в этой итерации» (прогресс)
Хотя написать историю может любой, согласование их добавления в Бэклог команды и добавление их в базовый план системы является обязанностью Владельца продукта (Product Owner). Понятно, что бумажные стикеры плохо приспособлены для использования в масштабах крупного предприятия, поэтому истории часто быстро переносятся в инструменты Agile Lifecycle Management (ALM).
В SAFe, как описано ниже, есть два типа историй: пользовательские истории (user stories) и вспомогательные истории (enabler stories).
Источники появления историй
Обычно истории появляются, как результат декомпозиции бизнес и вспомогательных фич (см. рис. 1).
Пользовательские истории
Пользовательские истории являются основным средством описания необходимой функциональности. Они, по существу, заменяют традиционную спецификацию требований (requirement specification). Однако, в некоторых случаях, они служат средством для объяснения и проработки поведения системы, которое позже будет зафиксировано в спецификации, поддерживающий потребность: в удовлетворении требованиям регуляторных органов, соответствии стандартам поставщиков, в трассируемости и т. д.
Поскольку истории фокусируются на пользователе, как на выгодополучателе, а не на системе, пользовательские истории ориентированы на клиента и описывают ценность с точки зрения клиента. Поэтому рекомендуемой формой записи истории является «голос пользователя», а именно:
Как <пользователь в определенной роли>, я хочу <выполнять определенные действия>, чтобы <получить ценность>
Используя этот формат, команды ориентируются на понимание того, кто использует систему, что он с ней делает и почему он это делает. Регулярное применение формата «голоса пользователя» способствует повышению компетентности команды в предметной области, команда лучше понимает реальные бизнес-потребности своего пользователя (см. рис. 2).
Примем во внимание тот факт, что пользовательская история описывает поведение системы с точки зрения пользователя, а не с точки зрения системы, и уж точно не описывает то, что должен сделать разработчик. История в части своей формулировки ни коим образом не является задачей, здесь она ближе к требованию.
Также отметим, что помимо упомянутого в статье формата «голоса пользователя» бывают и альтернативные варианты, например:
Как <пользователь>, я могу <действие> <влияние на пользователя>
В целях <цель>, как <пользователь> я могу <действие>
Второй вариант мы используем в том случае, если мы не можем четко сформулировать ценность выполнения действия конечным пользователем. Обычно такая проблема возникает, когда автоматизируется только часть какого-то процесса, т. е. итоговая цель, которую должен достичь пользователь, слишком далеко от того, что получается в результате выполнения пользовательской истории. В таком случае мы просто указываем, какое значение выполнение действия оказывает на пользователя, который это действие выполняет.
Третий вариант, наоборот, сфокусирован на ценности выполнения действия. Данная запись позволяет в первую очередь сосредоточится на то, какая бизнес-ценность будет создана в результате реализации истории. Если мы споткнулись на первом шаге формулирования истории, возможно, нет необходимости ее делать вообще.
Как описано в Дизайн Мышлении, персонажи описывают конкретные характеристики типичных представителей групп пользователей, которые помогают командам лучше понять своего конечного пользователя. Примерами персонажей для пассажира на рисунке 2 могут быть или искательница острых ощущений «Джейн», или робкий пассажир «Боб». Затем описания историй могут ссылаться на этих персонажей («Как Джейн, я хочу…»).
Хотя формат пользовательской истории типичен, не каждая система взаимодействует с конечным пользователем. Иногда «пользователь» — это устройство (например, принтер) или система (например, сервер транзакций). В этих случаях история может принять форму, показанную на рисунке 3.
Еще раз фокусируемся на том, что любой контракт взаимодействия системы с окружающем миром: человеком, другой системой или каким-либо программным обеспечением, должен быть выражен в форме пользовательской истории.
Вспомогательные истории
Команды также разрабатывают новую архитектуру и инфраструктуру, необходимые для реализации новых пользовательских историй. В этом случае история не может напрямую касаться какого-либо конечного пользователя. Команды используют «вспомогательные истории» для обеспечения исследований, проработки архитектуры или формирования инфраструктуры. Как показано на рисунке 4, вспомогательные истории могут быть выражены техническим, а не ориентированным на пользователя языком2.
Существует множество различных типов вспомогательных историй, в том числе:
- Рефакторинг и изучение путей реализации (Spikes, традиционно определяемые в XP)
- Создание или улучшение инфраструктуры разработки/развертывания
- Выполнение задач, требующих участия человека (например, индексирование миллиона веб-страниц).
- Создание необходимых конфигураций продукта или компонента для различных целей
- Проверка качества системы (например, тестирование производительности и поиск уязвимостей)
Хочется отметить, что при реализации большой пользовательской историй, в системах со сложной архитектурой, возникает ситуация, когда история не может быть реализована в рамках одной итерации или силами одной команды. Она требует декомпозиции, но при этом часть вновь появившихся историй перестает отвечать критериям пользовательской истории. Например, изменяемы компонент находится глубоко в недрах системы и не взаимодействует напрямую с пользователем со стороны, или мы не смогли вместить в историю вертикальный срез функциональности системы в силу того, что фронт и бэк реализуются разными командами. В этом случае изменение кода также может быть оформлено в виде истории — вспомогательной истории. Её также можно сформулировать в формате «голоса пользователя», где пользователем является какой-то компонент внутри системы, описывается ожидаемое поведение и причина, почему пользователю это поведение нужно. Признаком того, что это изменение спрятано глубоко в системе и явно не воздействует на контракт системы, является тип истории — вспомогательная.
Таким образом, вспомогательная история имеет двоякую природу. С одной стороны, подобно пользовательской, она может быть элементом фиксации требований к функциональности системы. С другой стороны, могут существовать вспомогательные истории, которые аналогичны задачам, т. е. описывают, какую работу должен выполнить член команды. Об этом стоит помнить при правильной формулировке вспомогательной истории. Она не должна звучать директивным указанием того, что должен сделать исполнитель. Формулировать вспомогательных историй-задач надо с точки зрения ожидаемого результата, например:
- не «провести рефакторинг» а «улучшить читаемость кода» или «обеспечить соответствие реализации архитектурным руководствам»
- не «провести спайк», а «выяснить возможность получения данных о сотрудниках компании из бухгалтерской системы»
- не «развернуть CI-конвейер», а «обеспечить команду инструментом непрерывной интеграции» и т. п.
С одной стороны это выглядит, как излишнее усложнение при формировании элементов бэклога, с другой — мы фиксируем наше ожидание, и оставляем свободу реализации конкретному исполнителю. Возможно, он предложит иной способ решения нашей проблемы.
Вспомогательные истории демонстрируются3 так же, как и пользовательские истории, обычно путем демонстрации полученных знаний, найденных решений, созданных артефактов или пользовательского интерфейса, заглушек или макетов.
Написание хороших историй
Хорошие истории требуют при создании нескольких пар глаз. В Agile-подходе все участники команды определяют единое понимание того, что им нужно создать, чтобы уменьшить количество переделок и увеличить производительность. Команды взаимодействуют, используя Разработку, управляемую поведением (BDD), чтобы определить подробные приемочные тесты, которые окончательно определяют каждую историю.
Совместное написание истории гарантирует учет всех точек зрения, общее согласие относительно сценария истории и ожидаемых результатов, которые определены в описании истории, критериях приемки и приемочных тестах. Приемочные тесты написаны в терминах предметной области с использованием BDD. Затем тесты BDD автоматизируются и выполняются непрерывно для обеспечения Встроенного Качества (Built-In Quality). Тесты BDD написаны в связке с требованиями к системе (историями) и, следовательно, могут использоваться в качестве окончательного описания поведения системы, заменяя стандартную документацию на систему.
Поясню необходимость участия всей команды при определении истории. Каждый участник команды, будучи представителем своей функциональной роли, определяет, достаточно ли написанного, чтобы выполнить действия, которые подразумевает его функциональная роль. Аналитик должен убедиться, что команда имеет информацию о процессе, в рамках которого используется описываемая функциональность, достаточно ли команде дополнительных данных для реализации истории, не упущены ли нефункциональные требования в рамках истории. Архитектор системы должен понять, может ли он принять решение об изменении контрактов системы и зон ответственности компонентов внутри системы. Разработчик — сможет ли он реализовать описанное, а специалист контроля качества — проверить это. AppSec-специалист должен определить, какие риски ИБ могут быть заключены в истории. Дизайнер — как улучшить UX. DevOps — потребует ли реализация истории изменений в среде разработки, контроля качества и эксплуатации. Все перечисленное мы подразумеваем, когда говорим, что история создается командой.
Далее, когда мы говорим о том, что приемочные тесты (BDD-тесты) привязаны к требованиям к системе, мы имеем в виду как-раз таки пользовательские истории, т. е. требования уровня пользователя в рамках стандартной классификации. Надо отметить, что использование термина BDD в данном контексте означает не столько применение формального языка (например, Gherkin) для написания и автоматизации тестов, сколько именно концепции написания тестов до написания реализации. Иными словами, команда, сформулировав историю, в качестве критериев приемки определяет список тестов на достаточно естественном (в смысле понимания сторонним человеком), но все же формализованном (для избежания разночтений) языке, позволяющем описать сценарии взаимодействия пользователя с системой в рамках истории. Фактически, это получается разделенный на части по разным потокам сценарий использования (use case) системы, где каждый тест — один из потоков.
Именно поэтому далее по тексту указывается, что такой набор тестов позволяет заменить пользовательскую документацию на систему, поскольку предоставляет всю необходимую информацию о том, кто, зачем и как может воспользоваться системой.
Подход трех С: карточка, обсуждение, подтверждение
Рону Джеффрису, одному из создателей XP, приписывают создание подхода 3Cs:
- Карточка (Card) — фиксирует заявление о намерениях в рамках пользовательской истории с помощью каталожной карточки, стикера или специальной системы. Каталожные карточки обеспечивают физическую связь между командой и историей. Размер карточки физически ограничивает длину истории и преждевременные предположения о специфике поведения системы. Карточки также помогают команде «почувствовать» предстоящий объем работ, потому что одно дело — смотреть на десять строк в электронной таблице, и другое — держать десять карточек в руке.
- Обсуждение (Conversation) — представляет собой «обязательство диалога» об истории между командой, клиентом (или доверенным лицом клиента), PO (который может представлять клиента) и другими заинтересованными сторонами. Обсуждение необходимо для определения более подробного поведения, требуемого для реализации намерения. Обсуждение может породить дополнительную конкретику в виде критериев приемки (подтверждаемых ниже) или вложений в пользовательскую историю. Обсуждение является частью всех этапов жизненного цикла истории:
- Проработки бэклога (Backlog refinement)
- Планирования (Planning)
- Реализации (Implementation)
- Демонстрации (Demo)
Эти своевременные дискуссии создают общее понимание объемов работ, которое не может дать обычная документация. Спецификация на основе примеров заменяет подробную документацию. Обсуждения также помогают выявить пробелы в пользовательских сценариях и нефункциональных требованиях.
- Подтверждение (Confirmation). Критерии приемки предоставляют информацию, необходимую для понимания, что история реализована правильно и покрывает соответствующие функциональные и нефункциональные требования. На рисунке 5 приведен пример. Некоторые команды часто используют раздел подтверждения на карточке истории, чтобы записать то, что они будут демонстрировать.
Опять мы возвращаемся к важности подходов BDD. Спецификация на основе примеров (Specification by example (SBE)) — это один из подходов определения требований и приемочных тестов. Как я уже говорил выше, вы детализируете описание поведения системы, данной в истории, через набор тестовых сценариев, записанных на понятном заказчику языке. С одной стороны эти тесты являются уточнением истории, с другой — позволяют проверить корректность реализации. Использование естественного языка позволяет привлечь к формированию спецификаций представителя заказчика без необходимости обучения его каким-то специфическим подходам.
Agile команды автоматизируют приемочные тесты везде, где это возможно, часто на языке предметной области, удобном для бизнеса. Автоматизация создает исполняемую спецификацию для проверки правильности реализации и верификации решения. Автоматизация также обеспечивает возможность быстрого регрессионного тестирования системы, улучшая Непрерывную Интеграцию, рефакторинг и обслуживание.
В идеале, написанная на формализованном языке спецификация теста должна исполняться средой выполнения автотестов. В реальности, использование таких инструментов сопряжено с рядом трудностей, поэтому написанный тест является исходным материалом для создания специалистами контроля качества автотестов в том инструменте, который принят в компании. В зависимости от природы тестируемого контракта это может быть инструмент UI или API-тестирования.
Инвестиции в хорошие истории
Agile-команды тратят много времени на выявление, проработку и понимание пользовательских историй и написание приемочных тестов. Так и должно быть, потому что соответствует следующей истине:
Написание кода для осознанной цели не обязательно является самой сложной частью разработки программного обеспечения.
Наоборот, понимание реального назначения кода — это сложно. Поэтому вложение в хорошие пользовательские истории, пусть даже и в последний ответственный момент4, — соответствующая трата ресурсов команды. Билл Уэйк придумал аббревиатуру INVEST (Leffingwell, 2011) для описания атрибутов хорошей пользовательской истории.
- I (Independent) — независимая (истории не зависят друг от друга и могут реализовываться в любом порядке)
- N (Negotiable) — обсуждаемая (история представляет гибкое заявление о намерениях, а не контракт)
- V (Valuable) — ценная для клиентов и бизнеса (предоставляет клиенту вертикальный срез функциональности, имеющий для него ценность)
- E (Estimable) — оцениваемая (история должна быть небольшой и возможной к обсуждению, чтобы ее можно было оценить с достаточной точностью)
- S (Small) — маленькой (история должна умещаться в одну итерацию)
- T (Testable) — тестируемая (история понятна в той мере, чтобы было ясно, как её проверить)
Пояснение по атрибуту Negotiable, возникающее из-за того, что я привязываю историю к изменению контракта системы. История — это соглашение о намерениях (а не контракт) со стороны клиента и команды разработки об изменении контракта между системой и пользователем. То-есть, клиент и команда согласны, что жесткий договор между пользователем системы и системой должен быть как-то изменен. Никакого противоречия здесь нет.
Правильное разделение историй
Более маленькие истории реализуются быстрее и с большим уровнем качества, поскольку небольшие элементы проходят сквозь любую систему быстрее, подвержены меньшим изменениям и меньшим рискам. Поэтому разбиение больших историй на более мелкие — обязательный навык для каждой Agile-команды. Это одновременно и искусство, и наука инкрементальной разработки. Agile Software Requirements описывает десять способов разделения историй. Ниже приводится краткое изложение этих методов:
- Шаги рабочего процесса
- Варианты бизнес-правил
- Основное усилие
- Простые/сложные
- Варианты данных
- Методы ввода данных
- Отложенные характеристики качества системы
- Операции (например, CRUD)
- Сценарии использования
- Выделение исследований
На рисунке 6 показан пример разделения по сценариям использования.
Шаги рабочего процесса
В случае, если начальная пользовательская история содержит внутри себя длинный сценарий взаимодействия, ее надо разделить на отдельные шаги и в первую очередь делать шаги, имеющие максимальную ценность. Например, мы автоматизируем заявку на развертывание микросервиса в среде промышленной эксплуатации. Планируется, что заявку формирует релиз-менеджер, после чего согласующим приходит уведомление в телеграм, и когда они согласуют заявку, она автоматически исполняется.
Как релиз-менеджер, я хочу, чтобы после создания мной заявки на развертывание микросервиса согласующие получали в телеграм уведомление, согласовывали заявку на развертывание микросервиса, и она автоматически исполнилась после согласования, чтобы не нарушать регуляторных требований и ускорить процесс развёртывания.
История получается слишком большой для того, чтобы быть реализованной в один спринт. Поскольку источником появления истории являются регуляторные требования, которые гласят, что любое изменение инфраструктуры должно быть согласовано и задокументировано, то основная ценность истории — в существовании артефакта об изменении. Можно разделить историю на три:
Как релиз-менеджер, я хочу, чтобы согласующие согласовали заявку на развертывание микросервиса, чтобы не нарушать регуляторных требований.
Как релиз-менеджер, я хочу, чтобы согласованная заявка на развёртывание микросервиса исполнялась автоматически после согласования, чтобы ускорить процесс развёртывания.
Как релиз-менеджер, я хочу, чтобы после создания заявки на развёртывание согласующим приходило уведомление в телеграм, чтобы ускорить процесс согласования.
При реализации первой истории, когда мы просто получаем согласование заявки по установленному маршруту согласования, мы реализуем основную ценность. Остальные истории могут расширять нужный нам результат и их можно реализовать в последующих спринтах.
Варианты бизнес-правил
Бывают истории, которые содержат в себе сложные пути достижения результата. Например, мы хотим сформировать каталог товаров с возможностью фильтрации по нужным покупателю критериям.
Как покупатель, я хочу отфильтровать каталог товаров, чтобы получить подборку товаров только с интересующими меня характеристиками и параметрами.
Когда мы на PBR начнем уточнять списки фильтров, мы выясним, что существует множество наборов правил, позволяющих осуществлять фильтрацию, для разных категорий товаров, зависящих от типа товара:
- для одежды это размер, цвет, тип
- для компьютеров это количество ядер процессора и их частота, объем оперативной памяти, объем и тип накопителя.
В случае необходимости, мы можем разбить нашу пользовательскую историю на истории вида:
Как покупатель, я хочу отфильтровать каталог одежды, чтобы получить подборку одежды только нужного мне типа, размера и цвета.
Как покупатель, я хочу отфильтровать каталог компьютерной техники, чтобы получить подборку компьютеров с необходимыми мне характеристиками процессора, оперативной памяти и накопителя.
Основное усилие
Существуют ситуации, когда в рамках истории реализуется несколько однотипных сценариев, при этом основные усилия будут затрачены на реализацию первого сценария. Например, мы реализуем агрегатор поиска авиабилетов.
Как пользователь, я могу найти нужный мне рейс любой авиакомпанией из множества различных авиакомпаний, чтобы перелет был максимально удобен для меня, и его цена меня устраивала.
Понятно, что в этом случае максимальные трудозатраты будут при подключении первой авиакомпании – именно тут будет реализован механизм ввода параметров, передачи их в информационную систему авиакомпании, получения результатов и их визуализацию. Подключение остальных авиакомпаний с учетом значительной унификации билетных систем различных авиакомпаний будут требовать значительно меньше времени.
Следовательно, мы можем выделить пользовательскую историю на несколько следующих:
Как пользователь, я могу найти нужный мне рейс авиакомпании S7, чтобы перелет был максимально удобен для меня, и его цена меня устраивала.
Как пользователь, я могу найти нужный мне рейс любой авиакомпанией из множества различных авиакомпаний (с условием, что поиск по одной авиакомпании уже реализован), чтобы перелет был максимально удобен для меня, и его цена меня устраивала.
Истории в данном случае не являются независимыми, но их связь понятна и прозрачна.
В условиях, когда мы не знаем приоритет подключения авиакомпаний, первую историю можно переформулировать так:
Как пользователь, я могу найти нужный мне рейс хотя бы в одной авиакомпании, чтобы перелет был максимально удобен для меня, и его цена меня устраивала.
Простые/сложные
В процессе уточнения истории на PBR или планировании размер истории увеличивается из-за уточнения деталей и подробностей. Необходимо выделить минимально работающий вариант истории (MVP истории), а все остальное вынести в отдельные истории. Например, историю по фильтрации каталога в интернет-магазине можно сформулировать так:
Как покупатель, я хочу отфильтровать каталог одежды, чтобы получить подборку одежды только нужного мне размера.
… нужного мне цвета.
… нужного мне типа.
Варианты данных
Сложность истории может обуславливаться сложным набором входных данных. Например, для в рамках интернет-магазина я реализую бэкофисную часть с возможностью поиска заказов по параметрам.
Как сотрудник поддержки магазина, я хочу найти заказ, сделанный покупателем, чтобы помочь клиенту в решении его проблемы с заказом.
При реализации данной истории мы можем производить поиск с использованием дат (дата заказа), числовых значений (сумма заказа), текста (название товара или фамилия заказчика), регулярных выражений (адрес доставки) и т. п. Соответственно, начальную историю можно разбить на отдельные истории по типам данных, например:
… я хочу найти заказ, сделанный покупателем, по дате заказа…
… я хочу найти заказ, сделанный покупателем, по фамилии заказчика…
Методы ввода данных
Иногда сложность реализации истории заключается не столько в алгоритме ее функционирования, а в пользовательском интерфейсе. В этом случае в первую очередь можно реализовать историю сделав упор на ее функциональности, а потом расширять удобство использования за счет доработки интерфейса. Например, при реализации истории по подбору авиабилетов мы можем реализовать ввод параметров простыми полями ввода:
- даты вводятся как текст, без выпадающего календарика
- коды аэропортов или города вылета/назначения – без автозаполнения
- количество пассажиров – без выпадающего списка.
Как пользователь, я могу найти нужный мне рейс, используя простой ввод даты и городов отправления/назначения…
… я могу найти нужный мне рейс, используя календарь…
… я могу найти нужный мне рейс, используя автозаполнение для аэропортов отправления/назначения…
В этом случае мы нарушаем принцип независимости историй, но делаем это осознано, выигрывая в управляемости и предсказуемости результатов работы команды.
Отложенные характеристики качества системы
Часто разработчики начинают реализовывать сложные технические решения помня о наличии нефункциональных требований, например по производительности. Это похвально, но в самом начале можно реализовать самый простой алгоритм работы, а после получения обратной связи о востребованности функции заняться ее оптимизацией.
Например, реализуя поиск авиабилетов можно сделать последовательный запрос ко всем подключенным авиакомпаниям, а для снижения негатива от клиентов реализовать красивую анимацию прелоадера. А дальше реализовать распараллеливание запросов на несколько воркеров, включить кэширование результатов для сокращения времени выполнения запроса до 60 секунд.
… я могу найти нужный мне рейс менее, чем за 60 секунд…
Операции
При описании сценариев использования аналитики используют слово «управлять». Данное слово подразумевает под собой набор операций по созданию, просмотру, изменению и удалению какого-либо элемента. Например, создаем функциональность по работе пользователя с заметками:
Как пользователь, я могу управлять своими заметками, чтобы не забывать ту информацию, которая мне нужна и избавляться от ненужной.
Тут подразумевается, что я могу создать заметку, просмотреть ее, изменить и удалить. То-есть, историю для удобства можно разделить на четыре:
… я могу создать заметку…
… я могу просмотреть заметку…
… я могу изменить заметку…
… я могу удалить заметку…
Выделение исследований
Большая оценка истории может обосновываться не только сложностью реализации, но и высокими рисками вследствие непонимания способов ее реализации. Для этого мы делим историю на проведение исследовательской работы по способу реализации — это будет вспомогательная история (enabler story), и непосредственно реализацию — пользовательская история (user story). Еще раз просмотрите раздел про вспомогательные истории.
Следует помнить следующее! Не стоит делить историю по слоям архитектуры, по компонентам системы, по типам работ, которые нужно выполнить или по функциональным ролям. Это так называемое «горизонтальное» деление. Данный подход приводит к тому, что какой-то результат будет отложен во времени.
Используйте только «вертикальное» деление, при котором реализация даже совсем маленькой истории дает ощутимый результат пускай не для конечного пользователя, но хотя бы для команды разработки.
Оценка историй
Agile-команды используют для оценки своей работы сторипоинты и метод «покер планирования». Story Point — это особое число, которое представляет собой комбинацию следующих характеристик:
- Объем работ – сколько нам придется трудится, чтобы сделать это?
- Сложность — насколько это будет сложно сделать?
- Понимание – что известно о задаче, насколько мы ее понимаем?
- Неопределенность – что нам неизвестно?
Сторипоинты представляют собой относительную единицу, не привязанную к какой-либо конкретной единице измерения. Размер (объем усилий команды, затрачиваемый командой на реализацию истории) каждой истории оценивается относительно наименьшей истории, которой присваивается размер «единица». Для оценки применяется модифицированная последовательность Фибоначчи (1, 2, 3, 5, 8, 13, 20, 40, 100), отражающая присущую неопределенность в оценке, особенно в области больших чисел (например, 20, 40, 100).
Еще раз хочется отметить, что сторипоинты — это объем работы всей команды. Поскольку команда планировалась вся целиком, ответственность за задачи они берут также целиком. Поэтому не бывает «сторипоинтов разработчика» и «сторипоинтов тестировщика». Про сторипоинты опубликую отдельную статью позже.
Покер планирования
Agile-команды часто используют метод «покера планирования», который сочетает в себе методы экспертной оценки, оценки по аналогии и декомпозицию для создания быстрых, но достаточно надежных оценок. Декомпозиция означает разделение истории или фичи на более мелкие части, которые легче оценить.
(Обратите внимание на то, что можно использовать несколько других методов для оценки.) Правила оценки с помощью покера таковы:
- Участвуют все члены команды
- Каждому участнику дается колода карт, содержащая модифицированную последовательность Фибоначчи.
- Владелец продукта (product owner) принимает участие, но не оценивает
- Скраммастер/Team Coach принимает участие, но не оценивает, если только он не совмещает роль с ролью разработчика
- Для каждого оцениваемого элемента бэклога PO читает описание истории
- Команда задает вопросы и получает ответы
- Каждый участник самостоятельно и в тайне от других выбирает карту со своей оценкой
- Все карты переворачиваются одновременно, чтобы избежать предвзятости и сделать все оценки видимыми
- Участник, давшие самую высокую и самую низкую оценки объясняют мотивы своего решения
- После обсуждения каждый участник дает повторную оценку
- Скорее всего оценки совпадут. В противном случае процесс повторяется5.
Уместно провести несколько предварительных обсуждений архитектуры. Однако трата слишком большого количества времени на обсуждение архитектуры может оказаться напрасной тратой усилий. Настоящая ценность использования покера планирования заключается в том, чтобы добиться общего понимания объема истории. Это также весело6!
Внимание на фразу: уместно провести несколько предварительных обсуждений архитектуры. Это отнюдь не означает, что нужна какая-то отдельная активность по обсуждению архитектуры. Под архитектурой здесь понимается определение публичных контрактов и принятие решения о распределении зон ответственности между компонентами системы. То-есть мы должны понять:
- как изменится интерфейс взаимодействия с пользователем – будет ли изменение существующих методов (с версионированием и сохранением обратной совместимости) для API, или какие дополнительные интерфейсные элементы появятся в GUI;
- какие из существующих модулей смогут взять на себя реализацию этих интерфейсов;
- нужны ли новые модули для реализации интерфейсов;
- какие новые интеграции возникают у нашей системы, чтобы реализовать историю.
Все эти активности проходят в рамках PBR в один из спринтов, предыдущих планируемому. Еще раз — обсуждение архитектуры происходит, не после взятия в реализацию, не на планировании, а до планирования.
Скорость команды (Velocity)
Скорость команды для итерации равна сумме сторипоинтов за все завершенные истории, соответствующие их критериям готовности (DoD). Чем дольше команда работает вместе, тем их средняя скорость (завершенные сторипоинты за итерацию) становится более надежной и предсказуемой. Более предсказуемая оценка скорости помогает в планировании и дает возможность ограничить незавершенную работу (WIP), поскольку команды не берут на себя больше историй, чем позволяет их историческая скорость. Эта метрика также позволяет оценить, сколько времени потребуется для доставки эпиков, фич, капабилити и вспомогательных элементов бэклога, которые также оценивать с использованием сторипоинтов.
Исходный базовый уровень для оценки
В обычном Scrum оценка в сторипоинтах и скорость каждой команды — это проблема самой команды. При масштабировании производства становится проблематично предсказать размер в сторипоинтах более крупных эпиков и фич, если скорость команды сильно различается. Чтобы решить эту проблему, команды SAFe изначально калибруют исходный базовый уровень сторипоинта, чтобы один сторипоинт определялся примерно одинаково для всех команд. Нет необходимости повторно калибровать оценку команды или ее скорость. Калибровка выполняется один раз при запуске новых Agile Release Trains.
Нормализация сторипоинтов, обеспечивающая достижение согласованного исходного уровня для историй и скорости, осуществляется следующим образом:
- Дайте каждому разработчику/тестировщику в команде восемь сторипоинтов на двухнедельную итерацию (по одному сторипоинту за каждый идеальный рабочий день, вычтя два дня на общие накладные расходы).
- Вычтите по одному сторипоинту за каждый день отпуска и выходной для каждого члена команды.
- Найдите небольшую историю, для которой на написание кода вы потратите полдня, и на тестирование и проверку — потратите полдня. Оцените эту историю в один сторипоинт.
- Оцените остальные истории относительно этой «единичной».
Особое внимание обратите на следующее: команды SAFe калибруют исходный базовый уровень сторипоинта, чтобы один сторипоинт определялся примерно одинаково для всех команд. Это ответ на извечный вопрос при формировании оценок историй, фич и капабилити разными командами в рамках ARTа. SAFe явно говорит, что оценка в сторипоинтах нормализована (одинакова) для всех команд, участвующих в оценке элементов бэклогов. Другими словами, в рамках одного ARTа сторипоинт любой команды равен сторипоинту другой.
Пример: предположим, что команда состоит из шести человек, из которых трое — разработчики, двое — тестировщики и один PO, нет отпусков или праздников. Тогда предполагаемая начальная скорость = 5 × 8 сторипоинтов = 40 сторипоинтов за итерацию. (Примечание: может потребоваться небольшое снижение скорости, если один из разработчиков или тестировщиков исполняет роль Team Coach.)
Таким образом, сторипоинты в какой-то мере будут сопоставимы между командами. Менеджмент сможет лучше понять стоимость сторипоинта и более точно определить стоимость запланированной фичи или эпика.
В то время как команды имеют тенденцию увеличивать свою скорость с течением времени — и это хорошо — в действительности это число, как правило, остается стабильным. Скорость команды гораздо больше зависит от изменения размера команды и технического контекста, чем от изменений производительности.
Примечание. Kanban-команды SAFe обычно тратят меньше времени на оценку историй, чем Scrum-команды. В модели, основанной на потоке Канбан, рабочие элементы или истории обычно декомпозированы и доведены до такого размера, который позволяет команде поставить историю в течение нескольких дней. В контексте SAFe, где командам необходимо участвовать в планировании итераций и определять истории для будущих итераций, требуется некоторое понятие размера.
Канбан-команды SAFe могут первоначально использовать покер планирования или аналогичный механизм для определения размеров своих историй. Однако, скорее всего у них сформируется чувство разбиения работы на истории одинакового размера, поскольку это способствует потоку поставки в целом и гарантирует, что никакая крупная история не заблокирует другие истории, которым также необходимо пройти через Канбан-систему. Когда команды понимают свою скорость, они могут предположить, сколько историй они смогут реализоватьв единицу времени, что позволит им распределить истории по итерациям во время PI-планирования, и позволит брать на себя обязательства перед другими командами касательно того, когда конкретные истории будут реализованы.
Для команд, занимающихся постоянным обслуживанием и поддержкой, оценка обычных элементов их бэклогов часто имеет меньшее значение. Во многих случаях эти команды не оценивают этот тип работ. Однако у всех команд есть работы, возникшие по результатам ретро, потенциальные улучшения конвейера CD и другие важные задачи, требующие внимания, планирования и оценки.
Критерии проверки историй на корректность формулировки
Поскольку очень часто у команд разработки возникает вопрос «Как же все-таки правильно сформулировать эти ваши истории?» я немного расширил критерии INVEST и сформулировал их немного другим образом, именно для разработчиков.
Алгоритм определения типа истории
Пользовательская история
- Требует изменения кода.
- Приводит к изменению публичного контракта системы.
- Находится в контексте системы.
- Может быть сформулирована в формате «голоса пользователя».
- Определен пользователь, чьё взаимодействие с системой описывает история. Этим пользователем может быть человек, другая система или какое-либо аппаратное обеспечение.
- Описывает, что именно должна делать система, а не разработчик.
- Описывает, что должна делать система с точки зрения пользователя, а не системы.
- Описывает ценность, ради которой пользователь взаимодействует с системой.
- Реализация будет представлять вертикальный срез системы и поставлять законченную функциональность.
- Реализацию можно сделать вне зависимости от других пользовательских историй (не вспомогательных). Есть исключение, если история является результатом декомпозиции другой истории.
- Может быть реализована силами одной команды.
- Может быть реализована в одну итерацию разработки.
- Содержит приемочные тесты (требование DoR).
- Требует стандартных процедур контроля качества.
Вспомогательная история на разработку
- Требует изменения кода.
- Не приводит к изменению публичного контракта системы.
- Может приводить к изменению контрактов компонентов системы.
- Находится в контексте системы.
- Может быть сформулирована в формате «голоса пользователя».
- Пользователем описанной функциональности является компонент внутри самой системы.
- Описывает, что именно должна делать система, а не разработчик.
- Описывает, что должна делать система с точки зрения пользователя, а не системы.
- Описывает цель, ради которой пользователь взаимодействует с системой.
- Реализация может представлять изменение отдельного компонента системы и не нести законченной ценности для пользователей за пределами системы.
- Реализацию можно сделать вне зависимости от других пользовательских историй (не вспомогательных). Есть исключение, если история является результатом декомпозиции другой истории.
- Может быть реализована силами одной команды.
- Может быть реализована в одну итерацию разработки.
- Содержит приемочные тесты (требование DoR).
- Требует стандартных процедур контроля качества.
Вспомогательная история – задача
- Не требует изменения кода.
- Фактически, представляет собой задачу, которую необходимо реализовать команде для достижения целей итерации разработки.
- Направлена на проведение исследовательской деятельности, формирования архитектурного пути, создание/улучшение инфраструктуры разработки, обеспечение соответствия требованиям регуляторных органов, выполнения работ, связанных с эксплуатацией и сопровождением продукта, и т.п.
- Сформулирована в формате ожидаемого результата, а не описания работы.
- Может быть реализована силами одной команды.
- Может быть реализована в одну итерацию разработки.
- Требует специфической проверки результата работы.
Список литературы
Cohn, M. (2004). User Stories Applied: For Agile Software Development. Addison-Wesley.
Leffingwell, D. (2011). Agile Software Requirements: Lean Requirements Practices for Teams, Programs, and the Enterprise. Addison-Wesley,.
Leffingwell, D. (2011). Agile Software Requirements: Lean Requirements Practices for Teams, Programs, and the Enterprise. Addison-Wesley.
Jeffries, Ronald E., Essential XP: Card, Conversation, Confirmation / Ron Jeffries Site, дата обновления: 30.08.2001 / Ссылка (дата обращения: 21.12.2024)
Specification by example / Википедия — свободная энциклопедия, дата обновления 11.08.2021 / Ссылка (дата обращения: 21.12.2024)
Story / SAFe Studio / Ссылка (дата обращения: 21.12.2024)
- Поскольку корректные переводы названий артефактов достаточно длинные и не прижились в профессиональном общении, в дальнейшем по тексту будем использовать английские варианты терминов. ↩︎
- Стоит обратить внимание, что даже в этом виде описывается ожидаемый результат, а не просто работа, которую нужно сделать. ↩︎
- В рамках демонстрации системы (System Demo) ↩︎
- Здесь имеется в виду применяемая в agile-подходах стратегия принятия решения Last Responsible Moment (LRM). Он гласит, что критичные решения надо принимать в тот момент, когда стоимость непринятия решения станет выше, чем стоимость принятия. По-другому можно сказать, что тратить усилия по формированию историй можно до самого последнего момента, после которого уже нужно делать реализацию кровь из носу. ↩︎
- Обратите внимание, здесь явно указано, что не берется никакое среднее значение по оценкам, а оценки всех участников команды должны совпасть. Отсюда – оценка истории всегда целое число и совпадает с модифицированным рядом Фибоначчи. ↩︎
- По мнению авторов SAFe. ↩︎