Есть небольшое приложение со списком покупок, и к нему нужны две вещи: фильтр записей и выгрузка списка в CSV. Задачи между собой почти не связаны, и мысль напрашивается сама собой: поделить работу на двоих и получить обе части через полчаса вместо часа. Один исполнитель берёт фильтр, второй экспорт, каждому выделили свою рабочую папку, чтобы правки не смешивались. Как обе части понимают запись списка, заранее не обсуждали. Казалось очевидным.
Через полчаса обе части готовы. Свои проверки у каждой зелёные, объединение прошло без единого конфликта. А общий сценарий не работает: на экране фильтр показывает три строки, в выгруженном файле столбец пустой. Причина скучная. Один из исполнителей заодно переименовал поле completed в done, потому что так читалось лучше, и второй об этом не знал.
Отдельные папки развели файлы, но не согласовали решения. Разберём задачу в три шага: сначала решим, стоит ли вообще делить эту работу, потом распределим области изменений и общие договорённости, а в конце проверим собранный результат, чтобы такой сюрприз не доезжал до самого конца.
Проверьте зависимости
Причин делить работу обычно две. Первая - независимое исследование разных областей: одновременно выяснить, как устроен показ списка и чем формируется выгрузка. Вторая - одновременная работа над частями, у которых уже известны границы и проверяемый результат. Раскладка "здесь план, здесь исполнение, здесь проверка" сама по себе трёх участников не требует, одна сессия делает это по очереди. Для небольшой задачи разумно начать с одного исполнителя.
Если причина есть, следующий вопрос такой: какая часть работы B может идти, пока результат A неизвестен? Если B целиком зависит от A, эти шаги идут последовательно. Иногда B может заранее подготовить независимую часть, а соединение результатов всё равно должно дождаться A.
Три ситуации из того же примера со списком покупок:
| Что нужно сделать | Можно начинать одновременно | Почему |
|---|---|---|
| Найти, где список фильтруется сейчас, и найти, чем формируется выгрузка | да | читаются разные участки, выводы друг от друга не зависят |
| Подготовить окончательное исправление по ещё неизвестной причине | после проверки причины | сначала исследование; разные проверяемые гипотезы можно исследовать параллельно |
| Написать фильтр и написать экспорт | после договорённости об общем формате записи | обе части опираются на одно и то же поле; без общего решения результаты разойдутся |
Третья строка - самая частая ловушка. Кажется, что раз фильтр живёт в src/filter.js, а экспорт в src/export-csv.js, то части независимы: файлы-то разные. Но обе читают одну запись и одно поле completed. Сценарий из начала статьи сломался именно так, и разные файлы его не спасли. Независимость - про поведение и общие договорённости, а не про то, в скольких файлах лежит код.
Само разбиение можно поручить агенту, только без запуска исполнителей:
Раздели задачу на части. Для каждой укажи входные данные, результат,
зависимости и способ проверки. Отметь, какие части могут начаться сейчас.
Исполнителей пока не запускай.Такой ответ можно проверить до запуска. Части без незакрытых зависимостей - кандидаты на одновременный старт. Пустую колонку зависимостей стоит сверить с проектом: агент мог не заметить общую модель или настройку.
Посчитайте время до общего результата
Ускорение обычно меряют одним числом: сколько времени заняла сама работа. А считать надо время до принятого общего результата. Туда, кроме собственно работы, входят постановка для каждого исполнителя, повторное чтение проекта каждым из них, ожидание, проверка двух результатов и сборка их в один.
Условный расчёт по нашему примеру. Числа взяты для арифметики, это не замер производительности:
| Что считаем | Один исполнитель по очереди | Два исполнителя одновременно |
|---|---|---|
| Дополнительная подготовка разделения: две постановки и согласование между исполнителями | 0 минут | 5 минут |
| Исполнение | 20 + 20 минут | 20 минут на обе части |
| Проверка результатов и сборка | 10 минут | 15 минут |
| Итого до принятого результата | 50 минут | 40 минут |
| Итого, если одну часть переделали за 15 минут из-за разного понимания записи | 50 минут | 55 минут |
Общая подготовка задачи нужна в обоих вариантах, поэтому в расчёте её нет. Выигрыш в десять минут съедает одна переделка, и чем дороже сборка, тем больше времени должна экономить одновременная работа.
Кроме времени есть ещё две статьи расхода, про которые легко забыть. Каждый дополнительный исполнитель заново читает проект, и это расход ресурсов, а подробные итоги, вернувшись, занимают место в общей беседе. Вторая статья важнее: ваше собственное внимание. Два результата нужно прочитать, сверить между собой и принять по ним решение. Если вы не успеваете читать то, что приходит, скорость уже не ваша.
Ускорение почти вдвое возможно для двух сопоставимых независимых частей с небольшими накладными расходами. Но само число исполнителей этого не обещает.
Дубль или проверка
Дублирование не видно в отчётах. Два исполнителя молча открывают одни и те же файлы, оба разбираются, как устроена запись списка, и каждый честно пишет, что изучил код. Но иногда второй проход нужен специально: допущение критично, и вы хотите, чтобы его проверил кто-то, кто не видел рассуждений первого. От случайного дубля такая проверка отличается тем, что у неё заранее названы предмет, причина и то, что вы сделаете, если ответы разойдутся. Без последнего проверка превращается в сбор мнений.
Теперь обратная ситуация. Двух исполнителей спросили, что означает completed в записи списка. Оба ответили одинаково - отметка, что пункт куплен. Выглядит как подтверждение с двух сторон.
На самом деле оба прочитали одну строчку в README.md, написанную год назад, а в коде поле давно означает другое. Совпал у них не вывод, а источник.
Отсюда пара рабочих привычек:
- просите показать, на чём основан ответ: файл, строку, фрагмент кода. Не пересказ, а то, что вы можете открыть сами;
- одно общее допущение проверяйте отдельно и по-настоящему - что делает код и что происходит в сценарии. Даже когда все согласны.
Отдельные сессии не гарантируют независимости ошибок: общий источник даёт общий промах. А когда ответы расходятся, разбирайте основания, а не считайте голоса: один может смотреть в актуальный код, а двое в устаревшую заметку.
Проверенное по коду значение поля и становится основой общей модели, о которой договариваются до старта. Если после всех трёх проверок разделение всё ещё выглядит выгодным, переходим к распределению.
Назначьте ответственного за каждую область
Правило на время параллельной работы простое: у одной области изменений один ответственный. Не "этот файл вообще нельзя трогать вдвоём", а "пока идут эти две задачи, пишет сюда вот он". Договорённость на период, а не свойство проекта.
Вторая половина правила важнее первой, и о ней забывают чаще. У общего результата тоже есть ответственный: тот, кто соберёт части вместе и скажет, готово ли это целиком. Если сборку не назначили, она достаётся тому, кто закончил последним, а он как раз меньше всех знает про чужую половину.
Перед запуском посмотрите на проект и проверьте, что нужные части в нём уже как-то разделены. Если нет, это не повод затевать большой рефакторинг ради параллельности: он сам окажется самой крупной задачей дня, и делить будет уже нечего.
Для нашего списка покупок распределение получается такое:
| Область | Кто меняет | Условие |
|---|---|---|
src/filter.js, tests/filter.test.js | Исполнитель A | Отбирает записи по согласованному полю completed |
src/export-csv.js, tests/export-csv.test.js | Исполнитель B | Выгружает переданный набор записей в согласованном формате |
src/item-model.js | Ответственный за сборку | Общая модель записи согласована до старта; изменения обсуждаются со всеми, кто от неё зависит |
src/app.js, tests/filter-export.test.js | Ответственный за сборку | Подключает обе части и проверяет выгрузку отфильтрованного списка |
package.json, package-lock.json | Ответственный за сборку | Новые зависимости исполнители попутно не добавляют |
Таблица распределяет ответственность за изменение, а не права доступа: общие файлы читают все, кому они нужны.
Строка про package.json появилась не из вредности: npm install меняет оба файла сразу, поэтому две параллельные установки дают конфликт на ровном месте, а две разные версии одной библиотеки дают сюрприз при сборке. Если библиотека понадобилась обоим, исполнители возвращают требование, что нужно и зачем. Решение принимает и правку вносит один человек, один раз.
Договоритесь об общей модели до старта
Границы по файлам без общей договорённости работают плохо. Фильтр и экспорт опираются на одну модель записи. Ниже эскиз их интерфейсов, не исполняемый пример: реализации функций здесь не показаны. До старта также договорились сохранять порядок записей, не менять исходный массив и записывать булевы значения в CSV как true/false. Правила экранирования CSV остаются прежними.
// одна запись списка: эти три поля согласованы до старта и не переименовываются
const item = { id: 41, title: "oat milk", completed: false };
// filter.js отдаёт готовый набор записей, ничего не форматирует и не выгружает
filterItems(items, mode); // mode: "all", "active", "completed"
// export-csv.js принимает уже отфильтрованный набор и сам ничего не отбирает
exportCsv(items); // возвращает строку CSV с колонками id,title,completedОбратите внимание, что договорённость тут не про имена файлов, а про вход и выход. "Экспорт получает уже отфильтрованный список" звучит как мелочь, но именно эта строчка решает, кто из двоих отвечает за отбор записей. Без неё оба могут разумно предположить, что отбор на их стороне, и тогда в файл поедет либо всё подряд, либо отфильтрованное дважды.
До старта договариваются и о том, как будут принимать результат. Общий сценарий для нашего примера: у записей 41 и 43 стоит completed: true, у записи 42 - false. Режим "completed" должен показать 41 и 43 в исходном порядке, а CSV - те же две записи с заголовком id,title,completed и значением true в последней колонке. Сценарий проходит через обе части сразу и ловит ровно ту ошибку, с которой началась статья.
И отдельно про изменение самой договорённости. Если по ходу работы выясняется, что общий формат надо менять, это не мелкая правка одного файла. Это повод пересмотреть все задачи, которые от формата зависят, потому что часть уже сделанной работы могла опираться на прежний вариант.
Отдельные рабочие папки (git worktree) договорённость не заменяют. Они разводят правки физически, и это полезно: в общей папке проверка может захватить незавершённые изменения соседней задачи и подтвердить состояние, которое ни один исполнитель отдельно не проверял. Но согласовать формат записи папка не может, а .env, порты и базы по умолчанию остаются общими.
Дайте каждому короткое поручение
Поручение не обязано быть длинным. Семи пунктов обычно хватает: цель, от какой версии начинать, свои файлы, общие договорённости, проверяемый результат, что вернуть в ответе и что делать, если понадобится чужая область.
Цель: фильтр списка показывает все записи, только невыполненные или только выполненные.
Начинайте с: последнего коммита основной ветки, где согласована модель записи.
Ваши файлы: src/filter.js и tests/filter.test.js.
Общая договорённость: поле completed остаётся булевым, имена полей не меняем;
фильтр возвращает массив записей и ничего не выгружает.
Результат: тесты фильтра проходят; на наборе из трёх записей каждый режим
даёт ожидаемый список.
Верните: список фактически изменённых файлов, вывод прогона тестов
и версию, на которой их запускали, оставшиеся вопросы.
Если понадобится изменить src/item-model.js или src/app.js: остановитесь,
опишите нужное изменение и причину, дождитесь ответа.Поручение второму исполнителю устроено ровно так же: своя пара файлов, тот же формат записи, свой проверяемый результат и тот же порядок действий при выходе за границу. Переписывать его здесь целиком незачем.
Теперь про ответ. В нём нужен фактический список изменённых файлов и фактический вывод проверок. Фразу "чужие файлы не тронуты" написать легко, и она появится в ответе тем охотнее, чем настойчивее вы её просили. Сверяйте не с формулировкой, а со списком изменений. Граница в поручении работает как договорённость, а не как запрет: текст запроса не мешает агенту записать в файл, права доступа настраиваются отдельно.
В Claude Code субагент идёт в собственном контекстном окне. Обычный субагент не видит вашей беседы и работает от описания задачи, которое составляет Claude; вариант fork наследует разговор целиком. Так что общие договорённости и критерии лучше назвать в поручении явно. Вернётся сводка: она удобна для чтения, но не показывает, что именно поменялось в файлах, и сама занимает место в основной беседе. Подробнее: документация субагентов.
Пересогласуйте пересечение
Рано или поздно кто-нибудь упрётся в чужую границу. Исполнитель B выясняет, что без правки src/app.js экспорт вообще некуда подключить: кнопка выгрузки не вызывается.
Хороший ответ содержит причину, нужное изменение и зависимость. Подключение в src/app.js уже закреплено за ответственным за сборку, поэтому B может завершить свой модуль и передать запрос на подключение. Если без общего изменения нельзя продолжить свою часть, приостанавливается зависимая работа.
Дальше ходов два. Ответственный за сборку вносит изменение в общий файл сам, потому что это его область. Либо явно передаёт область исполнителю B, но сначала убеждается, что прежний владелец в этот файл сейчас не пишет: передача без остановки даёт ровно ту ситуацию, от которой распределение и защищает.
Чего точно не стоит делать, так это заставлять исполнителя обойти границу. Просьба "app.js не трогай, придумай что-нибудь" обычно даёт копию чужой логики в своём файле или лишнюю прослойку, которую потом никто не может объяснить. Формально таблица соблюдена. по сути в проекте появилась вторая правда о том, как работает выгрузка.
Соберите поведение, а не только файлы
Перед сборкой ответственному нужно понять три вещи: от какой версии стартовала каждая часть, что и на какой версии проверил каждый исполнитель, и какие файлы изменились на самом деле. Всё это уже должно быть в ответах по поручению, а список файлов сверяется с фактическими изменениями.
Потом части объединяются, и дальше главное: прогнать общий сценарий, о котором договорились до старта. Помешать могут две очень разные вещи.
Текстовый конфликт. Части по-разному изменили один участок, удалили или переименовали файл, и Git не смог объединить их автоматически. Конфликт разрешают, а затем проверяют итоговое поведение.
Смысловая несовместимость. Никто не трогал одну строку вдвоём, текст складывается чисто, сообщения о конфликте нет. И именно тут живёт наш случай с переименованием: фильтр читает done, экспорт читает completed и получает пустоту. Проверки обеих частей при этом зелёные, каждая проверяет своё и на своих данных.
Объединение без конфликтов не доказывает совместимость поведения. Общий сценарий можно пройти через интерфейс или тестом связки функций; локальные проверки его не заменяют.
На схеме смотрите на нижнюю стрелку. Верхняя половина это то, что обычно и называют разделением работы: две независимые части в своих файлах. А вывод о готовности живёт этажом ниже, в сценарии, который проходит через обе части сразу. Пока эта стрелка не пройдена, у вас есть два локальных результата и никакого общего. Если правила проекта требуют полный набор проверок, общий сценарий его не отменяет.
Зафиксируйте общее решение
После сборки ответственный за общий результат выбирает следующий шаг. Например:
- Принять общий результат: сценарий проходит, изменения объяснимы, критерии закрыты.
- Вернуть конкретную часть на доработку, назвав, что именно не сходится и с чем. Не "экспорт какой-то странный", а "экспорт читает поле, которого в модели больше нет".
- Пересмотреть само разделение: уменьшить число исполнителей или сделать части последовательными.
Третий вариант нормальный, а не провальный. Признаки, по которым состав пора пересматривать, заметны довольно быстро:
- общий формат записи меняется снова и снова, и каждый раз за ним переделывают обе части;
- исполнители ждут друг друга - значит, зависимость была, её просто не назвали;
- результаты приходят быстрее, чем вы успеваете их читать.
Последний признак самый неприятный, потому что снаружи выглядит как успех. И одно удачное разделение ещё не закономерность: результат агента меняется от запуска к запуску, поэтому важный вывод стоит повторить на сопоставимой задаче.
Частичный результат тоже нормальный итог. Фильтр может быть готов и проверен, а экспорт нет, и это стоит записать как есть: одна часть из двух. Общая задача при этом остаётся открытой. Считать её закрытой на том основании, что половина работает, не выйдет, и никакая аккуратность распределения этого не меняет.
Проверьте себя
Две ситуации. Сначала ответьте сами, потом сверьтесь.
Причина ошибки ещё не найдена.
Экспорт иногда выдаёт пустой файл, и хочется сразу поручить исправление двоим. Начните с воспроизведения и исследования. Если выявились разные проверяемые гипотезы, их можно поручить двум исполнителям. Но два предложенных исправления без основания ещё не решают задачу: каждое нужно связать с причиной и проверить на исходном сценарии.
Файлы разные, а число одно.
Фильтр считает количество товара в штуках, экспорт выгружает то же самое количество в упаковках. Проверки обеих частей зелёные, а числа на экране и в файле не совпадают. Это не ошибка в коде: ни одна из частей не нарушила свою границу. Не хватает договорённости о единице измерения. Она фиксируется в общей модели записи до старта, правку в src/item-model.js вносит ответственный за сборку, после чего общий сценарий прогоняется заново.
Что из этого берёт на себя Claude Code
Черновик разбиения агент предложит, как показано в начале статьи, а по вашим пунктам соберёт связный текст поручения. Ещё три места, где он полезен, а решение остаётся за вами:
- Черновик карты областей. Попросите пройти по проекту и предложить, какие файлы меняются под какую часть задачи, с путями. Это именно черновик: ответственного за каждую область назначаете вы.
- Сборка и общий прогон. Агент объединит части, покажет полный список изменений и прогонит общий сценарий. Изменения после этого смотрите сами, целиком.
- Спорное место. Два уверенных объяснения не делают решение принятым. Просите основания, файл и строку, а кто меняет общий файл, определено заранее таблицей, а не тем, чей ответ звучит убедительнее.
В итоге всё держится на одном решении и двух назначениях. Решение - стоит ли делить: у каждого дополнительного исполнителя должна быть польза, которая оправдывает подготовку, проверку и сборку его работы. Назначения - ответственный у каждой области изменений и ответственный у общего результата. В истории со списком покупок не хватило именно этого: ни договорённости о поле completed, ни того, кто прогонит фильтр и экспорт вместе. Инструменты снимают механическую часть работы, а вывод о том, что собранное поведение действительно то, которое заказывали, остаётся человеку.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