Привет!

В предыдущем посте Как справиться с декомпозицией задач и не перестараться наш коллега рассмотрел вертикальные способы декомпозиции — по бизнес-ценности.

С тех пор, как мы пришли к гибким методологиям разработки, продуктовому подходу и осознанному построению потока создания ценности, мы перепробовали на практике все перечисленные в статье способы. Но даже при наличии такого большого арсенала сложности все еще остаются.

Нарезая задачки по бизнес-ценности, мы столкнулись с двумя типами проблем:

  • максимально мелко нарезанные задачи все еще большие,

  • разные бизнес-задачи завязаны на общие изменения.

Мы хотим поговорить о том, как поделить задачку, когда деления по бизнес-ценности недостаточно. В посте мы рассмотрим три способа горизонтальной декомпозиции:

  • по типу работ,

  • по слоям приложения,

  • выделяя базовые функции.

Декомпозиция по типу работ 

Рассмотрим продукт, занимающийся разработкой сайтов для одного потребителя. Бизнес-заказчик хочет добавить лендинг, приуроченный к акции. 

Условимся, что для реализации необходимы работы:

  • backend:

    • разработка  API для фронта (BFF);

  • frontend:

  • верстка (где-то это могут быть отдельные верстальщики, для упрощения примера считаем, что этим занимается frontend-разработчик);

  • наполнение интерфейса данными из соответствующих методов BFF;

  • тестирование.

С точки зрения бизнес-ценности — это одна задача, и ценность она несет, когда все работы по ней выполнены. В таком случае внутри задачи появляются шаги, последовательность которых может не всегда быть очевидна. Такие кейсы решаются сами собой в командах со стабильными и налаженными коммуникациями, чаще через координацию от TL или PL, в том числе между работами. Также их можно решить за счет громоздкой статусной модели, которая будет учитывать всю последовательность работ явно. 

Минусами такого подхода могут быть: 

  • все еще большой размер задачи,

  • последовательное выполнение работ,

  • часто в таких задачах frontend выступает как тестировщик для backend, что приводит к потерям производительности первого (то есть front получает в работу сырой вариант реализации от back, так как обычно тестирование проводится в конце цикла реализации задачи).

Альтернатива

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

Для примера мы разделим задачи front и back на независимые подзадачи — декомпозиция по типу работ. Вот как будет выглядеть горизонтальная декомпозиция в таком случае: 

  • подзадача на back-end на реализацию API,

  • подзадача на front-end на верстку,

  • подзадача на  front-end для реализации финального UI на основе подготовленного API и верстки.

Процесс в таком случае будет выглядеть так: 

Плюсы такого подхода:

  • между подзадачами явные связи, которые можно выстроить на timeline и удобно отслеживать;

  • тестирование “размазано” по процессу, чтобы равномерно распределять нагрузку на тестирование и атомизировать тестируемые задачи;

  • небольшие размеры задач.

Декомпозиция по слоям приложения

Представим, что в некоторой информационной системе, занимающейся процессом ведения корзин клиентов в интернет-магазине, появилось бизнес-требование по поддержке промокодов. Раньше такой функциональности не было в принципе.

Сохраняя бизнес-ценность, требование можно декомпозировать на операции — добавление и редактирование. Но если остановиться на такой нарезке, то задачи придется делать строго последовательно.

При горизонтальной декомпозиции можно определить части работ на разных слоях информационной системы:

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

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

  • в слое dao необходимо реализовать хранение промокодов в БД.

Для того чтобы работы на всех слоях можно было проводить независимо, необходимо заранее создать контракт (новые интерфейсы или методы в интерфейсах) взаимодействия между слоями, а также спроектировать модель для промокода в корзине, которая будет передаваться через эти новые интерфейсы.

Модель будет выглядеть примерно так:

public class Promocode {
 String code;
 boolean valid;
}

public class Basket {
 ...
 Promocode promocode;
}

 Интерфейс для сервиса бизнес-логики по применению промокода к корзине будет таким:

