Инстинкт при сложной фиче — «давай что-нибудь попробую» — но архитектурные решения прячутся в третьей модели, в пятом edge case. Настоящая ценность plan mode в том, чтобы перенести архитектурный разговор в текст до кода. Реальный кейс: система общественного жюри Pickful — 3032 строки, 119 spec зелёные, один коммит, ноль архитектурных переделок после.
Столкнувшись со сложной фичей, у большинства инстинкт один — «дай попробую что-нибудь».
Проблема в том, что у сложных систем есть особенность — архитектурные решения прячутся в третьей модели, в пятом edge case, в восьмом правиле начисления очков. Нырнуть в код и наткнуться на эти решения посередине, а потом возвращаться и менять их, обходится в 10× дороже, чем проговорить всё в тексте с самого начала.
Я использовал plan mode Claude Code, чтобы собрать TopicReview — общественную систему присяжных в Pickful, — и увидел крайний вариант этого неравенства: один коммит, 3032 строки, 119 спеков зелёных, отгружено за один раз. Ни один из десятка с лишним последующих коммитов не был архитектурной переделкой — всё это подстройка параметров, допиливание UI, заплатки для edge case. Система с тех пор работает стабильно и сейчас является центральной для того, как сообщество модерирует само себя.
Эта статья о том, почему сложные фичи требуют сначала plan mode, что на самом деле делает фаза plan и когда разговор достаточно созрел, чтобы начинать писать код.
TopicReview — это общественное голосование, решающее, удалять ли некачественный пост. Одной фразой — но спецификация раскладывается слоями:
Суть сложности не в отдельных правилах — она во взаимодействии правил. Каждое новое правило может запустить rollback где-то ещё.
Когда пишешь с разгона, самые сложные проблемы — не видимые факты, а вопросы, которые тебе не пришло в голову задать. Прочитав код TopicReview задом наперёд, видно минимум четыре стены, о которые ты неизбежно ударишься, если пропустишь plan:
Правила отбора присяжных. На вид просто User.jurors_and_judges.sample(12). Но реальные правила: исключить автора поста, исключить того, кто на него пожаловался, исключить того, кто уже голосовал в первичной стадии (чтобы один и тот же человек не голосовал и в апелляции). Три слоя исключений. Пишешь модель одним махом — и пропустишь одно-два.
Отложенная выдача очков. Вердикт remove обычно запускает выдачу очков присяжным. Но апелляция может перевернуть remove — и когда переворачивает, присяжные, голосовавшие за старый вердикт, оказываются на неправильной стороне, и выдачу надо пересчитать против нового вердикта. Значит, вердикт remove должен ждать закрытия 24-часового окна апелляции, прежде чем платить присяжным. Пропустишь это правило, выдашь очки раньше — потом будешь их отзывать: в 10 раз геморройнее, чем заплатить позже.
Проектирование интерфейса запланированной задачи. CloseTopicReviewJob с виду «закрыть review». На практике он обрабатывает три ситуации:
# Истечение окна первичного голосования
CloseTopicReviewJob.set(wait_until: voting_ends_at).perform_later(review.id)
# Отложенная выдача очков присяжным после вердикта remove
CloseTopicReviewJob.set(wait: 24.hours).perform_later(review.id, award_juror_points: true)
# Истечение окна апелляции
CloseTopicReviewJob.set(wait: 24.hours).perform_later(review.id, appeal_id: appeal.id)
Без предварительного планирования напишешь первую сигнатуру, а во втором случае обнаружишь, что интерфейс надо менять.
Самая дорогая стена: можно ли подать апелляцию во время временного удаления? Когда голоса remove превышают порог, пост немедленно скрывают (временно), но review всё ещё в состоянии voting, а не decided. Может ли пользователь уже подавать апелляцию?
decided и appealedОдно это решение расходится назад по коду и меняет проверки параметров в open_appeal! и логику конечного автомата. Принять его посередине = переписать половину системы.
Plan mode Claude Code — это режим, в котором писать код запрещено: Claude может читать репо, обдумывать подходы, обсуждать с тобой, но любая модификация файлов жёстко блокируется, пока ты не одобришь план.
Это механическое ограничение и есть суть: оно заставляет разговор архитектурного уровня происходить в тексте.
Несколько вещей, которые фаза plan делает на практике:
1. Рисует конечный автомат и роли. 5 состояний, 4 роли (присяжный / судья / автор / жалобщик), что каждая роль может и не может делать в каждом состоянии — всё в нескольких строках markdown. Несколько строк vs десятки файлов. Стоимость изменения отличается на два порядка.
2. Прогоняет поток каждой роли:
Где шаг застревает, всплывает скрытый вопрос: «видят ли присяжные голоса других присяжных?», «что может делать автор во время временного удаления?»
3. Допрашивает edge case. Фаза plan не проектирует «нормальный путь» — она специально задаёт вопросы, которые обычно не всплывают:
finalize — гонка удвоит выплату? (Добавить поле juror_points_awarded для идемпотентности.)95% таких вопросов не всплывают естественно при написании кода. Фаза plan заставляет пройтись по ним по одному.
4. Бухгалтерская книга очков. Система очков достаточно сложна, чтобы обсуждения было мало — надо реально нарисовать таблицу: каждый поток очков с триггером, суммой и путём rollback. Когда книга сходится, все edge case (возврат временного штрафа, возврат апелляции, бонусы) согласуются.
Жёсткий критерий:
closed (keep / remove / warn × с апелляцией / без × временное / нет) рассказать от начала до концаКогда планка достигнута, писать код — значит переводить план на Ruby.
Первый деплой системы:
d162f1e Add community moderation system with jury/judge review and appeals
63 files changed, 3032 insertions(+)
119 specs, 0 failures
63 файла, 3032 строки, 119 спеков зелёных. Задеплоено за один раз.
Следующие десяток с лишним коммитов подтверждают, что архитектура устояла:
Apply legacy penalty (1pt) for posts created before 2026-03-26
Fix topic review appeal bugs: window mismatch and verdict not updated
Add topic_removed status for posts removed by community review
Default to keep verdict when review expires with zero votes
Reduce provisional removal penalty to 1pt during trial period
Allow topic creator to withdraw active reviews
Add handled tab to jury dashboard, fix tabs styling
Каждый — подстройка / фикс / полировка / маленькая фича. Ни один не «вернуться и перепроектировать конечный автомат» или «модель очков надо менять». Каркас оказался полностью верным.
Это и есть настоящая отдача plan: выполнение не прерывается. Нет «подожди, а этот путь как?» — план уже покрыл. Нет «про этот edge case я не подумал» — фаза plan спросила. Нет «этот интерфейс надо переделывать» — план уже решил.
Часы написания кода, делая только одно: переводить ясный дизайн на клавиатуру.
Не всякая задача достойна plan:
Выгода plan mode в снижении стоимости переписывания. Если в задаче нет риска переписывания, plan — просто накладные расходы.
«Сначала подумай — потом делай» звучит как совет по характеру. Plan mode не об этом.
Plan mode — это когда разговор архитектурного уровня происходит там, где 10 слов могут его изменить, а не там, где придётся менять 30 файлов.
В условиях сложности стоит перевернуть старое отраслевое «words are cheap, code is expensive» — оно не значит «пиши код сначала, там видно будет» (это работает только при малом разрыве стоимости), оно значит использовать этот разрыв: перенести дорогой разговор вперёд, в дешёвую среду.
3000 строк одним коммитом — ощущение прекрасное. Не потому что я быстро пишу, а потому что план превратил «писать» в чисто механический акт.