OKR — штука в целом полезная, если знаете, зачем и как её внедрять.
Сегодня я не собираюсь вам продавать OKR как «идею» или «мечту» как способ достичь того, что не получается с помощью обычных средств. Моя цель сейчас — скорее, рассказать вам, как сделать в инструменте удобный механизм ведения Целей и Результатов (а именно так переводятся Objectives and Key Results) их дальнейшего отслеживания. В качестве инструмента будет выступать Jira, в качестве дополнительного удобства — плагин Structure с его удобными фишками. Рассказ будет поделен на 2 части — о создании механизма и о создании мониторинга.
В конце вас ждёт инструмент, который поможет вам сэкономить на покупке платного плагина для Jira. И который вы сделаете сами с помощью этого гайда.
Часть 1. Создаём механизм
На этапе, когда в компании началось массовое внедрение и команды начали знакомиться с этим подходом, предлагалось ведение всех целей в таблицах и файлах-документах. Для одной команды — вполне себе неплохой вариант, но когда команд за 30 и часть Целей зависит от других Целей, то понять общую картину в куче Excel-таблиц, где каждая команда, хоть и соблюдая общие принципы, но всё же ведет все по-своему — становится крайне тяжело. И мне это очень не понравилось.
Я решил попробовать найти или сделать механизм для этого.
В общем-то на Atlassian Marketplace есть уже готовые плагины для этого. Берём и покупаем их, ставим — и команды становятся счастливы и довольны.
Всем спасибо за внимание, ставьте лайки, пока!
…
Создаем проект
Ах, да… У нас же нет официальной возможности сейчас купить плагины. Неприятная ситуация. Ладно. Надо хоть посмотреть, что они делают, в общем, может, получится сделать самому.
Бóльшая часть плагинов под OKR делает достаточно простые вещи: создаёт ряд полей, новые типы связей, в общем, ничего такого, что не может сделать любой админ JIRA. Их основными «киллер-фичами» обычно выступают разного вида дашборды, майндмепы и отчёты. Но про это мы будем говорить во второй части, а сейчас мы постараемся сделать то, что можем без сторонних плагинов.
Так как OKR — это в принципе про «Гласность» и «Открытость», значит, то, что мы хотим получить, должно обладать правами на чтение для всех желающих и в принципе отражать то, что бывает в OKR.
Так что создаём Scrum-проект и называем его Objectives and Key Results, ключом, конечно, делаем OKR.

Почему Scrum? Достаточно просто. В качестве временного интервала у нас выбрали квартал, а я решил, что это будет удобно и такой квартал можно смело рассматривать как Спринт (чисто с функциональной точки зрения).
Создаем типы задач и их схему
Так, теперь нам надо наполнить наш проект. По сути, в методологии есть два типа активностей — Цель и Ключевой результат. При этом мы знаем, что одна Цель может измеряться несколькими ключевыми результатами. Это выглядит прямо как наши типы задач. Создаём схему типов задач, где основным уровнем будет Цель, а на уровне подзадач — Ключевой результат.

Создаем Бизнес-процессы
Далее — Бизнес-процессы. Для нашего эксперимента нужен достаточно простой бизнес-процесс, так что четырёх статусов достаточно. Зачем нам нужен Closed? В этом статусе мы считаем, что задача полностью завершена и оттуда не возвращается, у такой задачи есть Resolution. Этот бизнес-процесс распространяется и на Цель, и на Ключевой результат.

Создаём нужные нам поля
Теперь нам надо определиться с дополнительными полями, которые нам понадобиться.
Вот мой список:
1. Название: Objective Level (Уровень цели)
Тип: Select List (cascading)
Значения:
Компания
InfoWatch
Департамент
Продукты
Разработка
Сервисы
Команда
Первая
Вторая
Третья
Назначение: это поле будет показывать, на каком уровне существует цель. Так как команды, в которых ставятся цели, могут быть на разном уровне, а самые верхние Цели ставятся на уровне всей компании (или на уровне отдельного ЮЛ) — то трёх уровней нам достаточно
2. Название: Current Value (Текущее значение)
Тип: Number Field
Значения: любое числовое
Назначение: текущее значение для Ключевого результата. Будет отображать последнее изменение в команде
3. Название: Planned Value (Целевое значение)
Тип: Number Field
Значения: любое числовое
Назначение: целевое или плановое значение. Это значение ключевого результата, к которому команда будет стремиться
4. Название: Start Value (Исходное значение)
Тип: Number Field
Значения: любое числовое
Назначение: исходное или стартовое значение. Для того чтобы понять, как в динамике изменяются показатели, командам надо от чего-то отталкиваться. Для этого используется исходное значение, то есть то, с чего команда начинает, когда оценивает свою Цель по Ключевым результатам.
5. Название: Method (Метод измерения)
Тип: Select List (single choice)
Значения:
штуки
рубли
%
бинарное
минуты
часы
дни
гигабайты
Назначение: собственно, это то, в чём мы будем измерять Ключевые результаты. Мой список основан на тех показателях, которые выработали команды в своей работе: денежные, временные, штучные, процентные, объёмные и бинарное значение. Да, в методике OKR стараются избегать бинарных ключевых результатов, то есть, когда цель «Хотим сделать хорошо» и к ней два результата «Хорошо» и «Плохо», однако иногда (к примеру, в качестве одного из пяти Результатов) от этого отказываются. Кроме того, команды в то время у нас только учились, и лучше учиться на ошибках, поэтому этот вариант я оставил.
6. Название: Method direction (Направление измерения)
Тип: Select List (single choice)
Значения:
Увеличение
Уменьшение
Назначение: это направление измерения. Оно нам потребуется дальше для мониторинга. Ключевой результат может идти в сторону Увеличения — «Прибыль увеличена с 100 000 рублей до 400 000 рублей», так и в сторону Уменьшения — «Время отклика системы уменьшено с 3 секунд до 1 секунды»
7. Название: Сonfidence (Уверенность)
Тип: Select List (single choice)
Значения:
1
2
3
4
5
6
7
8
9
10
Назначение: одним из элементов для OKR также является и уровень Уверенности команды в том, что они реализуют тот или иной Ключевой результат. Команды решили оценивать его по 10-бальной шкале, так что 0 тут отсутствует. Также тут нет свободного числового ввода, так как 10-бальная шкала достаточна для понимания, и дроби лишь будут размывать общий взгляд команды на её уверенность
Создаём экраны и конфигурации полей
Теперь, когда мы разобрались с полями, нам нужно настроить экраны. Для старта я решил, что достаточно одного общего экрана на создание, просмотр и редактирование для Цели, где, по сути, обычные базовые поля и Уровень цели:

Также понадобился один общий экран для Ключевого результата.

А так как у нас простой процесс — то экраны на переходах нам не нужны.
В Конфигурации полей я лишь добавил обязательность для Уровня цели, так как этот параметр будет отделять «Зерна от плевел»

Что ещё нужно?
Схема приоритетов — базовая, на усмотрение команд.
Связи между задачами — Скорее всего, у вас уже есть в системе какой-нибудь тип связи, реализующий отношения Родитель-> Ребёнок. Если да — используйте его, если нет — то его следует добавить. Такая связь будет показывать, как цель команды будет реализовывать и влиять на Цель департамента, а та в свою очередь — на цель компании.
Схемы безопасности — как писалось выше — все доступно всем сотрудникам на просмотр — нет особых ограничений.
Схемы ограничений — По сути есть 2 роли — Владелец Цели и Ключевого результата и Наблюдатель. Первым даём права на всё, что можно (наверное, без администраторов проекта), вторым — только права на просмотр. Я решил, что Assignee (исполнитель) — это и будет Владелец Цели и владелец Ключевого результата. Так как команды периодически собираются для того, чтобы обсудить, как всё проходит, и Владельцы по сути рассказывают о данных им целям и каких результатов удалось достичь — это явно исполнитель в обычном понимании JIRA.
Готовый механизм
В итоге получился проект в JIRA, в котором состоят команды, Владельцы целей, да и вообще любой человек, который имеет доступ. Команды разного уровня могут фиксировать свои Цели и Ключевые результаты. В качестве временного интервала используется спринт длиной в 1 квартал. Так команды могут проводить Квартальное планирование целей с помощью стандартного функционала планирования спринта и аналогично включать или исключать задачи из своих целей. Все прозрачно для команд и каждому владельцу продукта легко найти свои цели. Часть целей выстраивается в систему взаимоотношений, показывающих, какая на какую влияет и как это всё взаимосвязано.


Часть 2. Создаём Мониторинг
Как я писал в начале первой части, сторонние плагины, помимо добавления полей и связей, добавляют ещё дополнительную отчётность. Когда был реализован механизм, стало понятно, что для нашей компании, у которой около 20 команд разного уровня и у каждой по 2-3 Цели с 3-5 Ключевыми результатами, механизм хоть и рабочий, но не совсем прозрачный.
Точнее этому механизму не хватает «Взгляда сверху», где можно было бы просто и быстро ориентироваться и переключаться между командами, в динамике смотреть на их прогрессию и как-то помогать им. Подумав немного, я понял, что в решении этого вопроса нам поможет Structure — хороший и очень мощный плагин, обладающий своим псевдо-языком, который поможет сделать собственный отчёт. Что ж, этим и займёмся.
Создаем Структуру
Собственно, для начала я создал обычную пустую структуру и назвал её OKR.


Так как ранее были добавлены связи разного уровня, и я понимал, что есть Цели, связанные с Целями компании, так и есть Цели. Наоборот, не связанные с Целями компании, то добавил две папочки — для будущего удобства отслеживания.

Теперь надо наполнить их.
Сначала добавилJQL выборку на основе:
project =OKR and issuetype ="Objective" and "Objective Level" = Компания
Затем сгруппировал их по спринтам, добавил связанные подзадачи типа Ключевой результат и добавил связанные по существующему у нас типу связи Родитель-> Ребёнок.
Для второй папочки сделал аналогично. Тут можно заметить, что используется issuefunction из плагина ScriptRunner для проверки отсутствия типа ссылок, но её можно заменить другими функциями JQL.

Теперь уже стало красиво. Видны простые связи и их дерево. Тут сразу хочется спросить — и за это сторонние производители просят 50 $ в месяц, за такой плагин? Ну да ладно, главное — я уже доволен первыми шагами.
Даём доступы
Теперь надо бы дать доступ к структуре.
Для начала переходим в настройки самой структуры.

Жмём на шестерёнку.

Теперь даём всем и каждому доступ к этой структуре.

Хорошо, с данными доступ есть. Я хотел ещё, чтобы у всех всё выглядело одинаково, поэтому создал отображение (View). Передвигаем местами парочку столбцов справа, в отображениях появится вариант «Сохранить как». Через него сохраняем Отображение.

Теперь лезем в настройки самого Отображения

И уже в деталях настраиваем.

Горизонтальное скроллирование.

Доступ к использованию для всех.

И ассоциирование с данной структурой. Теперь любой, кто перейдёт на созданную структуру, увидит этот вид по умолчанию

Настраиваем основные поля
Так как уже есть общее отображение, которое будет включаться по умолчанию, теперь нужно добавить несколько полей.
Для быстрого анализа нам точно понадобятся поля (добавляются к структуре через + справа у списка полей):
Название |
Тип |
Field |
Владелец (я выше писал про это) |
Field |
Assignee |
Исходное |
Field |
Start Value |
Текущее |
Field |
Current Value |
Целевое |
Field |
Planned Value |
Уровень цели |
Field |
Objective Level |
Измерение |
Field |
Method |
Направление |
Field |
Method direction |
Уверенность |
Field |
Сonfidence |
Key |
Field |
Key |
Summary |
Summary |
Ставим галочки на свое усмотрение · Show Description · Show Icons · Show Sprint and Version attributes |
Стало ещё красивее. Быстро и просто можно отслеживать Цели и Ключевые результаты (главное — после внесений изменений зайти в Отображение и сохранить изменения). Добавить можно и другие поля — Статус (status), например, или Решение (resolution)

