Привет! Меня зовут Настя Николаева, лид цифровой трансформации в компании Bimeister. И я хочу рассказать, как мы собирали единый роадмап компании с помощью плагина Structure Jira.

Наверное, в каждой компании, где есть несколько команд разработки, существуют подобные проблемы:

  1. у каждой команды свои правила и инструменты для построения роадмап

  2. некоторые роадмапы не найти, они не находятся в свободном доступе

  3. продуктовый роадмап и проектные живут в разных местах и не синхронизированы

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

Решив эти проблемы, мы сможем достигнуть поставленных целей:

  1. Прозрачные и понятные сроки исполнения проектных и продуктовых обязательств

  2. Выявление рисков на ранних этапах исполнения работ

Итак, верхнеуровнево мы наметили такие шаги:

Шаг 1 - выбираем единый инструмент построения роадмап

Так как у нас вся продуктовая разработка ведется в Jira, то и собирать единый роадмап правильно было бы в jira, избежав копи-пасти и дублирования информации.

В процессе ресеча различных плагинов и опыта других компаний мы выбрали Structure в Jira.

Structure — плагин для Jira, с помощью которого можно гибко визуализировать и структурировать задачи в jira в виде иерархии.

Structure позволяет на основе задач jira:

  • выстроить список задач jira в любой иерархии, по любому нужному запросу либо же вручную по одной задаче

  • сгруппировать списки по любому атрибуту тикета, отфильтровать ненужные, отсортировать так, как требуется

  • отобразить и показать требуемые поля тикетов

  • с помощью формул и языка запросов можно добавить нужные аналитические параметры и отобразить любые данные по тикетам

  • добавить gantt chart и ресурсы

  • и многое-многое другое

Шаг 2 - собираем роадмап

Первая проблема, с которой столкнулись - просто так, “сходу”, роадмап нам было не собрать, так как все команды ведут тикеты в jira по своим правилам; признаков или каких-то других атрибутов, по которым мы можем одним запросом вытащить все задачи, не было. Следовательно, его нужно было добавить. Для этого сделали несколько доработок по таскам:

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

  • добавили обязательные поля, по которым собираем structure

    • Teamname

    • Проект

    • Продукт

    • Даты старта и окончания проектных работ

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

Дальше создаем структуру, задаем название. 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}!https://d1.almworks.com/.files/weath_$iconId.png|width=20,height=20!!https://d1.almworks.com/.files/Spacer.gif|width=$padding!{color:white}*$status*{color}!https://d1.almworks.com/.files/Spacer.gif|width=$padding!{panel}""" :
 
/*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 один из самых гибких инструментов, с помощью которых мне приходилось строить единые большие роадмапы, и самый эффективный.

Дальше в планах роадмап развивать:

  1. добавить полный цикл проекта - задачи внедрения, подготовки договоров и прочее;

  2. доработать модель рисков - добавить зависимость от этапов проекта, разработки, типов задач.