И как это может помочь вам найти своего MVP

Что такое пользовательская история?

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

Как выглядит пользовательская история?

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

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

Хорошо написанная пользовательская история устранит двусмысленность вокруг этого:

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

Давайте разберемся с этим.

ВОЗ"

Первая часть пользовательской истории - это простой вопрос: кому нужна эта функция? Сначала это кажется очевидным, но иногда это не так. Прежде чем я научился быть Agile (с намеренно прописной буквой A), я помню конкретный проект, который я наполнил функциями, потому что думал, что они сделают его лучше. Некоторые из этих функций были полезны, но многие из них были просто вещами, которые я считал нужными или считал крутыми. Когда приложение было запущено, многие функции на самом деле не использовались, потому что я не думал о своих пользователях. На вопрос «кто?» заставляет думать о пользователе превыше всего.

"Почему"

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

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

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

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

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

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

Что"

Это наименее важная часть истории, но нельзя сказать, что это неважно. Возможно, на самом деле я имею в виду: «Если вы знаете проблему, любое решение, которое ее решает, должно принести пользу».

Обычно это первая часть истории, которую люди озвучивают, поэтому в итоге мы получаем излишне предписывающие истории, подобные приведенной выше. «Мне нужна кнопка сохранения» не учитывает, что для конечного пользователя существует более простое решение. Более сложным примером может быть совместное редактирование документов. Вспомните дни, когда еще не было Google Документов и тому подобного. Если бы вы тогда писали редактор документов, вы, возможно, видели пользовательскую историю, которая выглядела примерно так:

Как автор документа
Чтобы другие пользователи могли вносить свой вклад в мою работу
Я хочу, чтобы любой автор мог заблокировать документ для редактирования

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

Разработчик: «Можно ли, если мы позволим двум людям редактировать документ одновременно?»
Владелец продукта: «Вы можете это сделать?»
Разработчик: «Да»
Продукт Владелец: * взрывается разум *

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

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

Критерии приемки

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

Учитывая, что я в настоящее время редактирую документ A
Когда другой пользователь начинает редактировать документ A
Тогда, я должен быть уведомлен, что другой пользователь начал редактирование, не прерывая мой рабочий процесс

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

Давайте рассмотрим анатомию критериев приемлемости.

Данный

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

Я часто вижу что-то вроде этого:

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

Это те же критерии, но «данное» плохо продумано, а формулировка гораздо менее ясна. Ключ обычно кроется в четком повторении слов «дано» и «когда». Это означает, что тот, кто написал критерии приемлемости, не понимал цели слова «дано».

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

Также вполне приемлемо иметь несколько условий, например:

Учитывая, что я в настоящее время редактирую документ A
И другой пользователь также редактирует документ A
Когда другой пользователь делает изменение
Тогда я сразу увижу изменение

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

Учитывая, что продукт A существует
И продукт A имеет цену 10 фунтов стерлингов,
Когда у меня есть 2 единицы продукта A в моей корзине
Тогда общая сумма моей корзины должна составлять 20 фунтов стерлингов.

В этом случае можно предположить, что Продукт А существует, потому что в противном случае у него не было бы цены.

Когда

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

«Когда» - это часть критериев приемлемости, которую часто пишут плохо. Возьмем пример электронной торговли:

Учитывая, у меня в корзине 2 единицы продукта A
И продукт A имеет цену 10 фунтов стерлингов
Когда Я просматриваю свою корзину
Тогда общая сумма моей корзины должна составлять 20 фунтов стерлингов.

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

Это часто легко обнаружить, потому что в конечном итоге вы получаете множество критериев приемлемости, которые разделяют одно и то же «когда». Другой недостаток заключается в том, что триггер - это не конкретный момент времени. Чтобы это правильно переписать, оно должно выглядеть так:

Учитывая, что общая сумма моей корзины в настоящее время составляет 10 фунтов стерлингов
И продукт А имеет цену 10 фунтов стерлингов
Когда я добавляю 1 единица продукта А в мою корзину
Тогда общая сумма моей корзины должна составлять 20 фунтов стерлингов

Это делает более понятным, в какой момент следует выполнить изменение.

потом

Эта часть похожа на «Я хочу» в описании истории. Обычно это самая легкая часть для написания, так как это то, о чем люди думают, когда они составляют список неструктурированных критериев приемлемости:

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

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

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

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

Сколько критериев приема я должен написать?

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

Чтобы проиллюстрировать это, давайте рассмотрим следующие критерии приемлемости из нашей истории сотрудничества:

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

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

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

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

Минимально жизнеспособный продукт

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

Но что, если бы мы могли сделать его похожим на это?

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

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

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

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