Тобі дали метод processOrder і попросили «просто відрефакторити його перед міграцією».

У ньому 380 рядків. 14 параметрів, половина з них булеві з назвами на кшталт skipDiscount і forceRecalculate. Він викликає сім інших методів, які ти ніколи не відкривав. Є коментар від 2018 року: // DON'T TOUCH - Finance will riot. Автор пішов із компанії у 2020-му. У тікеті два речення й жодного критерію приймання.

Твій перший порив логічний. Ти відкриваєш Cursor, виділяєш увесь метод і пишеш «відрефактори це для читабельності». І саме тут, статистично, все починає йти не так.

Проблема не в ШІ. ШІ робить рівно те, що ти попросив. Проблема в тому, що ти попросив його змінити те, чого ще не розумієш. Цей порядок дій майже всі плутають. Раніше ціна помилки була кілька годин дебагу, а тепер це diff на 4000 рядків, що чудово виглядає на рев'ю й тихо ламає нічну джобу білінгу.

Рішення легко описати й напрочуд важко виконати: спершу зрозумій, потім рефактори. ШІ чудово робить обидва кроки. Але тільки якщо ти ставишся до них як до двох окремих завдань.

Ця стаття про перше завдання. Три конкретні процеси, як за допомогою ШІ зрозуміти легасі-метод, перш ніж він змінить бодай один символ: підсумовування, карта залежностей і виявлення прихованих правил. Жоден із них не пише код. Кожен дає артефакт, який ти можеш прочитати, поширити й перевірити. А разом вони складають те, що я б назвав досьє розуміння: саме те, що ти хотів би, щоб тобі залишив попередній автор.

Чому ми пропускаємо крок із розумінням

Індустрія не винагороджує розуміння. Вона винагороджує diff'и. Story points, кількість PR, «throughput»: усе це міряє обсяг змін, які ти видаєш, а не глибину того, що ти знаєш про систему. Тож коли з'явився ШІ й здешевив зміни вдесятеро, природним поривом було робити вдесятеро більше змін. А порив здешевити розуміння теж вдесятеро прийшов пізніше, якщо взагалі прийшов.

Це помилка, і щокварталу вона коштує дедалі дорожче.

ШІ працює як підсилювач. На що його не наведеш, те він робить швидше. Наведи на «рефактори» і отримаєш рефакторинг на швидкості ШІ, разом з усіма частинами, про які ти й не здогадувався, що метод їх робить. Наведи на «поясни» і отримаєш розуміння на швидкості ШІ, разом із тими частинами методу, які ти ніколи не помітив би під час читання. Та сама модель, зовсім інша цінність, і залежить вона лише від дієслова, яке ти обрав.

Досвідчений інженер витратить першу годину на пояснення, щоб заслужити право витратити другу на зміни. Ця година купує тобі рефакторинг, який лягає чисто, PR, що рев'юить сам себе, і недільний вечір, який не перерве пейджер.

У трьох проходів нижче є природний порядок, і він важливий. Ти не складеш карту залежностей того, що не можеш підсумувати. Ти не побачиш прихованих правил, поки не знаєш, хто споживачі. Кожен прохід спирається на попередній. Якщо перестрибнеш наперед, отримаєш «рефакторинг», що ламає щось, про що тобі ніхто не казав.

Прохід 1. Підсумовування (що він насправді робить?)

Це найефективніший спосіб застосувати ШІ в легасі-коді, і майже ніхто не виконує його як окремий крок. Люди стрибають одразу від «дай-но пробіжуся по методу» до «дай-но відрефакторю його» й пропускають момент, коли отримуєш чіткий письмовий опис того, що він робить.

Мета проходу 1: отримати опис методу на один абзац простою мовою, написаний ззовні всередину. Що він приймає? Що повертає? Що змінює у світі? Не покроковий розбір рядок за рядком. Підсумок, такий, який ти написав би в Slack, щоб пояснити метод колезі за кавою.

Промпт навмисно нудний:

Text prompt-summarize.txt
Below is a single method from a legacy codebase. I want you to:

1. Describe in 3-5 sentences what this method does, from a caller's perspective.
2. List every input parameter and what it appears to be used for.
3. List every value the method returns (or "void" + what it mutates).
4. List every side effect: database writes, network calls, file I/O,
   event emissions, exceptions thrown, fields mutated on the input objects.