Включаем красоту на максимум.
Вообще вся эта статья, обе её части, была написана ради этого раздела. Да и изначальная проблема, которая была у меня, решалась именно этой частью, так как все остальные действия до этого — это почти базовая функциональность, и любой опытный пользователь ПК сможет такое настроить.
Но вот дальше будет интереснее.
Я постараюсь объяснить каждый элемент — почему и зачем я его добавляю и как он работает. Но для начала я очень настоятельно рекомендую ознакомиться с документацией, особенно с разделом по работе Формул (Formula), так как вся магия будет заключаться именно в нём.
Но давайте по порядку.
В листингах я так же постарался добавить комментарии.
В итоге мы добавим разные и удобные столбцы для понимания ситуации в целом с высоты птичьего полета, которые будут выглядеть так:

Столбец «Ошибки»
Задача столбца — подсветить быстро ошибки, которые могли допустить Владельцы Целей и Ключевых Результатов. Это будет полезно, особенно в начале квартала, когда происходит квартальное планирование Целей.

Переменные называются как соответствующие поля
CONCAT(
//Пустой Владелец Цели
IF (issuetype='Key Result' OR issuetype='Objective';
IF (assignee=undefined;"{panel:bgColor=#900020}{color:white}А кто Владелец Цели?{color}{panel}";'')
;'')
;
//Нет текущего значения
IF (issuetype='Key Result';
IF (currentvalue=undefined;"{panel:bgColor=#900020}{color:white}Не знаем сколько сейчас{color}{panel}";'')
;'')
;
//Нет планового значения
IF (issuetype='Key Result';
IF (plannedvalue=undefined;"{panel:bgColor=#900020}{color:white}Не знаем чего надо добиться{color}{panel}";'')
;'')
;
//Пустая Уверенность
IF (issuetype='Key Result';
IF (conf=undefined;"{panel:bgColor=#900020}{color:white}Уверены?{color}{panel}";'')
;'')
;
// Нет того в чем мерием Ключевой Результат
IF (issuetype='Key Result' ;
IF (method=undefined;"{panel:bgColor=#900020}{color:white}А в чем измеряем?{color}{panel}";'')
;'')
)
Столбец «Ход Достижения»
Этот столбец призван рассчитывать процент достижения Ключевого Результата. Формула есть в общей методологии, но тут в дополнение также считается и достижение Цели через среднее арифметическое Ключевых результатов. Благодаря тому, что Результаты мы сделали подзадачами, они воспринимаются как Листья у дерева, а так как мы дополнили структуру связями — Цель компании будет считаться как среднее арифметическое целей Департаментов и Ключевых результатов компании, что даст более точный процент.

Тут переменные называются как соответствующие поля
// Вычисляем среднее-арифметическое листиков
AVG#leaves{
//Рассматриваем в двух разных случаях
CASE ( methodDirection,
// Если у варианта с уменьшением у нас не определено Начальное или Текущее значение ИЛИ если у нас нет разницы между между текущим и начальным, то прописываем 0, иначе считаем процент.
"Уменьшение", IF ((currentvalue-startvalue)=0 OR (currentvalue-startvalue)=undefined;
0;
1/((plannedvalue-startvalue)/(currentvalue-startvalue))
),
// Если у варианта с увеличением у нас не определено Начальное или Целевое значение ИЛИ если у нас нет разницы между между Целевым и Начальным, то прописываем 0, иначе считаем процент.
"Увеличение", IF ((plannedvalue-startvalue)=0 OR (plannedvalue-startvalue)=undefined;
0;
(currentvalue-startvalue)/(plannedvalue-startvalue)
)
)
}
Столбец «Прогресс»
В структурах уже существует готовый столбец с отображением прогресса, но в данном случае он не совсем подходит, так как по Ключевому результату может быть спокойно достигнуто значение, превышающее Целевое значение. Это не значит, что команда не справилась или что сделала что-то плохое, однако это следует подсвечивать, чтобы в будущем она трезвее смотрела на свои оценки.

