Как правильно писать User Stories: руководство для разработчиков

2
15961
views

Перевод статьи «Engineering guide to writing correct User Stories».

User Stories

Люди, работающие по методологии Agile, одержимы написанием user stories. И это, конечно, очень мощный инструмент. Но по своему опыту могу сказать, что множество людей пишут их неправильно.

Взгляните на этот пример:

User Story
Как пользователь
Я хочу получать webhooks о проблемах на Gitlab
Чтобы я мог помещать все текущие задачи в список

На вид совершенно нормальная user story, правда? Но на самом деле эта маленькая история имеет несколько проблем. И если вы не можете найти в ней как минимум 8 ошибок, вам действительно стоит прочесть эту статью.

Статья делится на три основные части:

  1. Улучшение user story в рамках текущего формата.
  2. Переписывание user stories с применением BDD, чтобы сделать их проверяемыми.
  3. Связывание user stories с тестами, исходным кодом и документацией.

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

Обнаружение и исправление проблем

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

User stories имеют тенденцию упускать некоторые из этих характеристик. Нам нужно это исправить.

Последовательность в терминах

Желания «Получать webhooks о проблемах» и «помещать все текущие задачи в список» как-то связаны между собой? «Проблемы» и «задачи» это одно и то же или все-таки нет? Ведь это могут быть как совершенно разные вещи, так и просто неудачный побор слов. Как нам это определить?

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

Допустим, в нашем случае после консультаций с экспертами мы обнаружили, что «задачи» и «проблемы» это одно и то же. Теперь нам нужно удалить неправильный термин.

User Story
Как пользователь
Я хочу получать webhooks о проблемах на Gitlab
+++Чтобы я мог помещать все текущие проблемы в список
---Чтобы я мог помещать все текущие задачи в список

Прекрасно. Использование одинаковых слов для одинаковых сущностей делает наши требования более понятными и последовательными.

Желания пользователей и ваши желания это не одно и то же

Когда мы изменили последнюю строку, я обратил внимание, что цель пользователя – «помещать все текущие проблемы в список». Зачем бедный пользователь хочет составлять списки проблем? В чем смысл этих действий? Никакой пользователь ничего подобного не хочет. Это просто некорректное требование.

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

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

Как пользователь
Я хочу получать webhooks о проблемах на Gitlab
+++Чтобы я мог просматривать и отслеживать прогресс по
этим проблемам
---Чтобы я мог помещать все текущие проблемы в список

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

Убираем технические детали

Вам когда-нибудь доводилось встречать человека, который хотел бы именно «получать webhooks о проблемах»? Это никому не нужно. В данном случае мы тоже смешиваем разные вещи.

Есть четкое разделение между целями пользователя и техническими способами их достижения. И «получать webhooks о проблемах» это определенно детали реализации. Завтра webhooks могут измениться на WebSockets, всплывающие уведомления и т. п. А цели пользователя при этом останутся прежними.

User Story

Как пользователь
+++Я хочу иметь обновляемую информацию о проблемах на Gitlab
---Я хочу получать webhooks о проблемах на Gitlab
Чтобы я мог просматривать и отслеживать прогресс по этим
проблемам

Видите? Теперь осталась только важная информация, без деталей реализации.

Уточнение ролей

Из контекста довольно понятно, что речь идет о каком-то инструменте, связанном с разработкой. Мы используем Gitlab и issue management. Так что не сложно догадаться, что у нас есть разные категории пользователей: джуниоры, мидлы и сеньоры. Возможно, менеджеры проектов, а также другие люди.

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

О каких пользователях идет речь в данной конкретной user story? Будут ли джуниоры точно так же отслеживать прогресс, как менеджеры проектов и архитекторы? Очевидно, что нет.

User Story

+++Как архитектор
---Как пользователь
Я хочу иметь обновляемую информацию о проблемах на Gitlab
Чтобы я мог просматривать и отслеживать прогресс
по этим проблемам

После обдумывания вариантов мы можем разделить user stories по ролям пользователей. А это позволяет нам более тонко контролировать поставляемый функционалом и то, кому мы этот функционал поставляем.

Расширяем user stories

Шаблон «Как <роль/человек>, я хочу <цель/нужды>, чтобы <почему>» очень хорош, поскольку он краткий и одновременно мощный. Он дает нам отличную возможность для коммуникации. Однако, у этого формата есть и несколько недостатков, о которых тоже стоит знать.

Делаем user stories проверяемыми

Проблема с приведенной выше user story в том, что она по-прежнему не проверяемая. Как нам проверить, что эта история (на данный момент или все еще) является рабочей для наших пользователей? Мы не можем этого сделать.

У нас нет четкой связи между этой user story и нашими тестами. Было бы прекрасно, если бы можно было писать user stories в качестве тестов…

Погодите, но ведь это возможно! Для этого у нас есть BDD («разработка через поведение») и язык gherkin. Именно для этого BDD и создавалась изначально. Это означает, что для того чтобы чтобы сделать нашу user story проверяемой, мы можем переписать ее в формате gherkin.

User Story в формате gherkin
История: Отслеживание прогресса проблем
Как архитектор
Я хочу иметь обновляемую информацию относительно проблем
на Gitlab
Чтобы я мог просматривать и отслеживать прогресс по этим
проблемам

