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

Шаг 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 один из самых гибких инструментов, с помощью которых мне приходилось строить единые большие роадмапы, и самый эффективный.
Дальше в планах роадмап развивать:
-
добавить полный цикл проекта — задачи внедрения, подготовки договоров и прочее;
-
доработать модель рисков — добавить зависимость от этапов проекта, разработки, типов задач.
ссылка на оригинал статьи https://habr.com/ru/companies/bimeister/articles/741416/
Добавить комментарий