Как мы настроили покер планирования в Youtrack и выиграли дополнительное время
В этой статье расскажем, как совместили Youtrack и покер планирования.
Однажды мы решили улучшить процесс оценки задач. В других подразделениях компании уже был успешный опыт применения покер планирований. Мы вдохновились опытом коллег и, прочитав несколько тематических статей, решили использовать методику в работе.
Наш основной инструмент для ведения задач и планирования – это Youtrack. Нам очень нравится его гибкость. Что, если попробовать совместить Youtrack и покер планирования?
Готовое решение найти не удалось, а потому мы реализовали свой вариант на основе рабочих процессов Youtrack. В этой статье вы найдете описание решения и особенности настройки.
Разберёмся, что есть что
Youtrack – это инструмент управления проектами, созданный специально для Agile-команд. Подробнее об инструменте можно почитать по ссылке: https://www.jetbrains.com/ru-ru/youtrack/
Покер планирования – это техника, основанная на достижении договорённости и используемая для оценки трудоемкости задач в ходе разработки программного обеспечения.
Про технику покер планирования можно почитать, например, здесь:
Planning Poker: как сделать процесс постановки задач максимально прозрачным и четким
или здесь:
Как дать адекватную оценку времени, когда неопределённость бьёт по башке
Наш подход
Мы выбрали покер планирования, чтобы команда разработки быстрее оценивала сложность задач.
Так как задачи команда ведет в Youtrack, то и оценивать их мы решили там же: это удобно, поскольку в Youtrack есть все требования по задаче и подготовленное техническое задание. Это позволяет не копировать задачу в сторонние приложения и не использовать дополнительные инструменты для оценки.
Как проходит оценка:
Сначала задача пребывает в состоянии «Анализ». Аналитик выполняет сбор требований и готовит техническое задание.
Затем аналитик переводит задачу в состояние «Оценка» и выбирает в качестве исполнителя разработчика.
Разработчик привлекает к оценке задачи ещё двух участников из команды разработки. Каждый участник в скрытом режиме ставит свою оценку. Как только все участники проголосуют, общий результат голосования отобразится автоматически.
Если результаты голосования неоднозначные, то участники голосования после обсуждения могут вернуть задачу аналитику – для уточнения требований.
Если же участники пришли к общему мнению (в результате голосования или по итогам обсуждения), то в задаче проставляется итоговая оценка. Меняется и статус задачи – «В очереди». Считается, что требования по такой задаче полностью понятны, трудозатраты оцифрованы, и можно включить ее в один из ближайших спринтов.
Для реализации описанного алгоритма в задаче Youtrack есть поля: одно для скрытого голосования, а второе для отображения результата. Правила окончания голосования можно придумать любые. Например, мы решили, что голосование завершается в тот момент, когда оценку по задаче проставили три участника.
Вот как выглядит голосование в задаче Youtrack в нашем проекте.
Голосование не завершено (результат скрыт):
Голосование завершено (результат виден):
Мы используем технику покер планирования для быстрой оценки большинства задач. Качество оценки повышается, поскольку учитывается мнение нескольких участников команды.
Если быстрая оценка по задаче невозможна и требуется более глубокая проработка технического решения, то это выходит за рамки покер планирований и может потребовать дополнительного времени. В таком случае проработку технического решения и детальную оценку трудозатрат берет на себя один из разработчиков. Затем команда вновь оценивает предложенное решение.
Теперь подробно рассмотрим, как настроить Youtrack для оценки задач по методике «покер планирования».
Настройка проекта
Для оценки задач создадим в проекте Youtrack три дополнительных поля:
Покер план. Оценка – для голосования по задаче.
тип: перечисление (одиночное значение);
возможные значения: ⍰,1,2,3,5,8,13,21,34,∞. За основу взяли числа Фибоначчи и добавили два значения: ⍰ - требования не понятны и ∞ – требования понятны, но задача не может быть решена в конечные сроки;
значение этого поля будет очищаться сразу после простановки оценки, чтобы другие участники не узнали результат до завершения голосования.
Покер план. Результат – отображение результатов голосования.
тип: строковое значение;
в этом поле будем выводить результат голосования. До завершения голосования отображаем только список проголосовавших участников, а после завершения голосования отображаем участников и выбранные оценки.
Покер план. Данные.
тип: строковое значение;
скрытое техническое поле, доступное на просмотр только администратору;
в этом поле будем хранить промежуточные итоги голосования в виде JSON.
Видимость полей в проекте настраиваем через свойство «Отображать для» в дополнительных параметрах полей. Например, поле «Покер план. Данные» доступно только пользователям, входящим в группу «Покер план. админ». Обычный участник команды не видит содержимое этого поля, так как оно по сути является техническим.
Поля «Покер план. Оценка» и «Покер план. Результат» доступны для просмотра только участникам команды, входящим в специальную группу покер планирования. Так удобнее управлять процессом и скрывать лишнюю информацию в Youtrack при необходимости.
Поле «Покер план. Результат» доступно для редактирования (настройка «Могут обновлять») только участникам группы «Покер план. админ». Остальные участники могут только просматривать содержимое поля без возможности редактировать.
Фактически у нас нет участников, которые входят в группу «Покер план. админ». Группа создана только для того, чтобы обеспечить режим «только чтение» для поля «Покер план. Результат» и скрыть поле «Покер план. Данные». Прямое редактирование указанных полей не разрешается.
Настройка рабочего процесса
Итак, мы настроили проект и добавили все необходимые поля. Однако чтобы всё заработало так, как задумано, необходимо реализовать логику голосования и отображения результата. Для этой задачи будем использовать «Рабочие процессы» Youtrack, которые позволяют автоматизировать процесс обработки задач на языке JavaScript.
В настройках Youtrack заходим в меню «Рабочие процессы».
Создаем новый рабочий процесс.
В рабочем процессе создаем модуль, который будет выполняться при применении изменений к задачам.
Модуль представляет из себя скрипт, который выполняется при внесении изменений в задачу. Приведу скрипт, который автоматизирует обработку голосования, и прокомментирую несколько основных моментов.
/**
* Метод "Покер планирования" средствами Youtrack
*/
var entities = require('@jetbrains/youtrack-scripting-api/entities');
var MIN_VOTE_COUNT = 3; //минимальное количество проголосовавших
var EMOJI = ['♣', '♠', '♥', '♦'];
//наименования полей для Покер планирования
var FIELD_POKER_VOTE = "Покер план. Оценка"; // поле для голосования
var FIELD_POKER_RESULT = "Покер план. Результат"; // отображение результата
var FIELD_POKER_DATA = "Покер план. Данные"; // хранение результатов голосования
exports.rule = entities.Issue.onChange({
title: 'Poker_plan_vote',
guard: function(ctx) {
//обрабатываем только изменения в поле голосования
return ctx.issue.fields.isChanged(FIELD_POKER_VOTE);
},
action: function(ctx) {
var emojiIndex;
var pokerVote;
var isUserFirstVote;
var issue = ctx.issue;
var user = ctx.currentUser;
//получаем значение оценки, которое выбрал участник
var voteRes = issue.fields[FIELD_POKER_VOTE].name;
//получаем данные по предыдущим результатам голосования
var pokerDataJSON = issue.fields[FIELD_POKER_DATA];
var pokerData = JSON.parse(pokerDataJSON);
var pokerResult = "";
if ((pokerData)&&(pokerData.voteResult)){
//в задаче есть данные по предыдущим результатам голосования
isUserFirstVote = true; //по умолчанию считаем, что участник голосует в первый раз
for(var i=0; i < pokerData.voteResult.length; i++){
//ищем текущего пользователя в списке проголосовавших ранее
pokerVote = pokerData.voteResult[i];
if (pokerVote.login == user.login){
//пользователь найден в результатах. Меняем текущую оценку
pokerVote.vote = voteRes;
//запоминаем, что пользователь голосует не первый раз
isUserFirstVote = false;
}
}
if (isUserFirstVote) {
//участник проголосовал в первый раз. Добавляем нового пользователя и оценку в список
pokerData.voteResult.push({"login": user.login,"vote": voteRes});
}
}else{
//первое голосование по задаче. Создаем список и добавляем в него пользователя и оценку
pokerData = {voteResult:[{"login": user.login,"vote": voteRes}]};
}
if (pokerData.voteResult.length > 0){
//формируем строку для отображения результата голосования
emojiIndex=0;
for(var j=0; j < pokerData.voteResult.length; j++){
pokerVote = pokerData.voteResult[j];
if (pokerResult.length > 0){
pokerResult += "\n"; //добавляем перенос строки
}
if (pokerData.voteResult.length >= MIN_VOTE_COUNT){
//если проголосовало необходимое количество участников - отображаем оценки
pokerResult += pokerVote.vote + "=" + pokerVote.login;
}else{
//если количество участников меньше минимума - отображаем эмодзи вместо оценок
pokerResult += EMOJI[emojiIndex] + "=" + pokerVote.login;
//меняем эмодзи
emojiIndex++;
if (emojiIndex >= EMOJI.length){
//эмодзи закончились, возвращаемся к первому эмодзи
emojiIndex=0;
}
}
}
}
pokerDataJSON = JSON.stringify(pokerData);
//записываем результат в поля задачи
issue.fields[FIELD_POKER_DATA] = pokerDataJSON; //данные по голосованию
issue.fields[FIELD_POKER_RESULT] = pokerResult; //отображаемый результат
//сбрасываем значение поля оценки
issue.fields[FIELD_POKER_VOTE] = null;
},
requirements: {
PokerVote: {
type: entities.EnumField.fieldType,
name: FIELD_POKER_VOTE,
multi: false
},
PokerResult: {
type: entities.Field.stringType,
name: FIELD_POKER_RESULT
},
PokerData: {
type: entities.Field.stringType,
name: FIELD_POKER_DATA
}
}
});
Отмечу несколько ключевых моментов в скрипте, а остальное, надеюсь, будет понятно из комментариев в коде:
Выполнение основной части скрипта происходит только при изменении поля "Покер план. Оценка". Для этого служит проверка в секции guard:
return ctx.issue.fields.isChanged(FIELD_POKER_VOTE);
Доступ к параметрам текущего контекста Youtrack можно получить с помощью объекта ctx. Например, в коде ниже получаем текущую задачу и текущего пользователя.
var issue = ctx.issue; var user = ctx.currentUser;
Для получения значения поля типа «строковое» обращаемся к нему через свойство fields:
issue.fields[FIELD_POKER_DATA];
Для получения значения поля типа «перечисление» дополнительно используем свойство name:
issue.fields[FIELD_POKER_VOTE].name;
Для отладки можно вывести необходимую информацию в консоль рабочего процесса Youtrack:
console.log(issue.fields[FIELD_POKER_DATA]);
Чтобы значение оценки не отобразилось другим участникам голосования перед завершением выполнения скрипта присваиваем полю «Покер план. Оценка» значение null:
issue.fields[FIELD_POKER_VOTE] = null; //сбрасываем значение поля оценки
В блоке requirements описаны обязательные поля, которые необходимы для корректной работы скрипта. Если в проекте отсутствуют указанные поля, то при подключении процесса к проекту напротив процесса отобразится уведомление «требует настройки» и процесс не будет запускаться до тех пор, пока не будут устранены несоответствия требованиям, указанным в блоке requirements.
Скрипт готов к использованию, но, прежде чем подключать рабочий процесс к проекту, рассмотрим ещё одну важную функцию – «Сброс результатов планирования».
Сброс результатов планирования
После обсуждения задачи с командой или уточнения требований по задаче может потребоваться проведение повторной оценки. Для этого необходимо сбросить результаты покер планирования.
Создадим в существующем рабочем процессе дополнительное действие Youtrack для обнуления результатов покер планирования.
В настройках процесса создадим новый модуль типа «Действие». Такие действия доступны в меню задачи и выполняются по команде пользователя.
Приведем текст скрипта, который выполняет очистку результата покер планирования:
/**
* Метод "Покер планирования" средствами Youtrack
* действие: сброс результатов покер планирования
*/
var entities = require('@jetbrains/youtrack-scripting-api/entities');
//наименования полей для Покер планирования
var FIELD_POKER_RESULT = "Покер план. Результат"; // отображение результата
var FIELD_POKER_DATA = "Покер план. Данные"; // хранение результатов голосования
//группа участников Покер планирования
var POKER_PLAN_GROUP = "Покер план. Участники";
exports.rule = entities.Issue.action({
title: 'Сбросить результаты покер планирования',
command: 'poker_plan_clear',
guard: function(ctx) {
//действие доступно только для участников группы покер планирования
return ctx.currentUser.isInGroup(POKER_PLAN_GROUP);
},
action: function(ctx) {
var issue = ctx.issue;
//сбрасываем значения полей, относящихся к покер планированию
issue.fields[FIELD_POKER_DATA] = null;
issue.fields[FIELD_POKER_RESULT] = null;
},
requirements: {
PokerResult: {
type: entities.Field.stringType,
name: FIELD_POKER_RESULT
},
PokerData: {
type: entities.Field.stringType,
name: FIELD_POKER_DATA
}
}
});
Несколько ключевых моментов, на которые следует обратить внимание:
В секции guard мы добавили условие при, котором будет доступно действие в меню. В данном случае пункт меню будет доступен только пользователям, входящим в группу "Покер план. Участники".
Основное действие мы описали в секции action. Для очистки результатов присваиваем значение null соответствующим полям задачи.
В блоке requirements описаны обязательные поля, которые необходимы для корректной работы скрипта. Если в проекте отсутствуют указанные поля, то при подключении процесса к проекту напротив процесса отобразится уведомление «требует настройки» и процесс не будет исполняться до тех пор, пока не будут устранены несоответствия требованиям, указанным в requirements.
Действие будет доступно в меню задачи после подключения процесса к проекту.
Подключение к проекту рабочего процесса
Рабочий процесс настроен, теперь нужно его подключить.
Обратите внимание: как только процесс будет прикреплен к проекту, то в соответствии с заложенным алгоритмом начинает выполняться скрипт. Если в скрипте допущена ошибка, то под угрозой работоспособность всего проекта.
Для подключения процесса необходимо перейти на закладку «Рабочие процессы» в настройках проекта и нажать кнопку «Прикрепить рабочие процессы».
Итак, мы создали и подключили проект; теперь можно голосовать и оценивать задачи в Youtrack.
Наш опыт
Рабочий процесс «покер планирования», встроенный в Youtrack – это удобный инструмент, который позволяет оценивать задачи непосредственно в системе, где ведётся бэклог и где такая оценка наиболее востребована.
Основная особенность покер планирования заключается в том, что голосование проходит в закрытом режиме, и участники не видят оценки друг друга до завершения голосования. Такой подход обеспечивает неангажированность, учитывает сразу несколько экспертных мнений и снижает влияние их якорения. Не менее важно, что, обсуждая итоги голосования, участники команды обмениваются знаниями и лучше понимают суть решаемой задачи.
Голосовать участники могут в разное время и там, где им удобно – нет нужды собираться в назначенный час в одном помещении. Результаты обсуждают только в случае спорных ситуаций и делают это как очно, так и просто в комментариях к задаче. Используя Youtrack, мы повысили гибкость процесса оценки и стали проводить его параллельно разработке, подстраиваясь под текущую загрузку участников команды.
В целом оцениваем эффект от внедрения покер планирования в Youtrack как положительный. У разработчиков появился удобный инструмент для выведения консенсус-оценки по задаче.
Как результат, при оценке задач учитывается несколько экспертных мнений, что повышает качество оценки.
Youtrack позволил минимизировать время, которое мы обычно тратим на обсуждение и оценку задач.
Планы на будущее
В дальнейшем планируем скорректировать критерий окончания голосования и надеемся перейти к прямому указанию участников. При таком подходе результат голосования станет виден только после того, как все участники голосования, явно указанные в задаче, проставят свою оценку. Одним из плюсов такого подхода будет то, что участники смогут находить задачи, по которым ожидается их оценка, с помощью стандартного поиска.
Если новый подход зарекомендует себя, то расскажем о нем в одной из следующих статей.