Do not suggest improvements. Do not explain how it could be simplified.
I am not refactoring yet. I am only trying to understand.

<<< method goes here >>>

Рядок «do not suggest improvements» робить багато роботи. Без нього модель уже за два абзаци зісковзує в «ось як я б це підчистив», а це рівно те, чого ти зараз намагаєшся не робити. Ти ще не будуєш план. Ти будуєш відправну точку.

Читай вивід критично. Іноді модель вигадає побічний ефект, якого немає («цей метод, мабуть, пише в аудит-таблицю», хоча насправді ні), або проґавить реальний («викликає мейлер у try/catch без rethrow, тихо ковтає помилки надсилання пошти», і він справді так робить, але модель пройшла повз). Обидва типи помилок поширені. Сприймай підсумок як стартову гіпотезу, а потім один раз пройдися по методу з підсумком у руках і звір кожне твердження з реальним кодом.

Наприкінці в тебе має бути один короткий абзац, що схоплює поведінку методу на межі (входи, виходи, побічні ефекти), і список тверджень, які ти не зміг перевірити. Цей абзац стає якорем для всього подальшого. Прикріпи його десь, де він не загубиться: у тікеті, у Notion, на початку файлу з методом. Ти перечитаєш його тричі, перш ніж викотиш рефакторинг.

Прохід 2. Карта залежностей (кому є діло до цього методу?)

Метод не живе в ізоляції. Коли ти його рефакториш, ламається не сам метод, а ті сім викликачів, що покладалися на його стару поведінку так, як код самого методу й не показує.

Саме тут ШІ заощаджує тобі найбільше часу, бо перелічування моделям вдається до неприличного добре. Проходити реальну кодову базу руками, щоб знайти кожне місце виклику, кожен override, кожен тест, кожне посилання в конфізі: це години grep, клік, скрол, щось проґавив, знову grep. ШІ робить це за хвилини, якщо правильно налаштувати.

Процес має дві половини: що метод викликає і хто викликає його.

Половина перша: вихідні залежності. Чого торкається цей метод?

Text prompt-outbound-deps.txt
For the method below, list every external dependency it uses:

- Every other method or function it calls.
- Every static helper or utility.
- Every database table or query.
- Every environment variable or config flag.
- Every external service (HTTP client, queue, cache, etc.).
- Every global or singleton it reads from or writes to.

For each entry, note whether the dependency is "obvious from the signature"
or "hidden in the body".

<<< method goes here >>>

Найбільше важить рамка obvious vs hidden. Саме приховані залежності кусаються: глобальний currentUser(), закопаний на п'ять рядків углиб, обгортка кешу, що тихо віддає застарілі дані, feature flag, який змінює поведінку в проді, але не в staging, логер, що ковтає винятки в тестовому режимі. Уся суть цієї половини в тому, щоб витягти їх на поверхню заздалегідь. Щойно вони на папері, з ними можна працювати. Доти вони пастки.

Половина друга: вхідні залежності. Хто викликає цей метод?

Тут потрібно більше, ніж тіло методу. Моделі потрібна вся кодова база. У сучасних ШІ-інструментах для коду (Cursor, Claude Code, Aider, Continue, Cody) можна просто спитати:

Text prompt-inbound-deps.txt
Search the codebase for every call site of `OrderService.process`.
For each one, tell me:

- The file and line.
- The conditions under which it's called (the surrounding `if`,
  the function it lives in, the route or job it belongs to).
- What the caller does with the return value.
- Whether the caller has its own test coverage that exercises this call.

Останній пункт важить більше, ніж здається. Якщо викликач не має тестового покриття, ти не можеш безпечно змінити його поведінку, бо ніщо не зловить регресію. Модель підкаже, які місця виклику «несучі, але незахищені». Це твої споживачі з найвищим ризиком, і саме туди насамперед мають піти твої характеризаційні тести.

Тепер ти будуєш карту. Не UML-діаграму (будь ласка, не роби UML-діаграму). Простий ескіз на одну сторінку: метод посередині, стрілки назовні до його залежностей, стрілки всередину від його викликачів, і кожна стрілка підписана тим, що цікавить інший її кінець. Підійде вайтборд. Підійде серветка.

Коли ескіз готовий, ти маєш могти відповісти на питання: «Якщо я зміню тип, що повертає цей метод, з Order на OrderResult, скільки місць зламається і в яких із них є тести?» Саме від відповіді на це питання залежить, буде це рефакторинг на день чи інцидент на три тижні.

Карта залежностей одного легасі-методу: центральний вузол processOrder з вихідними стрілками до залежностей і вхідними стрілками від чотирьох викликачів, кожен викликач позначено статусом тестового покриття.

Прохід 3. Приховані правила (те, чого код не каже)

Це найважчий прохід і водночас найкорисніший. Прихованими правилами я називаю неявні інваріанти, на які метод покладається, але яких сам не перевіряє. Це те, що колись було правдою про входи, базу даних, навколишню систему, час доби, контекст виклику, і що ніхто не задокументував, але всі припускали.

Кілька категорій, із формами, які я впізнав би в будь-якій великій кодовій базі:

  • Припущення про порядок операцій. «Цей метод треба викликати після того, як validateOrder відпрацював у тому самому запиті.» У коді про це ніде не сказано. Якщо викличеш їх не в тому порядку, отримаєш зіпсований запис, що проходить усі подальші перевірки.
  • Припущення про форму вхідних даних. «Масив items завжди має хоча б один елемент, бо фільтр вище по потоку прибирає порожні кошики.» Той фільтр відрефакторили два роки тому. Тепер метод тихо повертає 0 для порожніх масивів, а дашборд тихо занижує виручку.
  • Припущення про середовище. «Цей метод припускає, що запит загорнутий у транзакцію бази даних.» Запусти його з CLI-команди, і отримаєш часткові записи, коли третій запит впаде.
  • Часові припущення. «Цей метод припускає, що поточна дата припадає на робочий день.» Запусти його із суботнього cron, і в згенерованих інвойсах будуть некоректні дати оплати.
  • Припущення про наявність полів. «Якщо customer.tier дорівнює null, вважай його standard.» У коді ніде не сказано, що customer.tier може бути null, але якщо колись стане, решта кодової бази впаде, бо вона припускає, що поле завжди задане.
  • Припущення про конкурентність. «Тільки один викликач колись звертатиметься до цього для одного замовлення одночасно.» У 2017-му автор мав рацію. Нова політика повторів, додана у 2023-му, помиляється.

Промпт:

Text prompt-hidden-rules.txt
Read this method carefully and list every assumption it appears to make
about its inputs, its environment, or the broader system state.

I'm specifically looking for things that aren't enforced in the code
but that the method clearly relies on. Examples:

- "Assumes `user.organization` is loaded" (used, never checked).
- "Assumes there's an active DB transaction" (rollback-able operations
  happen but no transaction is started here).
- "Assumes the calling code has already verified permissions"
  (no auth check inside).
- "Assumes the input array is sorted by date" (relies on order
  without sorting).

Do not speculate. Only flag assumptions that are visible in the code,
anywhere a value is *used* without first being *checked* or *guaranteed*.

<<< method goes here >>>

