Введение: Эпоха Vibe Coding и иллюзия готовности
Привет. Я последние два года плотно сижу на vibe coding — когда ты накидываешь промпт, AI генерирует тебе код, и ты просто шлёпаешь его в прод. Звучит как мечта. Особенно для соло-фаундеров или небольших команд, где нет времени писать каждый эндпоинт руками. Но есть один нюанс, который я понял на своей шкуре, когда мой AI-ассистент за вечер наваял мне микросервис на FastAPI.
Он запустился. uvicorn main:app --reload — и зелёная галочка в терминале. Сервер слушает порт. Я радостно почесал репу и подумал: «Ну всё, готово, заливаем». И вот тут начинается самое интересное. То, что бэкенд просто запускается — это не фича. Это и есть главная проблема.
В этой статье я разберу, почему AI-сгенерированный код, который «просто работает» на localhost, чаще всего является бомбой замедленного действия в продакшене. И главное — как это исправить, чтобы не проснуться в 3 часа ночи от того, что БД упала, а API отвечает 500-ми ошибками.
1. Почему AI генерирует «красивый» код, который не выдерживает нагрузки
Давайте сразу к делу. Я дал ChatGPT-4o (на июнь 2026 это уже стандарт) задачу: «Напиши бэкенд для приложения заметок с регистрацией, JWT-авторизацией и CRUD». Через 30 секунд я получил:
- main.py с FastAPI
- models.py с SQLAlchemy
- auth.py с bcrypt
- schemas.py с Pydantic
Всё чисто, читаемо. Запускается с первого раза. Но давайте посмотрим, что под капотом.
Проблема №1: Отсутствие обработки ошибок
AI часто генерирует код, который предполагает идеальный сценарий. Вот пример типичного эндпоинта от AI:
@app.get("/notes/{note_id}")
async def get_note(note_id: int, db: Session = Depends(get_db)):
note = db.query(Note).filter(Note.id == note_id).first()
return note
Выглядит логично? А теперь представьте, что:
- note_id — строка, а не int (AI не добавил валидацию)
- Заметка не найдена (вернётся None, а не 404)
- База данных временно недоступна (исключение упадёт в 500)
На localhost вы этого не увидите. Вы передадите правильный ID, БД жива — всё ок. Но в реальном мире клиенты шлют что угодно.
Проблема №2: Нет таймаутов и ретраев
AI сгенерирует вызов внешнего API без таймаута:
async def fetch_user_data(user_id: int):
async with aiohttp.ClientSession() as session:
async with session.get(f"https://api.example.com/users/{user_id}") as resp:
return await resp.json()
Всё красиво. Пока внешний сервис не ответит 30 секунд. Ваш эндпоинт зависнет. Потом второй. Потом третий. И вы получите cascading failure — все воркеры заняты, сервер не отвечает.
2. Что реально нужно проверить в AI-сгенерированном бэкенде
Я выработал для себя чек-лист, который прохожу каждый раз, когда AI выдаёт мне код. Он состоит из трёх уровней.
Уровень 1: Базовое выживание (без этого — в прод нельзя)
| Проверка | Что делать |
|---|---|
| Обработка ошибок | Добавить глобальный exception handler, возвращать корректные HTTP-статусы (400, 404, 422, 500) |
| Валидация входных данных | Проверить, что Pydantic схемы покрывают все поля с типами и ограничениями |
| Таймауты | Везде, где есть внешние вызовы — asyncio.timeout() или aiohttp.ClientTimeout |
| Логирование | Добавить structlog или loguru — AI обычно пишет print() или забывает логи |
Уровень 2: Продакшен-режим
- Health check endpoint:
/healthдолжен проверять соединение с БД, Redis, внешними API. Без этого оркестратор (Kubernetes, Docker Compose) не поймёт, жив ли сервис. - Graceful shutdown: AI не добавляет
app.add_event_handler("shutdown", ...). Без этого при рестарте контейнера вы убьёте активные соединения. - Rate limiting: Добавить
slowapiили middleware. Иначе один бот положит ваш бэкенд за минуту.
Уровень 3: Архитектурные косяки
AI часто генерирует монолитную структуру с одной таблицей в БД. Для MVP это ок. Но если вы планируете масштабироваться — нужно сразу закладывать:
- Разделение на слои (routes → services → repositories)
- Миграции БД (Alembic)
- Кэширование (Redis для частых запросов)
3. Практический пример: От AI-кода к боевому бэкенду
Давайте возьмём тот самый эндпоинт для заметок и превратим его в нечто, что не стыдно выкатить в прод.
Шаг 1: Добавляем exception handler
from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(HTTPException)
async def http_exception_handler(request, exc):
return JSONResponse(
status_code=exc.status_code,
content={"detail": exc.detail, "error_code": exc.status_code}
)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
return JSONResponse(
status_code=422,
content={"detail": exc.errors(), "error_code": 422}
)
Шаг 2: Добавляем таймаут на внешние вызовы
from asyncio import timeout
async def fetch_user_data(user_id: int):
async with timeout(5.0): # максимум 5 секунд
async with aiohttp.ClientSession() as session:
async with session.get(f"https://api.example.com/users/{user_id}") as resp:
return await resp.json()
Шаг 3: Health check с проверкой БД
from sqlalchemy import text
@app.get("/health")
async def health_check(db: Session = Depends(get_db)):
try:
db.execute(text("SELECT 1"))
return {"status": "healthy", "database": "connected"}
except Exception as e:
return JSONResponse(
status_code=503,
content={"status": "unhealthy", "database": str(e)}
)
Теперь, если БД упадёт, ваш health check вернёт 503, и оркестратор перезапустит контейнер. Вместо того чтобы клиенты получали 500 и думали, что ваш сервис сломан.
4. Как я тестирую AI-генерированный код перед деплоем
У меня есть простой, но эффективный процесс:
- Запускаю AI-генерацию → получаю рабочий прототип
- Прогоняю через чек-лист выше (30-40 минут правок)
- Пишу нагрузочный тест с помощью
locustилиk6
Например, для заметок я запускаю k6 с 50 виртуальными пользователями, которые одновременно создают, читают и удаляют заметки. Если хоть один запрос упал с 5xx — я возвращаюсь к коду.
- Добавляю мониторинг —
prometheus_clientс метриками (latency, error rate, request count)
Только после этого я разрешаю себе деплой. И знаете что? В 80% случаев AI-код проваливает нагрузочное тестирование на первом же запуске. Причина — race conditions, отсутствие транзакций, блокировки на уровне БД.
5. Когда AI-бэкенд — это ок, а когда нет
Давайте честно: AI отлично справляется с:
- Прототипами и MVP
- Внутренними инструментами (админки, дашборды)
- API для небольшого числа пользователей (до 100-200 одновременных запросов)
Но если вы строите:
- Платежный шлюз
- Систему с real-time данными
- Сервис, который должен держать тысячи RPS
…то AI-сгенерированный код — это только первый черновик. Вам придётся переписать его руками или хотя бы серьёзно доработать.
Заключение: Не верьте зелёной галочке в терминале
Я не призываю отказаться от AI. Наоборот — я за то, чтобы использовать его как супер-ускоритель. Но нужно понимать: AI генерирует код, который «проходит компиляцию», а не код, который «выдерживает продакшен».
Мой совет: после того как AI написал вам бэкенд, потратьте час на его «закалку». Добавьте обработку ошибок, таймауты, health check, логирование. Это сэкономит вам ночи без сна и репутацию перед клиентами.
И помните: ваш AI задеплоил бэкенд, который просто запускается. Это не победа. Это начало работы.
Пишите в комментариях, с какими багами AI-кода вы сталкивались в реальных проектах. Интересно собрать коллекцию самых эпичных падений.
Комментарии