Мої помилки з AI-агентами: як я пройшов шлях від vibe coding до контрольованої розробки

12 хвилин читання

Привіт! Мене звати Вадим Мирошник, я Lead Software Development Engineer у SPD Technology. Сьогодні поділюся своїми факапами в роботі з AI-агентами й тим, як я пройшов шлях від vibe coding до контрольованішої розробки. Щоб отримувати якісний код від AI, замало написати ідеальний промпт. Контроль треба вбудовувати в сам проєкт.

Кілька років я використовую власні pet-проєкти як полігон для експериментів з AI-агентами: пробую нові підходи й одразу бачу їхні наслідки в реальному коді, разом з усіма помилками. Далі розкажу про свої помилки й те, чого кожна з них мене навчила. Якщо коротко, з часом я почав зміщувати контроль на дедалі ранніші етапи: спочатку на етап написання коду, потім – у code review, а згодом – на етап архітектури/дизайну/планування. 

Як темна тема показала ціну швидкого старту

Одна з моїх помилок стала очевидною, коли я вирішив додати в проєкт темну тему.

Читайте також: Ще два роки тому вміння працювати з нейромережами вважалося приємним бонусом у резюме. Зараз рекрутери відсіюють кандидатів, які не можуть пояснити, як вони застосовують AI у щоденних задачах. Ринок перевернувся швидше, ніж більшість встигла оновити свій набір навичок. І це вже не про майбутнє, а про те, що відбувається на співбесідах прямо тепер.

Спочатку там була лише світла тема, тому hard-coded кольори в компонентах майже не створювали проблем. Але щойно я взявся за темну, виявилося, що просто так це не зробити: кольори були розкидані по різних компонентах, а не зберігалися в одному місці. За час розробки AI додавав їх по-різному – десь через hex або RGB, а десь просто вибирав трохи інший відтінок.

Тож довелося пройтися по всіх сторінках і компонентах, знайти hard-coded кольори та перенести їх у спільну тему. Я робив це за допомогою різних AI-агентів: склав план міграції, розділив роботу на етапи й кілька разів перевіряв результат.

Після першого проходу на деяких сторінках усе одно залишалися старі світлі кольори. Агент виправляв знайдені ним місця, але не мав перевірки, яка б доводила, що він знайшов геть усі. З кожним проходом таких місць ставало менше, але й помітити їх візуально було дедалі складніше. Тому довелося перевіряти вже не сторінки, а сам код.

У підсумку я додав кастомний аналізатор, який знаходить hard-coded кольори в компонентах. Поки такі місця залишаються, задача повертається агенту на доопрацювання. До цього я описував правило використовувати кольори зі спільної теми прямо в промпті – але в самому проєкті не було перевірки, яка знаходила б порушення.

Зараз кольори зберігаються централізовано, а новий код проходить автоматичну перевірку на hard-coded значення. 

Якщо в проєкті є правило, яке AI не повинен порушувати, я більше не залишаю його лише в промпті.

Я перестав покладатися на правила в промпті

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

Один агент міг чітко дотримуватися домовленостей. Інший у схожій задачі вже робив трохи інакше. Бувало, що агент генерував компонент на сотні рядків і залишав у ньому і UI, і логіку, хоча я хотів, щоб код був розділений на зрозумілі частини.

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

Якщо я хочу однакове форматування, немає сенсу щоразу пояснювати це AI – для цього є Prettier. Проблеми в коді ловить ESLint, типізацію перевіряє TypeScript. Окремо можна контролювати структуру проєкту – наприклад, які частини можуть напряму залежати одна від одної. А для правил, специфічних саме для мого проєкту, я почав додавати власні перевірки. Як у випадку з hard-coded кольорами з першої частини.

Ідея проста: щоб не пояснювати AI щоразу, як саме писати код, я переношу важливі правила в сам codebase.

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

Підписуйтеся на наші соцмережі

Промпт для мене залишився способом пояснити агенту, що я хочу отримати.

Усі перевірки пройшли, а баг залишився

Після перших двох факапів мені вже здавалося, що процес став досить контрольованим.

Мій pet-проєкт – навчальна платформа з вправами та квізами, яку я розробляю за допомогою AI-агентів. У проєкті були правила, автоматичні перевірки, тести та власні валідатори. Якщо агент порушував правило, завдання поверталося йому на доопрацювання. Build був зелений, lint проходив, типізація, тести й валідатори – теж.

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

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

Ця логіка з’явилася не за один раз. Одну перевірку агент додав під час реалізації одного сценарію, іншу — пізніше, коли вже працював над іншою частиною квізу. Кожна частина працювала окремо і була покрита тестами. Але тесту, який перевіряв би обидва сценарії разом і гарантував єдиний прохідний бал, у мене не було. Тому всі перевірки проходили, хоча в системі вже була суперечність.

У цьому випадку проблему помітив я сам. І саме тоді побачив межу автоматичних перевірок: вони ловлять лише ті правила і сценарії, які я вже встиг формалізувати.

