Межі, контракти і зупинки за задумом: грані інженерної дисципліни, яку AI зробив обов’язковою
Привіт, спільното! Мене звати Єгор Корнієнко, я понад п’ять років в Android-розробці, останні з яких — в українському продукті. Як і більшість команд, ми постійно тестуємо й впроваджуємо нові AI-інструменти. Але після року активного застосування постає питання: генерувати код ми стали кратно швидше, але чи почали так само швидко доставляти його в прод? Більшість рішень, які реально зближують ці дві швидкості, виявилися давно відомими інженерними практиками. Ця стаття про деякі з них: контракти на межах, зупинки за задумом, фіксація помилок у контексті та розмір дифів.
Ну що ж, поїхали.
Головне питання індустрії зараз не в тому, чи вміє AI писати підтримуваний код. На рівні окремого шматка це вже вирішено. Проблема в іншому: чи вміємо ми, як інженери, декомпозувати задачу так, аби її міг виконати новачок або AI. Погана декомпозиція не сповільнює AI, вона просто робить його марним. Це стосується і headless-режиму, і чату, різниця між ними лише в тому, коли ви отримаєте рахунок: дрібними платежами одразу чи спільним чеком наприкінці пайплайну. Рахунок цей, до речі, не новий. Одного вміння зробити «аби запрацювало» було замало і до AI-буму, просто раніше це ховалося за дедлайнами та «потім зарефакторимо».
Так, тут легко звалитися в снобізм, тому уточню. Одна річ, коли ви свідомо обираєте шлях «аби запрацювало» для MVP. У цьому є раціональність, оскільки ви купуєте швидкість перевірки гіпотези ціною майбутнього рефакторингу. Це також інженерне рішення. Зовсім інша річ, коли іншого режиму немає і ваш код в найкращому випадку тримається на милицях, в найгіршому — просто поганий.
Але, розклавши задачу на кроки, ви маєте знати, чим кожен крок захистити, бо та сама модель на тому самому вході може видати різний аутпут, який впливає на подальші кроки. А якщо вихід кроку різний щоразу, то єдине на що залишається спертись — це заздалегідь домовлені умови.
Контракти між кроками
Автоматичні перевірки між кроками легко списати на забаганки перфекціоністів. Насправді це невід’ємна частина самої декомпозиції. Правильно розкладена задача завжди визначає кроки разом із контрактами між ними.
Скористаємось документацією Claude Code, вона напрочуд чесна. Написати в промпті «ніколи не чіпай .env» означає лише висловити прохання до моделі і сподіватись, що вона послухається. Натомість хук, який технічно блокує таку правку, це вже гарантія. Промпт працює як «тільки одна серія перед сном». Намір щирий, але статистика інша.
Тоні Хоар назвав би це пре- і посткондиціями, і мав би рацію лише наполовину, тому що його умови формулювалися у світі, де перевірений компонент завжди поводився однаково. У нас же ситуація інша. Уявімо, що кожен крок в нашому ланцюжку має
Практичне правило таке: якщо ціна помилки на межі вища за вартість перевірки, захищати її слід технічним механізмом замість звичайного промпту. Зрозуміло, що не для кожної межі є готовий механізм. Хук, тест, лінтер — усе це працює, коли можна сформулювати перевірку заздалегідь і формально. Але що робити, коли кроком є формування плану? Такий результат роботи моделі треба оцінити змістовно, а не формально, і тут найнадійніший механізм, на жаль, людина. І поки ми не можемо покластися на AI в таких випадках, пора перестати сприймати зупинку headless-пайплайна як щось небажане.
Зупинка за задумом
Чи вважаєте ви запобіжник у щитку зламаним, коли він перегорає? Навряд, адже це штатна робота системи захисту. Гірше, коли запобіжника немає взагалі, і замикання тихо гріє проводку, поки не запахне димом. Те саме і з headless-пайплайном. Коли він спиняється, то перший рефлекс вважати це збоєм системи. Але майже завжди все навпаки! Зупинка у передбачуваній точці означає, що декомпозиція спрацювала! Задача дійшла до моменту, який вимагає навички, якої у моделі поки що немає, і чесно про це сказала. Інакше кажучи, система розпізнала межу своїх можливостей і зупинилася, а не поперла далі наосліп.
Пропоную називати це stop-by-design — зупинкою за задумом. Концепція здається близькою до human-in-the-loop, проте HITL зазвичай описує суб’єкта рішення (людину). Натомість stop-by-design визначає саме момент і точну точку цієї передачі. Таку точку розраховують наперед під час декомпозиції, уникаючи хаотичних дій коли щось пішло не так. До того ж отримувачем контексту може бути сильніша модель чи окремий верифікатор. Людина тут слугує лише одним із можливих варіантів.
Дуже показовим є те, що інструментарій, на прикладі Claude Code, перестав ставитися до таких зупинок як до аварій, і вони стали частиною контракту:
- запит підтвердження чи уточнення в людини існує в агентських SDK як окремо спроєктований механізм, а не як exception, який хтось ловить;
- ескалація складного рішення теж є спроєктованою точкою. Агент у ключовий момент консультується з сильнішою і дорожчою моделлю;
- дзеркальний бік тієї ж монети — явна умова завершення. У Claude Code за це відповідає /goal: задаєш умову, і агент працює хід за ходом, поки окрема швидка модель не підтвердить, що її виконано;
- і навіть чекпоінти з відкатом до будь-якого попереднього стану (rewind у Claude Code). Потреба знати, де був останній правильний стан також впирається в декомпозицію.
Коли ви проєктуєте власні оркестратори, то ці чотири механізми та інші, відомі вам, треба закладати явним чином. Крім цього, мають бути і точки зупинки, де на перших порах валідатором має бути людина. На практиці, якщо в довгій задачі трапляється крок, про який ви вже знаєте з власного досвіду, що AI наразі, найімовірніше, впорається погано, саме тут зупиніться і передайте рішення людині. Найчастіше це пов’язано з рев’ю плану або коду. Спроба проштовхнути модель через крок, де вона систематично хибить, гарантовано породжує брак, який потім розповсюджується по всьому ланцюжку. Кожен такий стоп варто фіксувати з деталізацією і описом на предмет того, яка проблема виникла (до цього ми ще повернемося нижче у статті). Навіть якщо відбулася зупинка, але попередній крок пройшов успішно, і ви без додаткового редагування даєте пайплайну зелене світло, це все одно варто фіксувати. З часом буде видно, чи росте success rate генерації.
Той самий принцип, зробити приховане явним, працює і для контексту навколо коду. Не треба сподіватись, що модель зрозуміє ваш творчий порив, з яким ви створили милицю. Модель не реформатор, а імітатор: якщо бачить безлад у репозиторії, то лише множить його. Ще раз наголошу, милиця сама по собі не гріх, шкодить борг несплачений і прихований, на чому наголошував ще Каннінгем. Задокументований безлад агент проаналізує й успішно обійде, тоді як мовчазні милиці він лише масштабує.
Тож зрілий підхід свідомо закладає зупинки в архітектуру. Недбалий headless маскує усі точки сумнівів, тоді як якісна реалізація виводить їх нагору та робить повністю керованими.
Рев’ю стало вузьким місцем не тому, що рев’юерів мало
За бенчмарками LinearB 2026 Software Engineering Benchmarks Report (8,1 млн PR, 4 813 команд, 42 країни), агентські AI-PR лежать у черзі, перш ніж рев’юер їх бодай відкриє, в 5,3 раза довше за людські (1055 хвилин проти 201 на p75). Є великою спокусою інтерпретувати це як «людей-рев’юерів не вистачає». Я ж думаю, що ми платимо податок на нерозкладеність. Але одна цифра нічого не доводить, тож підемо далі.
Після показників навантаження на чергу звіт наводить частку дійсно корисного коду. Протягом 30 днів мерджиться лише 32,7% AI-PR, проти 84,4% людських. Причин звіт називає кілька, і не всі вони про декомпозицію. Так, серед причин є відсутність у AI-PR власника, який проштовхнув би його далі, та низький пріоритет самої роботи, яку доручають AI.
Але найцікавіше починається там, де звіт пояснює, чому рев’юер не наважується апрувнути. За оцінками опитаних, ключова перепона полягає в контексті, тоді як коректність відходить на другий план. AI видає зміни, які виглядають розумно, але при цьому позбавлені наміру, обґрунтування й розуміння ситуації, необхідних, щоб упевнено натиснути апрув. Це і є податок на нерозкладеність.
А тепер найнеприємніше, і мова вже про іншу когорту — AI-assisted PR, де автором лишається людина. Диф більший за людський у 2,6 раза (408 рядків проти 157 на p75), проте щойно рев’юер його відкриває, витрачає 194 хвилини — менше, ніж 252 хвилини на людський PR. За цією швидкістю ховається банальне поверхове проглядання.
Задача, яку розклали на маленькі, ізольовані, самодостатні кроки, породжує малі PR з локальним контекстом, які розробник може перевірити без надмірного когнітивного навантаження. Натомість кілометрові дифи породжують у команді лише два виходи з ситуації: або чергу з кратною втратою швидкості, або LGTM наосліп.
Слушно можете зауважити, «нехай AI і проводить рев’ю». Інструменти для цього вже дійсно є, в тому числі з багатоагентними режимами, де паралельно перевіряється безпека, продуктивність, регресія. Корисно? Звісно так. Але перевірка не може полагодити рішення, ухвалене на вході. AI-рев’ю AI-коду поверх поганої декомпозиції — це охоронець біля вже зруйнованого будинку: він сумлінно вам доповість про кожну нову тріщину. Фундамент від цього рівнішим не стане. Тож ботлнек рев’ю лікують грамотною декомпозицією роботи на шматки замість намагань прискорити сам етап перегляду.
Це не waterfall: декомпозиція як гігієна, а не ритуал
Тут ви можете мене спіймати на слові. То що, повертаємось до big design up front? Спершу піврічне проєктування, потім натиснути enter? Звісно, ні. І не тому, що немає ресурсів на це!
Найкраща ілюстрація знов із практики інструментів. Документація не каже «спершу опишіть усе». Вона дає тригери і відповідні поради, які покращують ваш AI-сетап. Агент удруге помилився в тій самій конвенції? Зафіксуй її в постійному контексті. Втретє вставляєш у чат той самий плейбук? Зроби його скілом. Побічна задача заливає розмову виводом, який ти більше не читатимеш? Винеси її в ізольований контекст. Межі варто проводити за фактом, коли вдруге наступаєте на ті самі граблі.
Помилка, яку нікуди записати, обов’язково повториться
Кожна задача, яку робить AI-оркестратор в нашій команді, лишає по собі окрему директорію з маркдаун-файлами, в яких містяться вхідні дані для реалізації задачі і звіти кроків виконання. Окремо хочу розказати про файл, який не належить жодному кроку, але «стосується кожного», файл, який «при житті став легендою» — defect-log.md. Він містить проблеми і помилки, на які натрапляли під час генерації коду. Кожен запис — рядок таблиці: симптом, модуль, чому це неправильно і що з цього вийшло. Не має сенсу, щоб кожен розробник у своєму локальному чаті або ганяючи агента в headless-режимі щоразу наступав на ті самі граблі. Аби ці граблі прибрати, треба їх ідентифікувати.
Розробнику достатньо просто вказати на помилку в діалозі з чатом, а далі в справу вступає простий скіл, який сам структурує та записує її в таблицю. Якби цей файл доводилося заповнювати руками, практика прожила б два спринти й тихо померла, як і будь-що, що вимагає зайвої рутини під час розробки. А так фіксація практично нічого не коштує тому, хто робить задачу.
Наступним кроком код-оунер проходиться по всіх задачах, витягує всі дефекти, які стосуються його модуля завдяки полю module в таблиці кожного дефекту. Результат розбору дефекту буває трьох видів: рефактор кодової бази; написаний або відредагований md, який підтягується при генерації коду в цьому модулі; або чесне «разова похибка, чекаємо на другий випадок». Але якщо перший раз ще можна вважати збігом, то повторна помилка стає приводом діяти.
А що проти
Тут чесно треба назвати найсильніше заперечення. Якщо AI вже й сам непогано декомпозує задачі, то що тоді залишається людині рев’юїти? Частково тут нема чого заперечити, бо з механічним нарізанням на підзадачі AI вже справляється непогано. Саме тому цінність людини зміщується на крок глибше. Замість механічного нарізання тут потрібна оцінка того, яка декомпозиція підійде конкретній системі з її історією та контекстом, що поки не вміщається в жоден промпт. Згенерувати п’ять варіантів легко, проте обрати той оптимальний, що не розвалиться через дві ітерації, потребує реального досвіду.
Пропоную уявити систему, яка крім стандартного «що й навіщо змінили» додатково фіксує загальний напрямок розвитку та ймовірні виклики попереду, доповнюючи цим контекстом кожну задачу. Тоді модель і справді могла б приймати гарні інженерні рішення. Багато хто щиро в це вірить, зокрема і я. У день, коли ми навчимося фіксувати наміри й історичні рішення, я буду першим, хто віддасть цей крок машині. We’re not there yet.
І щоб у вас не склалося хибного враження, ніби ця стаття про тимчасову слабкість моделей, яка відпаде з наступним релізом, варто сказати: межі, контракти й точки передачі — це властивості систем, а не виконавців. Модуль без чесної межі погано перевіряється, погано тестується і погано змінюється незалежно від того, хто його писав: сеньйор, джун чи агент.
Підсумок
Тож, якщо ваш АІ-сетап генерує сміття, річ, найімовірніше, не в промпті або моделі. Проблеми лікуються старою доброю інженерією, всім тим, що дуже легко залишити за дверима, коли сідаєте за чат. Усе, про що була ця стаття, від контрактів на межах і зупинок за задумом до розміру дифа, це все різні грані одного цілого: інженерної дисципліни, яка ніколи не була про написання коду, вона завжди про розуміння, доведене до такої повноти, що його можна матеріалізувати у вигляді меж, контрактів, точок передачі та безлічі інших рішень, яких у самому коді не завжди видно.
Тому єдина порада, яку можу дати без жодних застережень, звучить нудно: несіть в AI-розробку більше інженерних практик, а не менше. Штучний інтелект їх не скасував, він зробив їх обов’язковими.
Немає коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів