1. Декомпозиция плана как отдельный шаг
Когда Implementation Plan уже утверждён, очень легко решить, что дальше всё очевидно: берём и делаем. Но между «я понял задачу» и «я безопасно её реализую» есть ещё один мост. Не построите его — Claude поможет широким жестом, и вы посмотрите на результат с лицом человека, который просил поправить полку, а получил капитальный ремонт кухни.
План отвечает на вопрос что вы меняете и почему. Декомпозиция отвечает на другой вопрос: какими минимальными порциями это двигать, чтобы каждый шаг был читаемым, проверяемым и откатываемым. Это не бюрократия ради бюрократии — это способ разрезать риск на маленькие куски, чтобы он перестал кусаться.
Полезно держать в голове простое различие:
| Уровень | На какой вопрос отвечает |
|---|---|
| Implementation Plan | Что именно мы делаем и в каком общем порядке |
| PR slice | Какую минимальную порцию изменения можно независимо проверить и отревьюить |
Здесь PR slice — это логическая единица планирования и контроля. Он может позже лечь в отдельный commit, в часть одного PR или в самостоятельный PR — сейчас нам важна не упаковка, а то, что кусок работы можно отдельно понять, проверить и при необходимости остановить.
Если в плане у вас написано «обновить сервис и контроллер», это может быть нормальной формулировкой на уровне задачи. Но для реальной работы такой шаг слишком широкий. Он уже просится в разбиение. Иначе вы получите классическое: «я тут немного дописал в сервисе, чуть-чуть подправил HTTP-слой, заодно выровнял обработку ошибок, ну и пару тестов затронул». Слово «чуть-чуть» в таких историях обычно означает «пристегните ремни».
Именно поэтому декомпозиция идёт после утверждения плана, а не вместо него. Сначала вы фиксируете направление. Потом режете путь на понятные участки. Это как маршрут на машине: знать, что вам надо из Киева во Львов, полезно, но ещё полезнее понимать, где повороты, заправки и места, где не стоит внезапно экспериментировать с подвеской.
2. Признаки хорошего PR slice
Хороший PR slice — это не просто «маленький шаг». Маленьким можно сделать и хаос. Хороший slice — это такая единица изменения, у которой есть одно ясное намерение, понятная зона файлов, своя проверка и отдельный смысл для ревью. Если другой инженер открывает такой slice и за минуту понимает, зачем он нужен, — вы на верном пути.
Сравнение обычно выглядит так:
| Признак | Слабый шаг | Хороший PR slice |
|---|---|---|
| Намерение | «Починить checkout» | «Добавить защиту от empty cart в доменной логике» |
| Область файлов | Сервис, контроллер, тесты, документация и ещё пара случайных файлов | Узкий набор файлов вокруг одного изменения |
| Проверка | «Потом прогоню всё» | Один понятный targeted check |
| Откат | При откате цепляет соседние изменения | Откатывается отдельно |
| Ревью | Нужно заново понять половину модуля | Видна одна идея и её границы |
Обратите внимание на важную вещь: slice не обязан быть «кусочком кода». Это может быть отдельный шаг проверки, шаг контракта или шаг документации. Очень часто первый хороший slice — это не fix, а фиксация поведения: небольшой regression check, воспроизводимый запрос, короткий smoke script. Его задача не «исправить», а заземлить проблему так, чтобы потом никто не спорил, был баг или вам просто «что-то не так показалось».
Из этого же вытекает важное правило: один slice — одно намерение. Если в одном шаге у вас одновременно живут feature, refactor и обновление зависимости, это уже не slice, а чемодан, который вы пытались закрыть ногой. Да, технически можно. Нет, ревьюеру от этого лучше не станет.
Ещё один хороший тест на зрелость slice: можете ли вы описать его одной фразой без союза «и ещё»? Если без «и ещё» не получается, скорее всего, перед вами уже две порции работы, а не одна.
3. Привязка slice к критериям приёмки и проверке
На этом этапе очень полезно перестать думать о slices как просто о «списке шагов». На самом деле это мост между Implementation Plan и критериями приёмки. Каждый slice должен либо напрямую двигать один acceptance criterion, либо снимать один конкретный риск. Если связь не видна, шаг пока сырой.
Возьмём наш Commerce OS и issue с пустой корзиной в checkout. Допустим, у вас уже зафиксированы примерно такие критерии: запрос с пустым items[] больше не даёт 500, API возвращает 400 с понятной validation error, а корректный заказ продолжает работать как раньше. Теперь задача декомпозиции — не просто «разбить на четыре пункта», а показать, какой шаг за какой критерий отвечает.
Это удобно видеть в таблице:
| Acceptance criterion | Какой slice его двигает | Чем проверяется |
|---|---|---|
| Пустая корзина больше не падает в 500 | Slice с фиксацией бага + slice с доменной проверкой | targeted check / regression check |
| API отвечает 400 и понятной validation error | Slice с HTTP mapping | ручной запрос или контрактная проверка |
| Валидный checkout не сломан | Проверка на каждом узком шаге | существующие релевантные проверки |
Здесь важно не скатиться в преждевременный курс по тестированию. Мы сейчас не проектируем полную test strategy. Мы всего лишь требуем, чтобы у каждого slice был свой способ доказать, что он не ушёл в туман. Если slice нельзя проверить хотя бы одним осмысленным действием, он пока не готов к работе.
Отдельно стоит помнить про stop conditions. Они нужны не только у всего плана, но и на уровне slice. Например, вы делаете slice с HTTP-mapping и вдруг понимаете, что ради него надо менять общую схему error response для нескольких модулей. Это уже не «маленький последний штрих». Это сигнал остановиться и вернуться к плану. И это хорошая новость, а не плохая: декомпозиция как раз и нужна, чтобы такие сюрпризы ловить рано, а не после трёх часов бодрого редактирования.
4. Разбираем issue #482 по slices
Теперь давайте приземлим всё на реальный пример, чтобы это не оставалось красивой теорией. У нас есть issue #482 в Commerce OS: POST /api/orders возвращает 500, если в checkout приходит пустой список товаров. У нас уже есть intake, investigation и общий plan. Теперь превращаем этот plan в последовательность slices, которую другой инженер сможет открыть и спокойно понять.
Общая дорожка здесь может выглядеть так:
issue #482
→ зафиксировать текущее поведение
→ добавить доменную защиту рядом с calculateTotal()
→ отразить поведение на HTTP-уровне
→ при необходимости обновить контракт / документацию
А в Implementation Plan это удобно записать так:
## PR slices
1. Regression check for empty cart
Files: OrderControllerWebTest.java
Verification: targeted check воспроизводит текущий failure path
2. Domain validation near calculateTotal
Files: OrderService.java
Verification: empty items отсекаются до path с items.get(0)
3. HTTP validation response
Files: GlobalExceptionHandler.java, OrderControllerWebTest.java
Verification: POST /api/orders with empty items[] returns 400 + agreed validation error body
4. Contract/docs update if this response is documented separately
Files: api/orders.md or equivalent contract note
Verification: documented example matches actual behavior
Обратите внимание, почему порядок именно такой. Первый slice ничего не «чинит». Он фиксирует проблему. Это важно, потому что без такого шага следующий slice легко превращается в магию: баг вроде бы был, потом вроде бы пропал, но как именно — уже не очень понятно. Первый slice даёт точку опоры. И именно поэтому slice не равен автоматически отдельному PR: сначала это единица мышления и контроля, а не обязательная форма упаковки.
Второй slice живёт на уровне доменной логики. Он не лезет сразу в HTTP-контракт, не переписывает половину контроллера и не начинает «заодно улучшать архитектуру». Его единственная задача — сделать так, чтобы пустая корзина корректно распознавалась как невалидный сценарий именно там, где живёт бизнес-правило.
Третий slice уже занимается тем, что увидит клиент API. И это отдельный смысл. Очень часто разработчики любят смешать второй и третий шаги, потому что «ну это же один баг». На уровне задачи — да, один. На уровне ревью — нет. Один slice меняет доменное поведение, другой — HTTP-представление этого поведения. Их полезно читать отдельно. Если существующий GlobalExceptionHandler уже умеет маппить нужную валидацию, этот slice может почти схлопнуться до короткой проверки — и это тоже нормально.
Четвёртый slice появляется не всегда, но в нашем случае он оправдан только если в проекте действительно ведётся отдельная документация API-контракта. Это маленький, но честный отдельный шаг. И он гораздо приятнее читается отдельно, чем в хвосте большого куска логики.
Теперь посмотрим на контрпример. Плохая декомпозиция для той же задачи выглядела бы так:
1. Fix empty cart bug
2. Refactor calculateTotal into PricingService
3. Cleanup error handling in orders module
4. Update Spring Boot patch version
Формально это тоже «четыре шага». Практически — здесь уже видно сразу несколько красных флагов. Во втором пункте внезапно появился рефакторинг, в третьем — широкая уборка по модулю, в четвёртом — изменение зависимости, которое к конкретному багу отношения почти не имеет. Такой список не уменьшает риск, а маскирует его красивой нумерацией.
И да, не в каждой команде первый slice обязательно будет именно failing test. Иногда это может быть короткий воспроизводимый запрос, служебный сценарий проверки или минимальный smoke script. Смысл первого slice не в том, чтобы «по заветам тестирования страдать от красного». Смысл в том, чтобы перестать спорить с реальностью и получить воспроизводимый сигнал: вот баг, вот как он проявляется.
5. Хранение декомпозиции и читаемость
Когда люди только начинают работать с артефактами, у них появляется соблазн на каждый чих заводить новый Markdown-файл. Это очень человечно. Кажется, чем больше файлов, тем серьёзнее инженерия. На практике иногда получается музей: plan-final.md, plan-final-2.md, really-final-plan.md. Красиво, но жить в этом тяжеловато.
Для текущего уровня чаще всего не нужно изобретать новый артефакт. Декомпозиция отлично живёт как раздел PR slices внутри уже существующего Implementation Plan. Так контекст остаётся в одном месте: вы видите цель, scope, риски, проверки и тут же — последовательность маленьких шагов.
Хороший рабочий формат выглядит так:
| Slice | Намерение | Файлы | Проверка | Сигнал стоп |
|---|---|---|---|---|
| 1 | Зафиксировать баг | OrderControllerWebTest.java | targeted check воспроизводит сбой | check не воспроизводится |
| 2 | Ввести доменное правило | OrderService.java | empty items отсекаются до calculateTotal() | затрагиваются shared callers calculateTotal() |
| 3 | Отразить ошибку в HTTP | GlobalExceptionHandler.java, OrderControllerWebTest.java | запрос даёт 400 и согласованную validation error | нужен общий redesign error schema |
| 4 | Обновить контракт, если он документирован отдельно | api/orders.md или equivalent note | пример совпадает с response | выясняется, что контракт шире |
Такую таблицу легко читать и человеку, и Claude. Если строка в ней перестаёт помещаться в одну мысль, это хороший сигнал, что slice широкий. Не надо стесняться резать ещё раз. Декомпозиция только выигрывает от дополнительной точности.
Здесь же полезно подключать ваш Workflow Kit, но без лишнего фанатизма. Вы уже сделали issue-analysis skill и reviewer-агента. Этого достаточно, чтобы не изобретать велосипед. Например, issue-analysis может сгенерировать первичный раздел PR slices, а reviewer в read-only режиме — проверить, нет ли слишком широких шагов, смешанных намерений или слабых проверок. Это хороший пример того, как мета-проект курса помогает реальному Commerce OS, а не существует сам по себе где-то в вакууме.
6. Просим Claude резать задачу аккуратно
Claude очень старается быть полезным. Иногда даже слишком. Если вы напишете «разбей задачу на шаги», он легко вернёт что-то вроде «обновить сервис, контроллер, тесты и документацию». Формально шаги есть. Практической пользы — примерно как от совета «работайте лучше». Поэтому у запроса на декомпозицию должен быть свой формат результата.
Хороший запрос звучит примерно так:
Раздели approved implementation plan на PR slices.
Для каждого slice укажи:
- одно намерение,
- конкретные файлы,
- одну проверку,
- что в slice не входит,
- сигнал остановки.
Не объединяй feature, refactor и dependency changes.
Если slice нельзя откатить отдельно, раздели его ещё.
Смысл здесь в том, что вы задаёте Claude не просто тему, а формат мышления. Вы не просите «сделай красиво». Вы требуете независимые единицы работы. Это сильно меняет результат.
После первого ответа полезно делать второй проход. Не с позиции «ну вроде нормально», а с позиции инженера, который уже однажды видел монструозный diff и не хочет повторения. Хорошие уточняющие вопросы здесь такие: «какой из этих slices самый широкий?», «какой шаг трогает больше всего подсистем?», «какой slice нельзя откатить изолированно?», «где ты смешал bug fix и cleanup?». Claude на такие вопросы обычно отвечает весьма честно, если его попросить прямо.
Если хотите использовать reviewer-агента из Workflow Kit, дайте ему очень конкретную задачу: не оценивать код, которого ещё нет, а оценить качество декомпозиции. Например, проверить, у каждого ли slice одна идея, есть ли явная проверка, нет ли спрятанного scope creep. Это особенно полезно, когда вы сами уже слегка замылили глаз и вам начинает казаться, что шаг «поправить обработку ошибок в orders» звучит достаточно конкретно. Не звучит. Но после третьей чашки кофе мозг иногда становится очень дипломатичным.
7. Готовый reviewable implementation path
Самая частая проблема в декомпозиции — не только слишком крупные шаги, но и неумение вовремя остановиться. Можно резать бесконечно и в итоге получить список из двенадцати микродвижений, каждое из которых уже неудобно само по себе. Поэтому важно понимать не только как делить, но и когда уже достаточно хорошо.
Проверить себя можно через несколько простых вопросов:
| Вопрос | Здоровый ответ |
|---|---|
| Можно ли описать slice одной фразой? | Да, без «и ещё» |
| Есть ли у slice один понятный check? | Да, пусть даже маленький |
| Можно ли его откатить отдельно? | Да, без каскадного ремонта |
| Поймёт ли другой инженер, зачем этот шаг существует? | Да, без устного пересказа на 15 минут |
| Видна ли связь с критерием приёмки или риском? | Да, явно |
Если на эти вопросы вы в основном отвечаете «да», у вас уже не просто список действий. У вас есть reviewable implementation path — последовательность, по которой можно двигаться спокойно и без инженерного экстрима. И это очень заметно по ощущениям. Вместо туманного «надо починить checkout» у вас лежит понятная цепочка: сначала зафиксировать баг, потом закрыть доменное правило, потом вывести это наружу на уровне API, а затем, если нужно, обновить контракт.
Задача перестаёт быть страшной не потому, что стала меньше, а потому, что вы наконец видите её по частям. Именно из такой цепочки потом рождается управляемая реализация: небольшие правки, отладка, diff review и только потом упаковка в коммиты и PR.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