1. Длинная задача ломается не в коде, а в процессе
Длинная задача ломается не оттого, что модель «плохо кодит», а когда процесс теряет форму: контекст разрастается, гипотезы перемешиваются, вы обсуждаете три проблемы сразу, diff не читается.
Возьмём AI Commerce Growth OS. Надо вынести refund-логику из RefundController в RefundService: не сломать API, не трогать платёжного провайдера, сохранить поведение. Дайте Claude запрос «отрефактори refund-flow и сделай код нормальным» — и полезность без границ расползётся: через полчаса обсуждаете DTO, exception handler, логи и pagination в соседнем модуле. Не задача, а клубок задач.
| Если работать «одним заходом» | Если работать как инженерный процесс |
|---|---|
| Один большой запрос | |
| Безымянная сессия | Именованная сессия под конкретную задачу |
| Правки «раз уж мы тут» | Жёсткий scope и запрет на unrelated work |
| Один коммит в конце | Маленькие коммиты на границах этапов |
| Остановка по усталости | Stop points по плану |
| Память только в диалоге | Summary и decision log |
Длинная AI-assisted задача — это не большой prompt, а маленький инженерный процесс. Не спроектируете его — задача поплывёт раньше, чем сломается код.
2. Каркас длинной задачи: spec и milestones
Чтобы задача не расползалась, ей нужен каркас на три вопроса: что делаем, чего не делаем, где останавливаемся проверять состояние. Превращайте task spec сразу в скелет процесса — цель, ограничения, деление на этапы:
# TASK_SPEC: refund-flow refactor
## Цель
Вынести refund decision logic из RefundController в RefundService.
## Область изменений
Только refund-flow.
## Не-цели
Не менять payment provider integration.
Не менять публичный API.
Не менять схему БД.
## Ограничения
Сохранить текущее поведение и зелёные тесты.
## Майлстоуны
M1 — зафиксировать текущее поведение тестами
M2 — вынести логику в RefundService
M3 — сделать контроллер тонким и перепроверить интеграционные тесты
## Точки остановки
После каждого milestone: summary + checks + commit
Задача получила форму: Claude не блуждает «улучшу ещё пару мест», а идёт по коридору.
flowchart TD
A[TASK_SPEC] --> B[Named session]
B --> C[Milestone 1]
C --> D[Summary + checks + small commit]
D --> E[Milestone 2]
E --> F[Summary + checks + small commit]
F --> G[Milestone 3]
G --> H[Stop point / finish]
Milestones делают задачу переносимой и проверяемой. Без них непонятно, где она, и любая пауза становится болью.
3. Именованная сессия: у задачи должно быть имя
Задача живёт дольше десяти минут — безымянная сессия работает против вас. Скоро у вас «та длинная про refunds», «ещё одна похожая, но там тесты» и «какая-то, где Claude предлагал util-класс». Память подводит уже не модель, а вас.
Дайте задаче имя, совпадающее с workstream, branch и смыслом task spec:
| Что именуем | Пример |
|---|---|
| Задача | |
| Branch | |
| Session | |
Тогда у вас не три сущности, а один маршрут: задача, ветка, сессия, изменения. До первого перерыва — мелочь, после первого обеда — спасение.
Переоткрытие:
# точное имя команды и флага зависит от версии Claude Code
claude --resume refund-refactor
Важна не команда, а привычка: сессия привязана к задаче, а не висит как «ещё один чат». Название вроде new chat, session-12 или test2-final-final — сигнал, что процесс уходит в хаос.
4. Milestone summary и decision log
Истории диалога мало: память выносят наружу короткими выводами. Milestone summary — «где мы сейчас», decision log — «какие решения приняты, чтобы через полчаса не спорить заново».
Минимальный summary после этапа:
## Summary майлстоуна: M1
Готово:
- добавлены тесты на текущее refund-поведение
- зафиксированы кейсы partial refund и approval > $100
Открытые вопросы:
- кейс refund > $1000 пока не покрыт
Риски:
- integration path с payment provider ещё не перепроверен
Дальше:
- вынести decision logic в RefundService
Decision log:
## Лог решений
- threshold $100 оставляем в configuration property
- payment provider integration не трогаем в этом refactor
- не переносим exception mapping в эту же задачу
Жить в одном «правильном» файле эти заметки не обязаны: рядом с задачей, в заметке к ветке, в markdown. Из них собирается handoff для fresh session или reviewer.
Без журнала сессия живёт в режиме «кажется, мы это уже обсуждали»: Claude снова предлагает отвергнутый вариант, а вы вспоминаете об этом через пару сообщений. Хороший summary — снимок состояния, не отчёт для начальства.
5. Stop points: останавливаться по плану
Задачи портятся от неудачной остановки. Самый частый stop point: «Ладно, устал, завтра продолжу». Усталость — плохой критерий: задача брошена в случайной точке.
Хороший stop point — конец осмысленного куска: добавили characterization tests; вынесли логику в сервис и прогнали проверки; сделали контроллер тонким и посмотрели diff. Это место, где задачу можно безопасно передать даже себе будущему: ясно, что сделано, что нет, какие проверки запускались, какой шаг следующий.
Маленький commit на границе этапа снижает цену продолжения:
git commit -m "test(refund): characterize approval rules"
git commit -m "refactor(refund): extract RefundService"
Summary объясняет смысл этапа, commit фиксирует состояние файлов. Только commit — не видно решений за ним; только текст — нечего продолжать.
И ещё: stop point не даёт тащить постороннее. «Сейчас бы заодно поправить формат логов» — спросите: это часть milestone или другая история? В девяти случаях из десяти — другая.
6. Context rot: когда сессия помнит лишнее
Загрязнение контекста вы уже видели на коротких задачах. На длинной оно коварнее: контекст не просто переполняется — он стареет. Это context rot: сессия тащит старые гипотезы и отвергнутые идеи как живые.
Вы решили не выносить refund-логику в RefundUtils — размоет границы ответственности. Через сорок минут контекст разросся, и Claude снова: «Возможно, стоит вынести общую логику в utility class». Не назло — старая гипотеза сидит рядом с новыми данными.
Мини-сценка:
Вы: utility class не подходит, оставляем сервисный слой
Claude: понял, идём через RefundService
...
через 30 минут
Claude: чтобы уменьшить дублирование, можно вынести логику в RefundUtils
Проблема не в ответе — сессия начала гнить. Другие признаки: ходит кругами по отвергнутым вариантам, смешивает подходы, ссылается на устаревшие assumptions, лезет в файлы вне scope.
Не дожимайте. Остановитесь, соберите summary, зафиксируйте решения, продолжайте из чистого состояния. Распознать context rot — не слабость, а дисциплина.
7. Два режима задачи: пошаговый и goal-driven
Каркас есть — как вести задачу? Два режима: пошаговый и goal-driven (работа до формально проверяемого условия завершения). Вопрос — какой безопаснее.
Сравним:
| Пошаговый режим | Goal-driven режим |
|---|---|
| После каждого шага вы подтверждаете, что делать дальше | Claude идёт несколько ходов подряд до достижения условия завершения |
| Лучше для рискованных изменений | Лучше для хорошо ограниченных задач |
| Подходит, когда нужно много человеческих решений | Подходит, когда есть детерминированные проверки |
| Медленнее, но прозрачнее | Быстрее, но требует хорошего verification harness |
Пошаговый — классика для чувствительных задач: шаг, diff, проверки, решение, дальше. Особенно где затронуты деньги, внешние контракты или форма решения ещё не ясна.
Goal-driven хорош только когда конец проверяет машина — есть machine-checkable termination condition без ваших субъективных оценок.
Плохие условия:
| Плохая формулировка | Почему она плохая |
|---|---|
| «Сделай аккуратно» | Машина не знает, что такое «аккуратно» |
| «Когда станет лучше» | Это субъективно |
| «Когда код будет чистым» | Тоже субъективно |
Хорошие условия:
| Хорошая формулировка | Почему она хорошая |
|---|---|
завершается без ошибок |
Проверяется машинно |
зелёный |
Проверяется машинно |
| Нужные тесты проходят | Проверяется машинно |
Пример в task spec может выглядеть так:
## Условие завершения
Задача считается завершённой, когда:
- RefundControllerTest проходит
- RefundServiceTest проходит
- ./gradlew test завершается с code 0
- публичный API refund endpoint не изменён
Здесь есть тонкий, но важный нюанс. Goal-driven режим безопасен только при детерминированной верификации. Если у вас нет надёжных тестов, если проверка субъективна, если задача про «на глаз стало удобнее», запускать такой режим опасно. Это уже не инженерный автопилот, а игра в «надеюсь, мы где-нибудь вовремя остановимся». А надежда, как известно, не входит в официальный стек quality assurance.
8. Сквозной пример: refund-flow в Commerce OS
Чтобы всё это не висело в воздухе, давайте соберём один цельный сценарий. Допустим, в Commerce OS у нас есть задача: refactor refund-flow так, чтобы decision logic ушла из контроллера в сервисный слой, а поведение осталось прежним. Задача не на пять минут, но и не на две недели. Классический кандидат на аккуратную длинную сессию.
Скелет может быть таким:
# TASK_SPEC: refund-flow refactor
Цель:
Вынести refund decision logic из RefundController в RefundService.
Область:
Только refund-flow.
Ограничения:
Не менять API, не трогать payment provider integration, сохранить tests green.
Майлстоуны:
M1 — characterization tests
M2 — RefundService extraction
M3 — controller cleanup + verification
Точки остановки:
После каждого milestone: summary + checks + commit
Сессию мы называем refund-refactor. Branch — feature/refund-refactor. После первого этапа фиксируем короткий summary:
## Summary майлстоуна: M1
Готово:
- добавлены тесты на approval threshold
- partial refund поведение зафиксировано
Решения:
- threshold не переносим в БД
- payment integration вне scope
Дальше:
- выделить RefundService без изменения API
Дальше, на втором этапе, вам уже не нужно заново вспоминать, почему threshold не ушёл в БД и зачем мы не трогаем интеграцию. Решение записано. Контекст не обязан таскать это только в своей истории.
Если на втором этапе Claude начинает снова предлагать utility class или лезть в PaymentGatewayClient, вы не спорите бесконечно. Вы видите: задача начинает уходить из рамки. Значит, пора остановиться, посмотреть summary и вернуть процесс в границы milestone. А если задача достаточно формализована и тесты надёжны, можно задать машинно проверяемое условие завершения именно для этого этапа: например, все refund-тесты зелёные и билд не падает.
В таком режиме длинная задача перестаёт быть бесконечным чатом, который страшно закрыть. Она превращается в последовательность небольших, понятных состояний. У каждого состояния есть имя, границы, short summary, зафиксированные решения и ясный следующий шаг. А это уже совсем другой уровень контроля. И, что особенно приятно, здесь Claude действительно начинает усиливать разработчика, а не тащить его за собой в хаос из «ещё одной хорошей идеи».
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