public interface PromocodeService {
 boolean applyPromocode(String basketId, String promocode);
 boolean deletePromocode(String basketId);
 Promocode getPromocode(String basketId);
}
 

Интерфейс для слоя dao:

public interface PromocodeDao {
 boolean savePromocode(String basketId, Promocode promocode);
 Promocode getPromocode(String basketId);
}

Если в корневой задаче реализовать данные модели и интерфейсы, то в трех независимых задачах можно параллельно доделать оставшиеся части в каждом слое:

  • в слое api реализовать новые методы api, которые вызывают методы из интерфейса PromocodeService;

  • В слое бизнес-логики создать реализацию для интерфейса PromocodeService, в которой будут вызываться методы из интерфейса PromocodeDao;

  • В слое dao реализовать логику методов savePromocode и getPromocode.

При необходимости можно завести финальную задачу, в которой написать интеграционные тесты, после того как параллельные задачи будут сделаны.

Выделение базовой функциональности

Случается, что при добавлении новой функциональности мы сталкиваемся с тем, что разным разработчикам для реализации своей части требований необходимо создавать функции с похожим поведением. Это приводит к дублированию работы и плохо сказывается на качестве кода. Для решения этой проблемы иногда используют cherry pick или отпочковываются от ветки другого feature branch. Обычно такая практика приводит к сложным мержам, очередям из задач и ошибкам в очередности вливания feature branch в основную ветку.

Для решения этой проблемы мы предлагаем на этапе декомпозиции требований выделить реализацию общей функциональности в отдельные задачи. Сами по себе данные задачи не будут нести никакой бизнес-ценности, но позволят распараллелить последующую работу, четко выстроить связи и избежать сложных мержей.

Рассмотрим пример, когда от бизнеса пришло требование передавать событие добавления товара в корзину в аналитическую систему. При использовании подходов вертикальной декомпозиции можно было бы разбить данное требование на задачи постранично:

  • отправка события с карточки товара, 

  • отправка события из корзины, 

  • отправка события с плитки товаров.

С одной стороны, такая декомпозиция выглядит хорошо, так как каждая задача несет в себе часть ценности, но с другой стороны, при реализации в каждой из этих задач необходимо будет реализовать метод отправки события. Для упрощения работ на этапе декомпозиции можно выделить отдельную задачу по созданию метода непосредственно отправки событий в смежную систему, а остальные задачи заблокировать. Это позволит избежать дублирования кода, ненужных cherry pick и сложных мержей.

Из минусов данного метода можно отметить только то, что задачи, которые несут бизнес-ценность, будут заблокированы до выполнения задачи, реализующей общую функциональность.

Организация потока работ

Когда задача декомпозирована по бизнес-ценности, вопросов по организации и визуализации на доске производственного потока обычно не возникает: одна задачка тащится слева направо, проходя от стадии аналитики до деплоя в прод целиком.

При горизонтальной декомпозиции возникает несколько вопросов:

  • кто декомпозирует задачу?

  • как организовать связанные карточки в джире?

  • кто отвечает за поставку бизнес-фичи целиком?

  • как деплоить бизнес-фичу в прод частями?

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

  • в какой момент и в какой задаче тестировать всю бизнес-фичу?

Попробуем ответить на эти вопросы.

Декомпозиция

Если говорить об устоявшихся практиках в компании, то можем отталкиваться от двух штатных вариантов:

  • Декомпозирует PL в рамках PBR, то есть выполняется вертикальная декомпозиция (по бизнес-ценности).

  • Декомпозирует аналитик с привлечением команды разработки и тестирования. Это уже более детальная декомпозиция с применением технических условий. 

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

Мероприятия

PBR (Product Backlog Refinement) — крупная декомпозиция с участием бизнес-заказчика. Помогает декомпозировать на ценностные задачи. В рамках данного мероприятия чаще происходит именно вертикальная декомпозиция, так как глубокого погружения в задачи не происходит.

Груминг — более детальная декомпозиция с включением ролей, задействованных в задаче. Чаще именно на этом этапе задачи можно декомпозировать горизонтально для удобства работы и при условии, что вертикальная декомпозиция больше невозможна или слишком усложняет процессы.