Переменная customProgress тут является ссылкой на поле «Ход Достижения», рассчитанное выше.
// Функция которая будет рисовать прогресс-бар из серых и зеленых квадратиков.
WITH simpleProgressBar(progress, maxProgress, stepCount) = (
WITH bars(count, color) = """{color:$color}${REPEAT("■", count)}{color}""":
WITH doneBarsCount = FLOOR(progress / maxProgress * stepCount):
bars(doneBarsCount, "lightgreen") CONCAT _bars(stepCount - doneBarsCount, "gray")
):
// Для любой Цели и ключевого результата рисуем квадратики
IF (issuetype="Key Result" OR issuetype="Objective";
simpleProgressBar(customProgress, 1, 10);
undefined
)
Столбец «Состояние»
Прогресс-бар — это, конечно, очень неплохо, но хотелось бы что-нибудь попроще и полегче. Самым простым, на мой взгляд, стали Смайлики и простые фразы-лозунги, что я и сделал в этом столбце.

Переменная hoddostijenia тут является ссылкой на поле «Ход Достижения», рассчитанное выше.
// Функция красивого форматирования панелек
with FORMAT_CAPTION(image, color, caption, offset1, offset2) = (
with SPACE(pixels) = """!https://upload.wikimedia.org/wikipedia/commons/5/52/Spacer.gif|width=$pixels!""":
"""{panel:borderStyle=inset|borderColor=white|bgColor=$color} !$image|width=20,height=20! ${SPACE(offset1)}{color:white}$caption{color}${SPACE(offset2)}{panel}"""
):
//для Целей и Ключевых результатов
IF (issuetype="Key Result" OR issuetype="Objective";
// Делаем панельки от итогов
IF hoddostijenia < 0:
FORMAT_CAPTION("https://lh3.google.com/u/0/d/19uzgf1XoSVOgJqXjoJKSjKGZggnfV83A=w2760-h1400-iv1", "#FF0000", "*ПРОБЛЕМА* ", 1, 1)
ELSE IF hoddostijenia < 0.10:
FORMAT_CAPTION("https://lh3.google.com/u/0/d/19uzgf1XoSVOgJqXjoJKSjKGZggnfV83A=w2760-h1400-iv1", "#EF4B59", "*Далеко до цели* ", 1, 1)
ELSE IF hoddostijenia < 0.30:
FORMAT_CAPTION("https://lh3.google.com/u/0/d/19uzgf1XoSVOgJqXjoJKSjKGZggnfV83A=w2760-h1400-iv1", "#ff7518", "*Первые шаги* ", 1, 1)
ELSE IF hoddostijenia < 0.50:
FORMAT_CAPTION("https://lh3.google.com/u/0/d/19uzgf1XoSVOgJqXjoJKSjKGZggnfV83A=w2760-h1400-iv1", "#ffbb29", "*Разгоняемся* ", 1, 1)
ELSE IF hoddostijenia < 0.70:
FORMAT_CAPTION("https://lh3.google.com/u/0/d/1pAa-BvSvKCiPv1oplNFbkih1t7wuT2A6=w2880-h1430-iv1", "#ABB32E", "*Видим цель*", 2, 2)
ELSE IF hoddostijenia < 0.90:
FORMAT_CAPTION("https://lh3.google.com/u/0/d/1pAa-BvSvKCiPv1oplNFbkih1t7wuT2A6=w2880-h1430-iv1", "#6ad902", "*Уже близко*", 2, 2)
ELSE IF hoddostijenia < 1.01:
FORMAT_CAPTION("https://lh3.google.com/u/0/d/1pNS0ctx-1T8z5s73rT_IsJmxV5fNhSKF=w2880-h1430-iv1", "#00b34a", "*ОТЛИЧНО!*", 2, 2)
ELSE:
FORMAT_CAPTION("https://lh3.google.com/u/0/d/1pNS0ctx-1T8z5s73rT_IsJmxV5fNhSKF=w2880-h1430-iv1", "#228B22", "*СУПЕР!!!*", 2, 2)
;
undefined
)
Столбец «-1w. Значение»
Понимать текущее значение, которое мы выводим, уже неплохо. Но что было неделю назад? Надо же теперь лезть в каждую задачу и смотреть по журналу изменений за историческим значением. А мне лень. В структурах в Формулах с определённой версии появилась возможность обращаться к истории изменений задачи. Поэтому я сделал вот что:

