Единый роадмап компании с помощью Structure Jira
Привет! Меня зовут Настя Николаева, лид цифровой трансформации в компании Bimeister. И я хочу рассказать, как мы собирали единый роадмап компании с помощью плагина Structure Jira.
Наверное, в каждой компании, где есть несколько команд разработки, существуют подобные проблемы:
у каждой команды свои правила и инструменты для построения роадмап
некоторые роадмапы не найти, они не находятся в свободном доступе
продуктовый роадмап и проектные живут в разных местах и не синхронизированы
нет единого общего роадмап, по которому можно увидеть статус продуктовой разработки и проектных обязательств
Решив эти проблемы, мы сможем достигнуть поставленных целей:
Прозрачные и понятные сроки исполнения проектных и продуктовых обязательств
Выявление рисков на ранних этапах исполнения работ
Итак, верхнеуровнево мы наметили такие шаги:
Шаг 1 — выбираем единый инструмент построения роадмап
Так как у нас вся продуктовая разработка ведется в Jira, то и собирать единый роадмап правильно было бы в jira, избежав копи-пасти и дублирования информации.
В процессе ресеча различных плагинов и опыта других компаний мы выбрали Structure в Jira.
Structure — плагин для Jira, с помощью которого можно гибко визуализировать и структурировать задачи в jira в виде иерархии.
Structure позволяет на основе задач jira:
выстроить список задач jira в любой иерархии, по любому нужному запросу либо же вручную по одной задаче
сгруппировать списки по любому атрибуту тикета, отфильтровать ненужные, отсортировать так, как требуется
отобразить и показать требуемые поля тикетов
с помощью формул и языка запросов можно добавить нужные аналитические параметры и отобразить любые данные по тикетам
добавить gantt chart и ресурсы
и многое-многое другое
Шаг 2 — собираем роадмап
Первая проблема, с которой столкнулись — просто так, «сходу», роадмап нам было не собрать, так как все команды ведут тикеты в jira по своим правилам; признаков или каких-то других атрибутов, по которым мы можем одним запросом вытащить все задачи, не было. Следовательно, его нужно было добавить. Для этого сделали несколько доработок по таскам:
унифицировали тип задач feature и story, внедрили единый флоу по ним. Это нам поможет собрать единую структуру роадмап по всем командам разработки
добавили обязательные поля, по которым собираем structure
Это поможет правильно сгруппировать задачи, выстроить модель рисков и сделать связь продуктового роадмап и календарно-сетевого графика проекта.
Дальше создаем структуру, задаем название. Structure создаем пустой, без преднастроек, которые предлагает сам плагин. Всю кастомизацию делаем руками.
Чтобы структуру наполнить можно воспользоваться 2-мя способами:
добавить задачи вручную — это довольно долго, подходит для небольших роадмапов и небольших команд
добавить задачи автоматически по какому-то условию. Этим способом мы и наполняем нашу structure.
Нас интересуют задачи с типом таски = feature, так как это для нас ключевая сущность.
Дальше, для наглядности, фичи нужно сгруппировать. Группировать можно по любому полю тикета jira.
Мы сделаем по Эпику, так как Эпик — это сущность большого раздела для разработки продукта. Получаем список всех эпиков с вложенными в них фичами.
Чтобы разложить эпики по продукту — добавляем группировку по продукту. А чтобы разложить продукты по проектам — группируем по проектам.
Итог: мы можем видеть все проекты компании, в каждом проекте видим продукты, которые подключены, и в каждом продукте видим эпики и фичи, которые разрабатываются для проекта.
Можно через связи добавить к фичам более мелкую детализацию — стори и таски на аналитику и дизайн — для этого так же через автоматизацию добавляем задачи через связь contents или содержит.
Шаг 3 — соединяем продуктовый роадмап и календарно-сетевой график проектов
Базовый вид structure для нас не самый информативный и не содержит нужные нам для анализа данные. Поэтому мы разработали свой вид structure.
Для этого добавили больше столбцов:
приоритет задач
статус задач
название команды, которая делает разработку
lead time по задачам, которые уже выполнены
фактические даты старта и окончания работ — даты продуктовой команды, в которые задачи будут браться в работу
плановые даты старта и окончания работ — проектные даты, которые были согласованы с руководителями проектов и заказчиком, и по которым и строится календарно-сетевые графики проекта
ФТТ — какие требования заказчика по договору решаются в рамках каждого тикета
прогресс по задаче — процент выполнения задачи в зависимости от ее статуса
В итоге что мы получаем — структурированный список задач продуктовой разработки в виде таблицы с прозрачными сроками, статусами по каждой.
А теперь добавим немного наглядности. Мы разработали модель рисков, по которой можно отследить, есть ли у тикета риск быть не выполненным в срок в зависимости от дат, статусов и типа задачи.
За основу мы взяли стандартный параметр Item Health и изменили формулу, по которой определяется риск:
формула:
WITH renderStatus(bgColor, iconId, status, padding) =
"""{panel:borderStyle=solid|borderColor=white|bgColor=#$bgColor}!}*$status*{color}!}""" :
/1 - РИСКИ ДЛЯ ФИЧИ/
if (type="feature"):
(
/*Если статус Бэклог и даты старта еще далеко впереди*/
if (StartDate - TODAY() > 0):
(
if progress = 1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
else if progress = 0 :renderStatus("652CB3", "icn-01", "Работы запланированы", 7)
else : renderStatus("59B161", "icn-01", "Опережаем сроки!", 7)
)
/*Если дата старта сегодня и работы начались*/
else if StartDate=TODAY():
(
if progress = 1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
else if progress>0.1: renderStatus("59B161", "icn-03", "Опережаем сроки!", 7)
else : renderStatus("B610D2", "icn-03", "Нужно поторопиться", 7)
)
/*Если дата старта прошла*/
else if StartDate <= TODAY() :
(
if EndDate - TODAY() > 60: /*до конца более 60 дней*/
(
if progress = 1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
else if progress >= 0.8 : renderStatus("59B161", "icn-01", "Опережаем сроки!", 7)
else if progress >= 0.4 : renderStatus("FFAF00", "icn-02", "В рамках сроков", 7)
else if progress > 0: renderStatus("FFAF00", "icn-02", "В рамках сроков", 7)
else: renderStatus("B610D2", "icn-03", "Нужно поторопиться", 7)
)
else if EndDate - TODAY() > 30: /*до конца более 30 дней*/
(
if progress = 1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
else if progress >= 0.8: renderStatus("FFAF00", "icn-02", "В рамках сроков", 7)
else if progress >= 0.4: renderStatus("B610D2", "icn-03", "Нужно поторопиться", 7)
else if progress > 0: renderStatus("EF4B59", "icn-03", "Есть риск не успеть", 7)
else: renderStatus("EF4B59", "icn-03", "Есть риск не успеть", 7)
)
else if EndDate - TODAY() > 0: /*до конца более 0 дней*/
(
if progress = 1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
else if progress >= 0.8: renderStatus("FFAF00", "icn-02", "В рамках сроков", 7)
else if progress >= 0.4: renderStatus("B610D2", "icn-03", "Нужно поторопиться", 7)
else if progress > 0: renderStatus("EF4B59", "icn-03", "Есть риск не успеть", 7)
else: renderStatus("EF4B59", "icn-03", "Есть риск не успеть", 7)
)
/*deadline прошел*/
else if EndDate <= TODAY():
(
if progress =1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
else if progress >= 0.8: renderStatus("EF4B59", "icn-03", "Deadline наступил!", 7)
else if progress >= 0.4: renderStatus("EF4B59", "icn-03", "Deadline наступил!", 7)
else if progress > 0: renderStatus("EF4B59", "icn-03", "Deadline наступил!", 7)
else: renderStatus("EF4B59", "icn-03", "Deadline наступил!", 7)
)
)
/*Если прогресс=1*/
else if progress =1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
/*Если даты не заполнены*/
else : renderStatus("000000", "icn-03", "Незаполнены даты", 7)
)
/-------------------------------------------------------------------/
/2 - РИСКИ ДЛЯ СТОРЕЙ И ТАСОК/
else if (type="Story" or type="Task"):
(
/*Если статус Бэклог и даты старта еще далеко впереди*/
if (StartDate - TODAY() > 0):
(
if progress = 1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
else if progress = 0 :renderStatus("652CB3", "icn-01", "Работы запланированы", 7)
else : renderStatus("59B161", "icn-01", "Опережаем сроки!", 7)
)
/*Если дата старта сегодня и работы начались*/
else if StartDate=TODAY():
(
if progress = 1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
else if progress>0.1: renderStatus("59B161", "icn-03", "Опережаем сроки!", 7)
else : renderStatus("B610D2", "icn-03", "Нужно поторопиться", 7)
)
/*Если дата старта прошла*/
else if StartDate <= TODAY() :
(
if EndDate - TODAY() > 7: /*до конца более 7 дней*/
(
if progress = 1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
else if progress >= 0.8 : renderStatus("59B161", "icn-01", "Опережаем сроки!", 7)
else if progress >= 0.4 : renderStatus("FFAF00", "icn-02", "В рамках сроков", 7)
else if progress > 0: renderStatus("FFAF00", "icn-02", "В рамках сроков", 7)
else: renderStatus("B610D2", "icn-03", "Нужно поторопиться", 7)
)
else if EndDate - TODAY() > 4: /*до конца более 4 дней*/
(
if progress = 1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
else if progress >= 0.8: renderStatus("FFAF00", "icn-02", "В рамках сроков", 7)
else if progress >= 0.4: renderStatus("B610D2", "icn-03", "Нужно поторопиться", 7)
else if progress > 0: renderStatus("EF4B59", "icn-03", "Есть риск не успеть", 7)
else: renderStatus("EF4B59", "icn-03", "Есть риск не успеть", 7)
)
else if EndDate - TODAY() > 0: /*до конца более 0 дней*/
(
if progress = 1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
else if progress >= 0.8: renderStatus("FFAF00", "icn-02", "В рамках сроков", 7)
else if progress >= 0.4: renderStatus("B610D2", "icn-03", "Нужно поторопиться", 7)
else if progress > 0: renderStatus("EF4B59", "icn-03", "Есть риск не успеть", 7)
else: renderStatus("EF4B59", "icn-03", "Есть риск не успеть", 7)
)
/*deadline прошел*/
else if EndDate <= TODAY():
(
if progress =1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
else if progress >= 0.8: renderStatus("EF4B59", "icn-03", "Deadline наступил!", 7)
else if progress >= 0.4: renderStatus("EF4B59", "icn-03", "Deadline наступил!", 7)
else if progress > 0: renderStatus("EF4B59", "icn-03", "Deadline наступил!", 7)
else: renderStatus("EF4B59", "icn-03", "Deadline наступил!", 7)
)
)
else if progress =1: renderStatus("59B161", "icn-01", "Работы завершены!", 7)
/*Если даты не заполнены*/
else : renderStatus("000000", "icn-03", "Незаполнены даты", 7)
)
Модель рисков в виде таблицы:
Теперь система нам подсветит те задачи, по которым есть риск быть не выполненными в срок, и даст время команде нивелировать его.
Ранее уже писала, что важная составляющая structure — это возможность положить список задач на ганта.
Для добавления Ганта нужно выбрать дополнительный слой структуры Gantt Chart и в параметрах Ганта определить даты, по которым будет строиться гант.
Гант также довольно гибкий:
на него можно добавить важные Milestone
добавить маркеры, по которым отслеживаются важные проектные даты или события
добавить даты релизов и тд
Итого: мы получили единый роадмап с важными для нас параметрами, с автоматическим добавлением задач и автоматическим определением рисков. Роадмап доступен каждому в компании, все прозрачно и доступно в едином месте по одной ссылке. Роадмап актуален в любое время, так как вся информацию тянется из тикетов jira, и при обновлении тикетов роадмап обновляется автоматически.
Тем самым полечили боли и достигли поставленных целей.
Вывод
Structure один из самых гибких инструментов, с помощью которых мне приходилось строить единые большие роадмапы, и самый эффективный.
Дальше в планах роадмап развивать:
добавить полный цикл проекта — задачи внедрения, подготовки договоров и прочее;
доработать модель рисков — добавить зависимость от этапов проекта, разработки, типов задач.