Карточки

Мы пробовали два варианта организации задач, относящихся к одной бизнес-ценности:

  • подзадачи внутри задачи,

  • задачи внутри эпика.

Подзадачи внутри задачи

Так как мы делим одну бизнес-ценность, такой способ декомпозиции кажется логичным. Разберем плюсы и минусы.

Плюсы:

  • бизнес-ценность приоритизируется в бэклоге продукта целиком,

  • понятен момент завершения — задача готова, когда выполнены все подзадачи.

Минусы:

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

  • непонятно, кто двигает родительскую задачку (тут можно отжаться скриптами, которые будут переводить в работу задачу, когда хоть одна ушла в работу и закрывать, когда все закрыты),

  • непонятно, на что ориентироваться при пополнении бэклога: на задачи или подзадачи.

Задачи внутри эпика

Бизнес-ценность формулируется на уровне эпика, а горизонтальная декомпозиция выполняется с помощью задач внутри него.

Плюсы:

  • корректно считается количество задач в работе,

  • по каждой задаче понятен исполнитель.

Минусы:

  • бизнес-ценность размазывается в бэклоге продукта. Из-за ошибочной расстановки приоритетов задачки могут разъехаться во времени,

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

  • эпики принципиально используются для более высокого уровня группировки, в бэклоге появляются эпики с разным смыслом — крупные юзер-стори и задачи.

Ответственность за поставку

Этот вопрос можно раскрыть так:

  • кто определяет приоритет задач в бэклоге?

  • кто проверит, что все части бизнес-фичи доехали до прода?

Мы решаем эти вопросы так же, как и с задачами, декомпозированными по бизнес-ценности, поэтому у нас за приоритизацию и поставку отвечает PL.

Деплой в продакшен

Большинство наших команд отказались от релизных циклов и перешли на ci/cd. Горизонтальная декомпозиция в таком подходе означает, что нам нужно деплоить не готовую задачу на все контуры, включая продакшн. 

Для этого мы используем несколько способов:

  • фиче флаги, то есть деплоим код в выключенном состоянии,

  • версионирование API,

  • интерфейсы и замену реализации.

Поясним последний способ. При таком подходе создаются интерфейсы для тех классов, логика которых будет впоследствии изменена. Реализация разрабатывается параллельно. При этом она может деплоиться в прод, так как не используется и не затрагивает работающий код. Затем старая реализация подменяется новой.

Управление зависимостями

Нам важно не запутаться в зависимостях. Для этого каждой команде прежде всего нужно соблюдать гигиену в JIRA:

  • правильно указывать связи, то есть использовать соответствующие типы связей: blocks, contains и тд;

  • выставлять флаги (flagged), если задача блокируется.

Для наглядной визуализации можно использовать инструменты джира:

  • Настройка карточек с запросами на досках продукта:

    • вывод на карточку зависимостей (через настройки доски),

    • настройка цветовых индикаторов по событию,

    • jIra Automation для настройки синхронизации между задачами.

  • Structure.

  • Plans

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

Тестирование

Проблема заключается в том, что нельзя написать приемочные тесты на кусочки бизнес-фичи. Мы заводим отдельную задачу на тестирование. При этом, если у вас CD, надо учитывать, что отдельные задачи уедут в прод без тестов.

Также часто при горизонтальной декомпозиции необходимо заводить задачу, в рамках которой мы собираем требуемую бизнес-функциональность из ранее сделанных частей. В таких случаях приемочное тестирование всей бизнес-фичи можно провести в рамках этой задачи.

Вывод

В этой статье мы хотели показать, что горизонтальная декомпозиция — это не страшно. В определенных кейсах она дает вам выигрыш, позволяя сделать задачи мельче, распараллелить работу или заранее реализовать общие функции. Главное — помнить, что она несет дополнительные накладные расходы на управление задачами, поэтому использовать ее стоит только там, где без нее не обойтись.

Авторы поста: Вячеслав @fedyuninvmФедюнин, Вадим Грибиников, Ольга @oraykovaРайкова.

Комментарии (0)