Привет!
В предыдущем посте Как справиться с декомпозицией задач и не перестараться наш коллега рассмотрел вертикальные способы декомпозиции — по бизнес-ценности.
С тех пор, как мы пришли к гибким методологиям разработки, продуктовому подходу и осознанному построению потока создания ценности, мы перепробовали на практике все перечисленные в статье способы. Но даже при наличии такого большого арсенала сложности все еще остаются.
Нарезая задачки по бизнес-ценности, мы столкнулись с двумя типами проблем:
максимально мелко нарезанные задачи все еще большие,
разные бизнес-задачи завязаны на общие изменения.
Мы хотим поговорить о том, как поделить задачку, когда деления по бизнес-ценности недостаточно. В посте мы рассмотрим три способа горизонтальной декомпозиции:
по типу работ,
по слоям приложения,
выделяя базовые функции.
Декомпозиция по типу работ
Рассмотрим продукт, занимающийся разработкой сайтов для одного потребителя. Бизнес-заказчик хочет добавить лендинг, приуроченный к акции.
Условимся, что для реализации необходимы работы:
-
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Райкова.