Сценарий: получен новый валидный webhook о проблеме
Дано: webhook о проблеме валидный
Когда он получен
Тогда создана новая проблема

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

Обнаружение неполноты

Когда мы привыкли писать наши user stories на gherkin, мы начали писать сценарии для наших user stories. И мы обнаружили, что для каждой user story может быть несколько сценариев.

Двайте рассмотрим первый написанный нами сценарий: «получен новый валидный webhook о проблеме». Погодите, но что происходит, когда мы получаем невалидный webhook? Должны мы сохранять эту проблему или нет? Может, кроме сохранения проблемы нужно сделать что-то дополнительно?

Давайте в качестве источника информации о том, что может пойти не так и что делать в этих случаях, возьмем документацию Gitlab.

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

Возможно, проблема с нашими токенами

Теперь мы можем добавить еще два сценария, чтобы сделать нашу user story полной.

Расширенная User Story в формате gherkin
История: Отслеживание прогресса проблем
Как архитектор
Я хочу иметь обновляемую информацию относительно проблем
на Gitlab
Чтобы я мог просматривать и отслеживать прогресс
по этим проблемам

Сценарий: получен новый валидный webhook о проблеме
Дано: webhook о проблеме валидный
И webhook о проблеме аутентифицирован
Когда он получен
Тогда создана новая проблема

Сценарий: получен новый невалидный webhook о проблеме
Дано: webhook о проблеме невалидный
Когда он получен
Тогда проблема не создается

Сценарий: получен новый валидный неаутентифицированный webhook о проблеме
Дано: webhook о проблеме валидный
И webhook о проблеме не-аутентифицирован
Когда он получен
Тогда проблема не создается
И сохраняется дата webhook-а для дальнейшего
расследования

Мне нравится, что теперь эта простая user story ощущается как нечто сложное. Потому что таким образом на наше обозрение выставляется ее внутренняя сложность. И мы можем подогнать наш процесс разработки к этой растущей сложности.

Упорядочиваем user stories по степени важности

Сейчас непонятно, насколько важно для архитекторов «просматривать и отслеживать прогресс по проблемам». Это более важно, чем другие наши user stories? Поскольку это кажется довольно сложным, может, вместо этого нам следует заняться чем-то более простым и вместе с тем более важным?

Расстановка приоритетов важна для каждого продукта, и игнорировать ее нельзя. Даже если user stories это единственный наш способ записи требований. Существуют разные методы для расстановки приоритетов в требованиях, но мы рекомендуем придерживаться метода MoSCoW. В основе этого простого метода лежат четыре главных категории: must, should, could и won’t. Подразумевается, что у нас в проектной документации будет отдельная таблица приоритетов всех пользовательских историй.

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

После обсуждения этой темы с разными архитекторами, которые работают с нашим продуктом, мы обнаружили, что требование в нашей user story это абсолютный must:

ФункционалПриоритет
Аутентифицированные пользователи должны иметь возможность отсылать приватные сообщения.Must
Архитекторы должны отслеживать прогресс проблем.Must
Должны быть уведомления о входящих приватных сообщениях.Should
Можно было бы поддерживать сообщения нескольких провайдеров.Could
Зашифрованные приватные сообщения не поддерживаются.Won’t

Итак, теперь мы можем изменить название нашей user story, чтобы обозначить приоритет:

История: Архитекторы должны отслеживать прогресс проблем

Можно даже поставить гиперссылку на таблицу с приоритетами требований в файле с user story.

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

Связывание всего воедино

Если не уделять этому делу достаточно внимания, вы вскоре утонете в перепутанных user stories, тестах, исходном коде и документации. По мере роста вашего проекта станет просто невозможно сказать, какие части приложения за какие use-cases бизнеса отвечают. Чтобы разобраться с этой проблемой, нам нужно связать все вместе: требования, код, тесты и документацию. Наша цель – получить примерно следующее:

Связывание историй, требований, кода и документации

Для иллюстрации этого принципа я использую Python.

Я могу определить use-cases как набор уникальных высокоуровневых действий, которые может осуществлять ваше приложение (это очень похоже на точку зрения Clean Architecture).

Обычно я определяю пакет под названием usecases и собираю в нем все use-cases, чтобы было легко их просматривать одновременно. Каждый файл содержит простой класс (или функцию), выглядит это так:

Пример кода на Python

Я использую sphinx и директиву literalinclude, чтобы включить тот же файл, который мы используем для тестов, для документации основной логики. Я также использую глоссарий, чтобы показать, что issue это не просто случайное слово, а конкретный термин, используемый в этом проекте.

Таким образом наши тесты, код и документы будут максимально связаны. И нам не придется слишком о них беспокоиться. Можно даже автоматизировать этот процесс и проверить, все ли классы внутри usecases/ имеют директиву .. literalinclude в своей документации.

Этот класс также можно использовать для тестирования нашей user story. Таким образом мы свяжем требования, тесты и собственно логику, реализующую эту user story.

Готово!

Заключение

Это руководство поможет вам писать лучшие user stories, фокусироваться на нуждах пользователей, поддерживать чистоту кода и максимально использовать один и тот же код для различных (но сходных) целей.

2 КОММЕНТАРИИ

  1. Не всё переведено в абзаце про обнаружении неполноты а именно пропущена первая строка and

ОСТАВЬТЕ ОТВЕТ

Please enter your comment!
Please enter your name here