Рамка used without checked or guaranteed тримає модель на якорі. Без неї отримаєш спекуляції («метод припускає, що в користувача є ім'я»). З нею отримаєш перевірювані твердження («метод розіменовує customer.subscription.tier без перевірки на null для customer.subscription»).

І ось що важливо: перевір кожне справжнім тестом, а не читанням. Напиши маленький тест, що порушує припущення. Передай customer.tier = null. Запусти метод поза транзакцією. Передай порожній масив items. Подивись, що буде. Якщо тест падає з null pointer чи частковим записом, приховане правило було реальним. Якщо проходить, припущення було надто обережним, і ти щойно дізнався, що метод надійніший, ніж здавався.

Цей прохід найдовший. Двадцять припущень для 300-рядкового легасі-методу зовсім не дивина. Більшість із них виявляються або тривіально істинними, або вже захищеними чимось іншим вище по потоку. А ті три-чотири, що справді валяться, коли ти їх по-справжньому промацуєш, і є причиною робити цей прохід узагалі. Це ті баги, що поїхали б разом із твоїм «чистим рефакторингом» і виринули б через два місяці, а винним назавжди оголосили б ШІ.

Триетапний процес розуміння у вигляді трьох з&#39;єднаних карток із написами Summarize, Map Dependencies та Surface Hidden Rules, що збігаються в єдину смугу Comprehension Dossier під ними.

Що в тебе залишається

Три проходи, кожен дає відчутний артефакт. Разом вони складають досьє розуміння для методу:

  1. Підсумок: короткий абзац простою мовою, що схоплює поведінку й побічні ефекти.
  2. Карта залежностей: вхідні й вихідні, з позначками тестового покриття на викликачах.
  3. Список прихованих правил: кожне або підтверджене тестом, або позначене «потребує дослідження».

Це займає більше часу, ніж люди очікують. Може, дві години на складний метод. Може, цілий день на 500-рядкового монстра з тридцятьма місцями виклику й заплутаною історією. Цього часу досить, щоб стало незатишно через те, що ти «ще нічого не викотив», і саме в цей момент варто згадати, навіщо ти це робиш.

Компроміс однозначний. Без досьє рефакторинг пишеться день, дебажиться тиждень, і є ненульовий шанс, що він так і не стабілізується повністю. З досьє рефакторинг пишеться той самий день, але перевіряється за пів дня, бо ти вже знаєш, що може зламатися, і вже написав тести проти цього. Математика майже завжди на боці досьє, і саме ШІ робить досьє достатньо дешевим, щоб його справді будувати у 2026-му, а не через три роки.

Приємний побічний ефект: саме досьє стає навчальним артефактом. Наступна людина, якій доведеться торкнутися цього методу (це можеш бути ти сам через пів року), почне з твоїх нотаток, а не з нуля. Якщо збережеш їх десь надовго (у тікеті, у вікі, у блоці коментарів усередині методу), то перетвориш приватну годину розуміння на постійний шматок інституційної пам'яті.

Кілька слів про інструменти

Три процеси вище навмисно не прив'язані до інструменту. Вони працюють у Cursor, Claude Code, Aider, Continue, Copilot Chat, Cody чи просто у вікні чату з копіюванням-вставлянням. Різниця між ними здебільшого в тому, скільки кодової бази модель бачить за раз.

  • Прохід 1 (підсумовування) працює будь-де. Моделі потрібне лише тіло методу.
  • Прохід 2 (карта залежностей) хоче інструмент із grep по всьому репозиторію. Cursor, Claude Code, Aider, Cody підходять усі. Чат із копіюванням-вставлянням годиться для вихідних залежностей, але болісний для вхідних.
  • Прохід 3 (приховані правила) це здебільшого тіло методу, з нечастими зануреннями в безпосередніх викликачів. Підійде будь-що, де є контекст на рівні файлу.

Якщо в твоєму інструменті є можливість «скласти системний промпт», постав щось на кшталт: «You help engineers understand legacy code. You never suggest changes. You report what is, not what should be.» Це зменшує природний дрейф моделі в бік «дай-но я це тобі підчищу», а це найпоширеніша поломка коде-обізнаних моделей саме тоді, коли ти хотів, щоб вони сиділи й не рипалися.

Найважче вміння: стриманість

Найважче в усьому цьому зовсім не промпти. Найважче встояти перед бажанням стрибнути одразу до змін.

Модель швидка. Відрефакторена версія методу може опинитися в твоєму редакторі менш ніж за дві хвилини. Вона виглядатиме розумно. Вона пройде наявні тести, бо наявні тести не покривають нічого з того, чого ти не знав. Вона пройде код-рев'ю, бо diff це «просто прибирання», і ні в кого в команді немає часу чи контексту, щоб заперечити. І тоді її змерджать. І тоді вона тихо зламає споживача нижче по потоку, якого ніхто не курує. І тоді черговий інженер витратить суботній ранок, з'ясовуючи, чому нічна джоба білінгу почала пропускати акаунти з tier = null.

Розуміння неефектне. Наприкінці дня немає жодного diff'а. Трекер досі показує «in progress». Ти не покажеш менеджеру скріншот досьє розуміння й не відчуєш себе продуктивним так само, як коли показуєш йому зелений PR.

Але розрив між інженерами, які спершу розуміють, і тими, хто спершу рефакторить, швидко росте, бо ціну нерозуміння підсилюють ті самі інструменти, що здешевлюють рефакторинг. Модель множить будь-яку дисципліну, яку ти приніс до завдання. Не принесеш жодної, отримаєш швидкі впевнені зміни в тому, чого не розумів. Принесеш три проходи вище, і отримаєш ту саму швидкість на змінах плюс єдине, що насправді робить рефакторинг легасі безпечним: чітку, записану картину того, що було тут до того, як ти це торкнув.

Спершу зрозумій, із допомогою. Потім змінюй, упевнено. Увесь сенс у порядку.