Issue-to-PR: спочатку зрозуміти, потім змінювати
У реальній команді завдання приходить не акуратною постановкою, а сирим issue з трекера, де факти, очікування і чуже готове рішення змішані в одному абзаці. Спокуса велика: відкрити вказаний у тікеті файл і одразу поправити - але саме так з'являються акуратні fix не там, де треба.
Тому сьогодні ми підемо іншим шляхом: розберемо тікет на факти й гіпотези, простежимо шлях даних у plan mode, зберемо план, який можна перевірити, і розріжемо його на невеликі reviewable slices - не торкнувшись жодного рядка вихідного коду.
- відокремити проблему від запропонованого fix;
- дослідити тільки потрібний code path;
- пов'язати ризики з перевірками і stop conditions;
- зупинитися перед кодом з ясним наступним кроком.
Тікет виглядає яснішим, ніж він є
В issue #731 вже написано, який файл треба виправити. Це звучить зручно, але тікет підтверджує симптом, а не місце помилки.
# Issue #731
Після скасування доставка зникає з Active,
але після refresh з'являється знову.
Сховайте cancelled delivery в ActiveDeliveries.tsx.
Скриншот додано.
Після refresh дані приходять заново. Frontend-фільтр помилку API тільки сховає - не полагодить.
- зарано - "виправ
ActiveDeliveries.tsx"; - інженерний старт - "відділи факт, очікування, гіпотезу і unknown".
Код розташований посередині маршруту
Якщо почати з implementation, можна швидко зробити акуратний fix не там, де треба. Спочатку перетворюємо issue на шлях, який можна перевірити.
На цьому вебінарі докладний маршрут закінчується на Slices. Implementation починається тільки після перевірки evidence, scope і ризиків. Далі - implementation, commits і review: це тема наступного рівня.
- tiny fix - один issue comment і один check;
- звичайне multi-file завдання - один
issue-plan.md; - high-risk change - owner review і суворі stop conditions.
Факт, очікування, гіпотеза, прогалина
Одне речення тікета часто виглядає як вимога. Розкладіть його на частини - і стане видно, що вже доведено, а що поки лише здається правдою.
| Частина issue #731 | Статус |
|---|---|
| Після refresh доставка знову видима | спостережуваний факт |
| Cancelled не повинна бути в Active | очікувана поведінка |
| Потрібен frontend-фільтр | гіпотеза автора |
| Який endpoint повертає список | unknown |
| Не змінювати всю status model | межа завдання |
Evidence відповідає "що ми знаємо", а hypothesis - "що варто перевірити". Не міняйте ці ролі місцями.
Issue Intake задає вхід для дослідження
Короткий Intake прибирає готовий fix з вимог і показує, що досліджувати далі.
# Issue Intake #731
Problem: після refresh скасована доставка знову в Active.
Requirement: Active не містить cancelled після перезавантаження.
Evidence: скриншот + відтворювана послідовність refresh.
Hypothesis: фільтр у frontend - правильний fix.
Unknown: який API віддає список і хто ним ще користується?
Non-goal: не переглядати модель статусів доставки.
Risk: frontend-фікс залишить поведінку API неправильною.
Перед investigation (дослідження завдання по коду) перевірте п'ять речей:
- symptom можна відтворити або точно описати;
- desired behavior можна спостерігати;
- solution відокремлене від problem;
- open questions видимі;
- scope не містить "заодно".
issue-plan.md. Новий файл потрібен лише тоді, коли він допомагає review.Спочатку досліджуємо без правок
Plan mode розділяє читання і дію: Claude досліджує codebase і пропонує підхід ще до редагування вихідних файлів. Він потрібен, коли неправильний перший fix обійдеться дорожче за коротке дослідження.
/plan investigate issue #731
Do not edit source files.
Trace the Active deliveries data path.
Return files, entry points, tests,
evidence, unknowns and risks.
Межа режиму проста: source files не змінюються до review плану.
- unfamiliar codebase - спочатку знайдіть call path;
- multi-file change - перевірте межі заздалегідь;
- очевидна одруківка - plan mode може бути зайвим.
/ultraplan - опціональний research preview у Claude Code on the web. Він потребує GitHub repo і ділить загальний rate limit акаунта. Локальний plan mode залишається основним шляхом.Не картографуємо весь repo
Широкий запит "поясни весь проєкт" принесе багато контексту і мало рішення. а issue #731 потребує тільки шляху Active deliveries.
- знайти UI action і network request;
- простежити route і controller;
- перевірити service і repository;
- знайти tests та інших consumers;
- подивитися error і edge paths.
rg "status=active|ActiveDeliveries" src test
# src/ui/ActiveDeliveries.tsx
# src/api/deliveries.ts
rg "findAll|findByStatus" src/server test
# src/server/DeliveryService.ts: return repo.findAll()
# test/api/deliveries.test.ts
Пошук ще не доводить fix. Він показує перший call path і місце, де query може губитися.
Evidence змінює напрям рішення
Investigation корисне не тоді, коли підтверджує першу здогадку, а коли допомагає вчасно від неї відмовитися.
# Investigation #731
Evidence
- UI рендерить відповідь API без локального кешу.
- GET ?status=active доходить до DeliveryController.
- DeliveryService ігнорує status і кличе findAll().
- Mobile використовує той самий endpoint.
- API test не перевіряє фільтрацію за status.
Unknown
- Невідомий status має повернути 400 чи порожній список?
- до investigation - "потрібен frontend filter";
- після investigation - backend ігнорує
status; - новий ризик - web можна виправити, а mobile залишити неправильним.
DeliveryService фінальним місцем зміни. Спочатку перевірте repository pattern і контракт query.Невідомість отримує наступний крок
Вимагати повного знання до плану так само шкідливо, як ховати невідомість. Розділіть unknown за впливом на рішення.
| Unknown | Owner | Next step | Blocks? |
|---|---|---|---|
| відповідь на невідомий status | API owner | уточнити контракт | так |
| ім'я test fixture | implementer | взяти наявний патерн | ні |
Зупиніться, якщо змінюється public API, зачеплена shared status model або product rule не підтверджене. Ім'я helper і вибір fixture можна залишити implementer.
Plan фіксує рішення, а не історію пошуку
Якщо вставити в plan весь transcript, implementer знову шукатиме рішення всередині історії. Залиште тільки те, що спрямовує зміну - і все.
- goal - яка поведінка змінюється;
- scope і non-goals - де межі;
- affected files і steps - де і в якому порядку міняти;
- assumptions і risks - що ще може змінити шлях;
- verification - чим довести результат;
- stop conditions - коли повернутися до людини;
- based on - issue state і base commit плану.
Для issue #731 обраний шлях іде через наявний backend query, зберігає endpoint contract і додає regression evidence.
Reviewable plan на одному екрані
Ті самі поля, але вже заповнені. Такий план читається за хвилину, і за ним можна ухвалити рішення, не відкриваючи transcript.
# issue-731-plan.md
Goal: Active не показує cancelled після refresh.
Scope/approach: передати status із service в repository query; без frontend filter.
Files: DeliveryService, DeliveryRepository, API test.
Steps: відтворити в тесті -> полагодити query -> перевірити consumers.
Assumption: cancelled не входить в Active.
Risks: mobile використовує той самий endpoint; особливе значення active.
Verification: targeted API test + наявні тести зелені.
Stop: змінюється response shape або assumption не підтверджується.
Based on: issue #731 у стані Open, commit a1b2c3d.
Зауважте: план не переказує investigation. Кожен рядок або спрямовує дію, або задає межу.
Ризик без реакції не допомагає
Risk list потрібен не заради повноти документа. Він пов'язує можливу проблему з check, mitigation або stop condition.
| Ризик | Сигнал | Реакція |
|---|---|---|
| Mobile залежить від endpoint | знайдено client usage | перевірити contract test |
active має особливий meaning | docs і code розходяться | stop і спитати owner |
| Fix тягне redesign | affected files ростуть | винести окреме завдання |
| Happy path змінюється | existing tests падають | повернути plan на revision |
Для звичайного завдання досить 3-5 реальних ризиків. Рядок "Claude розбереться на ходу" не вважається реакцією.
Verification пишеться до реалізації
Перевірка до коду швидко показує слабкий plan: спостережуваний результат доводиться формулювати одразу, відкрутитися не вийде.
- targeted API check - Active виключає cancelled delivery;
- regression check - endpoint без query зберігає contract;
- consumer sanity - web і mobile отримують одну семантику;
- diff review - немає frontend filter і broad refactor;
- manual smoke - refresh не повертає скасовану доставку.
targeted API test passes
existing delivery API tests pass
diff stays inside approved files
Machine-check доводить технічну поведінку. Product owner відповідає за product rule і окремо підтверджує, що cancelled справді не входить в Active.
Як повернути plan на revision
Чернетка плану звучить упевнено, але рішення залишається за людиною. Виходів три:
| Рішення | Коли обрати |
|---|---|
| approve | evidence, scope і checks достатні |
| revise | напрям правильний, але plan занадто широкий |
| stop | product rule не підтверджене або scope змінився |
Revise - це конкретні межі, а не "зроби краще":
Revise the plan.
Do not add a frontend filter.
Keep the change in the existing status query path.
Add the mobile consumer risk and the targeted API check.
Stop if the public response shape must change.
Результат: frontend filter виключений з підходу, у плані з'явилися mobile risk і targeted check, stop condition став ширшим.
Одна ідея, один зрозумілий check
План, реалізований одним шматком, приходить на review одним великим diff - чесно перевірити його вже ніхто не може. PR slice - мінімальна логічна частина approved plan, яку можна зрозуміти, перевірити і за потреби відкотити окремо.
- intent - одна спостережувана ціль без "і ще";
- files - вузька зона очікуваного diff;
- check - один зрозумілий сигнал результату;
- non-goal - чого цей крок не робить;
- stop - за якого відкриття повернутися до plan.
Погано: "полагодити API, підчистити status model і оновити dependencies".
Краще: "закріпити filtering contract для status=active в API test; production code поки не чіпати".
Кожен slice рухає criterion або знімає ризик
Approved plan перетворюється на послідовність, де кожен крок має один intent і вихід, який можна перевірити.
Перший slice закріплює симптом, другий змінює мінімальну production-зону, третій закриває consumer risk.
| Slice | Check | Stop |
|---|---|---|
| Відтворити баг фільтрації | test падає до fix | symptom не відтворюється |
| Виправити наявний query path | targeted test проходить | змінюється форма response |
| Перевірити consumers | web і mobile отримують одну семантику | потрібне нове product rule |