Після цього я почав відокремлювати review від реалізації. Спочатку я просив того самого агента, який писав код, перевірити свою роботу. Але якщо він уже зробив неправильне припущення під час реалізації, є шанс, що під час review він його теж не помітить. Тому зараз зміни переглядає окрема сесія або інший агент.

У review є свої правила: перевірити логіку зміни, подивитися на пов’язані частини коду та сценарії, які могли бути пропущені. І на наступних завданнях reviewer уже почав знаходити схожі проблеми, які implementation agent не помічав під час реалізації. Якщо reviewer виявляє проблему, завдання повертається implementation agent на доопрацювання. А якщо таку проблему можна перевіряти автоматично, разом із виправленням додається тест або окрема перевірка, щоб вона більше не повторилася.

З прохідним балом я зробив так само: після того, як помітив проблему, агент виніс логіку перевірки в одне місце, а інші компоненти почали використовувати її звідти.

Для мене зелений build, тести й валідатори тепер ще не означають, що завдання завершене. Вони перевіряють те, що я вже формалізував. А review допомагає знайти те, що я ще не врахував.

Я почав контролювати AI ще до того, як він написав код

У попередніх частинах я писав про те, як додав правила, валідатори, автоматичні перевірки та окремий review. Але весь цей контроль починав працювати вже на етапі реалізації або після нього. Те, що відбувалося до реалізації, я майже не контролював.

А потім я спробував Spec-Driven Development (SDD). Я чув про нього давно, але довгий час сприймав скоріше як додаткову документацію. У pet-проєкті я працював простіше: описував агенту фічу своїми словами, додавав кілька важливих деталей і давав йому писати код. Для невеликих змін цього часто було достатньо.

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

Один із прикладів — система оплати. Раніше я міг би просто написати агенту щось на кшталт: «Додай Stripe, сторінку оплати, купони й доступ після покупки». Технічно цього вже достатньо, щоб почати писати код.

Але коли я спробував підійти до цієї задачі через SDD, до реалізації ми перейшли не одразу. Спочатку потрібно було описати, як саме має працювати покупка: скільки триває доступ, що відбувається після успішної оплати, як працюють купони та повернення коштів.

І тут почали з’являтися питання, яких у моєму початковому описі не було:

  • Що буде, якщо користувач закрив браузер під час оплати й повернувся пізніше?
  • Що робити, якщо Stripe повторно надішле підтвердження платежу?
  • Коли саме починається період доступу?
  • Що відбувається з доступом після повернення коштів?
  • Як має поводитися система під час повторної покупки?

Окремо довелося подумати про виставлення рахунків, GDPR та інші юридичні вимоги.

Частину цих сценаріїв я міг би згадати й сам. Але різниця була в тому, що цей процес змусив пройтися по них до реалізації, а не знаходити вже під час написання коду або review. У результаті ще до реалізації в мене були вимоги, критерії приймання, план і конкретні задачі.

І тут я побачив ще одну корисну сторону роботи з AI. Агент може не лише реалізувати те, що я описав. Він може ще до написання коду поставити питання й допомогти знайти те, чого в моєму описі бракує.

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

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

Тепер намагаюся додати контроль ще на один крок раніше – до того, як AI взагалі почне писати код.

Контроль – це ще й те, що агенту дозволено бачити й робити

Окремо я почав думати про те, який доступ узагалі має AI-агент під час розробки. Якщо агент може читати весь environment, бачити production secrets або без підтвердження виконувати будь-які shell-команди, то якість коду вже не є єдиним ризиком.

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

Особливо це стосується команд, які можуть видаляти файли, змінювати Git-історію, працювати з deployment, cloud infrastructure або іншими зовнішніми системами. Для таких дій я хочу залишати ручне підтвердження.

Тобто контроль AI-агента зараз — це для мене вже не лише lint → tests → review. Це ще й контроль того, що агент може бачити і що йому дозволено робити.

Що я виніс для себе

Зрештою мій підхід звівся до кількох простих принципів:

  • Важливі правила не варто залишати лише в промпті. Якщо їх можна формалізувати в коді, конфігурації, лінтерах або інших автоматичних перевірках — краще зробити це.
  • Те, що можна перевірити автоматично, варто перевіряти автоматично: через тести, статичний аналіз, типізацію та власні валідатори.
  • Навіть якщо всі тести й перевірки пройшли успішно, це ще не означає, що зміна правильна. Вони знаходять лише ті проблеми, для яких уже існують правила або сценарії перевірки, тому окремий review усе одно потрібен.
  • Контроль варто починати ще до написання коду: уточнювати вимоги, продумувати сценарії та обмежувати те, що AI-агент може бачити й робити.

Для мене робота з AI-агентами поступово перестала бути питанням того, наскільки хороший промпт я можу написати. Набагато важливішим виявилося побудувати процес, у якому помилки можна виявити якомога раніше, а критичні правила не залежать від того, чи «згадає» про них агент.