Переменная this зарегистрирована в системе и определяется как Item, history — так же зарегистрирована и определяется как History
// добавляем функцию которая возвращает текущее время
with begin=now():
// добавляем функцию которая возвращает на неделю раньше чем текущее время
with end=now()-7*86400000:
// Записи в историю изменений делаются относительно языка системы у пользователя, производившего изменения. У нас Английский и Русский, по этому надо будет выбирать последнюю запись еще и в зависимости от языка.
IF(
IF(
// ищем запись в истории изменений по времени от текущее минус 1 неделя до текущего времени. Если такая есть — берем её, иначе возвращаем 0
history.changes.FILTER($.field = "Current Value").changeGroup.FILTER($.timestamp<begin).FILTER(end> $.timestamp).LAST().timestamp>0;
history.changes.FILTER($.field = "Current Value").changeGroup.FILTER($.timestamp<begin ).FILTER(end> $.timestamp).LAST().timestamp;
0)
> IF(
// аналогично но уже для русского языка ищем значение в истории изменений
history.changes.FILTER($.field = "Текущее значение").changeGroup.FILTER($.timestamp<begin ).FILTER(end> $.timestamp).LAST().timestamp>0;
history.changes.FILTER($.field = "Текущее значение").changeGroup.FILTER($.timestamp<begin ).FILTER(end> $.timestamp).LAST().timestamp;
0) ;
//выводим то значение, у которого время ближе к текущему минус 1 неделя
historical_value(this, "Current Value", end);
historical_value(this, "Текущее значение", end)
)
Аналогично, изменяя либо значение в функциях begin и end, мы можем поискать данные в других временных отрезках, либо заменив Current Value и Текущее значение поле, к примеру, Уверенность — можно найти информацию по историческому значению другого поля.
Столбец «Текущее»
С одной стороны, столбец с текущим значением мы уже вывели, но с другой — хотелось бы быстро понимать некоторую динамику. Значение растёт, падает или никак не меняется? Так что пришлось его тоже запихнуть в формулу.

Переменная onewcurrenvalue — это значение из столбца -1w. Значение, которое мы рассчитали выше.
// добавляем функцию которая возвращает текущее время
with begin=now():
// добавляем функцию которая возвращает на неделю раньше чем текущее время
with end=now()-7*86400000:
// Записи в историю изменений делаются относительно языка системы у пользователя, производившего изменения. У нас Английский и Русский, по этому надо будет выбирать последнюю запись еще и в зависимости от языка.
IF(
IF(
// ищем запись в истории изменений по времени от текущее минус 1 неделя до текущего времени. Если такая есть - берем её, иначе возвращаем 0
history.changes.FILTER($.field = "Current Value").changeGroup.FILTER($.timestamp<begin).FILTER(end> $.timestamp).LAST().timestamp>0;
history.changes.FILTER($.field = "Current Value").changeGroup.FILTER($.timestamp<begin ).FILTER(end> $.timestamp).LAST().timestamp;
0)
> IF(
// аналогично но уже для русского языка ищем значение в истории изменений
history.changes.FILTER($.field = "Текущее значение").changeGroup.FILTER($.timestamp<begin ).FILTER(end> $.timestamp).LAST().timestamp>0;
history.changes.FILTER($.field = "Текущее значение").changeGroup.FILTER($.timestamp<begin ).FILTER(end> $.timestamp).LAST().timestamp;
0) ;
//выводим то значение, у которого время ближе к текущему минус 1 неделя
historical_value(this, "Current Value", end);
historical_value(this, "Текущее значение", end)
)
Заключение
Мы разобрали, как своими силами настроить механизм для ведения Целей и Ключевых результатов в Jira, когда нет возможности (или желания) покупать сторонние плагины. Получился удобный, простой и прозрачный инструмент. Самостоятельная настройка формул позволяет максимально адаптировать инструмент под нужды организации.
Если честно, вынужден признать, что механизм я создавал где-то один рабочий день, а вот на мониторинг (и, в частности, на проблему с Историей изменений, в которую пишутся записи на языке пользователя) — около рабочей недели.
Надеюсь, этот гайд будет полезен как тем, кто хочет попробовать что-либо у себя сделать по OKR, так и тем, кто экспериментирует со структурами в Jira.