Головна / Статті / 30 днів ШІ-розробки без рук: п’ять архітектурних пасток у справжньому SaaS

30 днів ШІ-розробки без рук: п’ять архітектурних пасток у справжньому SaaS

Місяць, коли весь код production-мікро-SaaS писав ШІ, оголив дрейф архітектури, неправильну обробку вебхуків, нескінченне налагодження, роздування і проблему джунів.

1507 слів

Кінець 2026 року приніс звичні вигуки: програмна інженерія мертва, увесь код тепер пише ШІ, звичайна мова — єдиний синтаксис, який потрібен. Щоб зрозуміти, де закінчується хайп і починається продакшн, 30-денний експеримент запровадив одне суворе правило: жодного рядка коду застосунку вручну. ШІ тут не був помічником під час набору чи автодоповненням. Людина виконувала роль архітектурного наглядача й рев’юера: задавала схеми, писала вимоги, читала диффи й схвалювала зміни, а модель писала логіку, запити та інтеграції.

Проєкт не був списком справ. Це справжній мікро-SaaS: вхід, міграції, дашборди користувачів, вебхуки Stripe і публічні API з обмеженням частоти запитів. Він працює, розгорнутий і обслуговує живий трафік. Перші 40% розробки здавалися чарами. Потім репозиторій перевалив за 15 000 рядків, з’явився розподілений стан, і ідея «ШІ пише все» вперлася в жорсткі інженерні межі. Було зафіксовано сім типів збоїв. Нижче п’ять із них і уроки, яких вони вчать.

Перші дні справді швидкі

Спершу чесно: підняти каркас нового проєкту з нуля — задача практично розв’язана.

  • Сучасний full-stack на Next.js, Tailwind, PostgreSQL, Drizzle ORM з middleware авторизації та конфігами Docker зайняв пів дня.
  • Одноманітні схеми Zod, хелпери валідації, таблиці з сортуванням і пагінація з’являлися без зусиль, а передбачувані CRUD-ендпоінти виходили чистими, без жодної помилки в наборі.
  • Коли в одного окремого запиту виявився поганий план виконання, модель швидко помітила зовнішній ключ без індексу й запропонувала точну міграцію з індексом.

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

Пастка 1: локальний фокус, глобальний дрейф

Найпідступніша слабкість — локальна короткозорість. Модель з дивовижною точністю оцінює файл чи функцію у своєму контекстному вікні, але не бачить домовленостей, заданих трьома каталогами далі.

Architectural Drift Anti-Pattern
┌─────────────────────────────────────────────────────────────┐
│ Core Utilities (/src/lib/utils/format.ts):                  │
│ └── export const formatCurrency = (cents: number) => ...    │
└─────────────────────────────────────────────────────────────┘
                              ▲
                 (The AI ignores this file)
                              │
┌─────────────────────────────────────────────────────────────┐
│ New Billing Module (/src/modules/billing/invoice.ts):       │
│ └── export const formatPriceUSD = (amount: number) => ...   │
│     (Re-implements logic with different rounding edge cases)│
└─────────────────────────────────────────────────────────────┘

За два тижні агент написав три трохи різні хелпери форматування валюти в окремих модулях, заново реалізував вбудовану перевірку сесії всередині нового обробника маршруту замість наявного конвеєра middleware і оголосив два суперечливі інтерфейси UserProfile у різних пакетах, які збігалися на 90% полів, але по-різному серіалізували дати.

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

Пастка 2: правдоподібний, але хибний код

Найнебезпечніші баги в коді від ШІ — не падіння й не синтаксичні помилки: перші ловить компілятор, другі лінтер. Страшні ті, що добре читаються, гладко проходять локальні тести й тихо ламають транзакційні гарантії. Ось реальний шаблон, який ШІ видав для вебхука оплати Stripe:

// The AI's Initial Webhook Handler (Dangerous Anti-Pattern)
export async function handleStripeWebhook(req: Request) {
  const event = await parseStripeEvent(req);
  if (event.type === 'checkout.session.completed') {
    // 1. Immediately acknowledge event to Stripe
    sendImmediate200Response();     // 2. Perform async database update in unhandled background promise
    db.users.update({
      where: { customerId: event.data.object.customer },
      data: { isSubscribed: true }
    }).catch(err => logError(err));
  }
}

На машині розробника з тестовими даними це проходить усі перевірки. Код одразу підтверджує подію, повертає 200 OK і оновлює локальну базу SQLite. У продакшні під навантаженням це бомба уповільненої дії:

  • Якщо база зловить короткочасний тайм-аут з’єднання або дедлок усередині цього фонового промісу без await, помилка просто зникне.
  • З картки клієнта гроші списано, Stripe вважає вебхук доставленим, а акаунт так і не оновлюється.
  • Оскільки повернувся 200, Stripe ніколи не повторить спробу, і транзакцію втрачено назавжди.

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

Пастка 3: нескінченне самоналагодження

Коли баг залежав від стану, який змінювався поза відкритим файлом, прохання до ШІ полагодити власний код запускало виснажливий цикл:

The Debugging Despair Loop
┌────────────────────────────────────────────────────────┐
│ 1. Bug Identified: State race condition in worker      │
└──────────────────────────┬─────────────────────────────┘
                           │
                           ▼
  [Prompt AI to Fix] ──► Confidently rewrites worker.ts
                           │
                           ▼
  [Run Tests] ─────────► Bug disappears; introduces memory leak
                           │
                           ▼
  [Point out Leak] ────► Confidently rewrites worker.ts again
                           │
                           ▼
  [Run Tests] ─────────► Re-introduces the original race condition!

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

Пастка 4: нездатність сказати «ні»

Добра інженерія залежить від того, що ви відмовляєтеся будувати, не менше, ніж від того, що випускаєте. Отримавши завдання спроєктувати, наприклад, екран налаштувань акаунта, модель за замовчуванням роздувала все без міри. Вона видала 14 налаштовуваних перемикачів, химерні матриці вподобань сповіщень і вкладені вкладки, про які не просив жоден користувач. Попросіть обробку помилок — і вона загорне кожну внутрішню функцію в загальні блоки try/catch, які ковтають початковий виняток і повертають "Something went wrong", знищуючи спостережуваність.

Модель не має продуктового чуття, і більше коду для неї означає більше цінності. Тримати інтерфейси компактними, вирізати зайві абстракції й зберігати читабельні стек-трейси — досі людські навички.

Пастка 5: останні 10% з’їдають увесь час

Розробка підкоряється суворому емпіричному правилу: приблизно перші 80% функцій з’їдають 20% графіка, а останні 20% — решту 80%. ШІ повністю автоматизує першу частину. Каркас інтерфейсу, REST-ендпоінти й стандартні форми зайняли менше тижня. Решта три тижні пішли на брудну роботу реального світу:

  • Мережевий джитер і повтори з експоненційною затримкою
  • Ідемпотентні записи в базу через розподілені вебхуки
  • Стани відновлення для граничних випадків, наприклад коли користувач тисне «Оновити» посеред OAuth-колбеку
  • Витоки пам’яті в довготривалих фонових воркерах

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

Парадокс джуна

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

The Knowledge Pipeline Dilemma
Traditional Career Path:
[Junior: Scaffolding / CRUD] ──► [Mid-Level: Debugging / APIs] ──► [Senior: System Design]
                                                                        ▲
Today's Reality:                                                        │
[AUTOMATED BY AI]            ──► [AUTOMATED BY AI]            ──► [Senior Judgment Required]
                                                                        │
Question: Where do tomorrow's senior engineers build their intuition? ──┘

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

Що змінилося в процесі

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

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

Машина пише код швидше, ніж будь-яка людина в історії. І все ж код — це зобов’язання, а актив — система, яку він утворює.