Інстинкт перед складною фічею — «дай щось спробую» — але архітектурні рішення ховаються в третій моделі, п'ятому 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 — чи race подвоїть виплату? (Додати поле 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 рядків одним комітом — відчуття прекрасне. Не тому, що я швидко пишу, а тому, що план перетворив «писати» на чисто механічний акт.