Часть 2 из 2. В первой части был холивар про самодельные фреймворки. Здесь — цифры: мы дали ИИ‑агенту построить одно и то же приложение на Джеймиксе и на голом Spring и честно сравнили результаты.

О чём эта часть

В первой статье я утверждал: строя на голом Spring, вы уже пишете свой Джеймикс — вручную и плохо. Комментаторы возражали: не всё из коробки нужно в проде. Хорошо. Проверим на агенте: когда код пишет не человек, а ИИ — помогает ему фреймворк или мешает?

Мы ждали простого ответа: «меньше кода» или «дешевле токены». Ответ оказался другим, и он интереснее.

Как устроен эксперимент

Один агент (модель GLM-5.2 в Codex CLI), одна задача, два стека.

Задача. Мини‑конвейер кредитных заявок: четыре сущности, CRUD‑экраны, две роли с разными правами (менеджер и офицер), детерминированный скоринг по формуле, локализация, данные переживают перезапуск. Задача заморожена и байт‑в-байт одинакова для обоих стеков. Полный текст — в спойлере ниже.

Два стека — старались сделать максимально сопоставимыми.

Голый Spring

Джеймикс

Основа

Spring Boot 3.5.16 (Spring Initializr)

Jmix 2.8.2 Full‑Stack (визард Studio, дефолты)

Стартеры / аддоны

web, data‑jpa, security, validation, Liquibase, HSQLDB

Базовый Full‑Stack‑набор (data, security + UI‑безопасность, Flow UI, datatools); маркетплейс‑аддонов — ноль

Spring под капотом

Boot 3.5.16

Boot 3.5.14 (привозит Jmix)

Java

21

21

Сборщик

Gradle 8.14.5

Gradle 8.14.4

БД и миграции

HSQLDB (файловая) + Liquibase

HSQLDB (файловая) + Liquibase

Инструкции агенту

общий AGENTS.md, без подсказок про фреймворк и домен

AGENTS.md + скилл‑пак: 20 инструкций, как правильно делать сущности, экраны, роли и миграции именно в Джеймиксе

Всё остальное одинаково: модель, окружение, база, задача. По три зачётных прогона на каждый стек (всего с пилотами и калибровкой — тринадцать). Весь бенчмарк обошёлся примерно в 45 долларов.

Приёмка. Каждый прогон проверялся по фиксированному чек‑листу плюс скрытым тестом: 12 замороженных сценариев скоринга прогонялись через реальный сервис приложения. Замечания агенту выдавались пачкой, после исправления — повторная проверка. Важное правило: права ролей проверяем, заходя под каждой ролью отдельно, а не под админом. Почему это важно — увидите ниже.

Чек‑лист приёмки (7 пунктов, одинаков для обоих стеков)
  • Приложение собирается и стартует; схема БД применяется автоматически; данные переживают перезапуск.

  • CRUD по всем четырём сущностям через интерфейс; редактирование любой записи сохраняет обновление, а не создаёт дубликат.

  • Каждая смена статуса заявки оставляет запись в истории статусов.

  • Скоринг совпадает с эталоном на всех 12 скрытых сценариях.

  • Права ролей: недоступное действие и блокируется на сервере, И не предлагается в интерфейсе. Проверяется входом под каждой ролью; вход и выход работают.

  • Подписи интерфейса на месте, единый язык.

  • Ошибки показываются в понятном виде — без стектрейсов и сырого JSON.

Прогон засчитан, когда все семь пунктов зелёные. Дополнительно фиксировались (но не требовались) метрики: написанные агентом тесты, объём кода, токены, деньги, вмешательства.

Полный текст задачи (TASK.md)
# Задача: мини-конвейер кредитных заявок

Построй веб-приложение с базой данных для обработки кредитных заявок. Требования — по поведению; технологии выбирай согласно настройкам проекта.

## Сущности

1. **Клиент**: ФИО, дата рождения, ежемесячный доход, ежемесячные платежи по текущим долгам, стаж работы (месяцев).

2. **Кредитный продукт**: название, минимальная сумма, максимальная сумма, годовая ставка (%), максимальный срок (месяцев), порог скоринга.

3. **Заявка**: клиент, продукт, запрашиваемая сумма, срок (месяцев), статус, балл, решение, дата создания. Статусы: NEW, SCORING, APPROVED, MANUAL_REVIEW, REJECTED.

4. **История статусов заявки**: заявка, статус «из», статус «в», время, комментарий. Создаётся при каждой смене статуса заявки.

## Экраны

CRUD (список + создание/редактирование) для всех сущностей. Запуск скоринга по заявке из её экрана.

## Скоринг

1. Ежемесячный аннуитетный платёж: `P*r*(1+r)^n/((1+r)^n-1)`, где P — сумма, r = годовая ставка/12/100, n — срок в месяцах; при r=0 платёж = P/n.

2. DTI = (платежи по текущим долгам + новый платёж) / доход.

3. Knock-out → решение REJECTED (балл всё равно считается): возраст <21 или >70; DTI>0.60; сумма вне [min,max] продукта; срок > макс. срока продукта.

4. Балл 0-100 = взвешенная сумма (границы диапазонов: нижняя включительно, верхняя исключительно; округление half-up):

   — DTI (вес 0.40): `clamp(100*(0.60-DTI)/0.40, 0, 100)`.

   — Доход (0.20): ≥150000→100; ≥80000→70; ≥40000→40; иначе 10.

   — Стаж мес. (0.15): ≥36→100; ≥12→60; ≥3→30; иначе 0.

   — Возраст (0.10): [30,55)→100; [25,30)∪[55,65)→70; [21,25)∪[65,70]→40.

   — Сумма/лимит (0.15): ratio=сумма/макс.сумма; ≤0.30→100; ≤0.70→70; иначе 40.

5. Решение: балл ≥70→APPROVED; 50-69→MANUAL_REVIEW; <50→REJECTED; knock-out→REJECTED.

## Роли

— **Кредитный менеджер**: CRUD заявок и клиентов, запуск скоринга. Не может редактировать продукты и не может подтверждать заявки в MANUAL_REVIEW.

— **Кредитный офицер**: подтверждение/отклонение заявок в MANUAL_REVIEW, управление продуктами.

## Прочее

— На первом старте схема применяется автоматически; созданные данные **сохраняются между перезапусками** приложения.

— Подписи интерфейса локализованы (единый язык).

— Действие, недоступное текущей роли, **не предлагается в интерфейсе** (кнопка/пункт скрыты или заблокированы), а не только отклоняется на сервере. Вход и выход из системы работают.

— Редактирование существующей записи открывает её данные и сохраняет **обновление** (не создаёт дубликат) — для любой записи, включая первую в списке.

— Ошибки (отказ в доступе, валидация и т.п.) показываются пользователю в понятном виде, без сырых технических ответов.
Детали стенда: провайдер, цены, как чинили грабли

Провайдер зафиксирован. OpenRouter по умолчанию раскидывает запросы между разными хостерами модели — а у них разные цены, кэш и даже качество (квантизация). Первые прогоны на этом миксе дали «поехавшие» цифры, поэтому для зачёта мы жёстко зафиксировали одного провайдера (z.ai). Цены на момент прогонов: $1.40 за миллион входных токенов, $4.40 за выходные, $0.26 за кэшированный вход.

Кэш решает всё в цене. Доля кэша в прогонах — 97–99%, а кэшированный вход в 5 с лишним раз дешевле обычного. Наивная формула «токены умножить на цену» завышает стоимость в разы. Мы считали с учётом кэша и сверялись с дашбордом провайдера — сходится с точностью до ~6%.

Скилл‑пак дорабатывали по ходу. Первые прогоны на Джеймиксе вскрыли повторяющиеся ошибки агента — все в одном месте: там, где он уходил от идиомы фреймворка в самодельный код. Например, агент заводил роли, но забывал дать им право на вход в интерфейс, и пользователи не могли залогиниться. Мы превратили эти грабли в правила скилл‑пака и перепрогнали Джеймикс‑прогоны на исправленной версии (Spring‑прогоны не трогали, там не было скилов в принципе). Результат честный и поучительный: ошибка входа исчезла во всех прогонах, а вот самодельную проверку ролей агент один раз написал снова вопреки прямому запрету в правилах. Правило, которое фреймворк может принудить механикой, работает всегда; правило‑совет лишь снижает риск.

Один прогон сожгли. Провайдер завис, агент час гонялся за несуществующим багом и пробил лимит токенов ($5 в трубу). Прогон целиком выкинули из зачёта и запустили заново с чистого листа.

Что намерили

Стек

Прогон

Стоимость

Токены

Замечаний

Приёмка

Spring

1

$3.14

10.2M

2

после исправления

2

$2.39

7.9M

1

после исправлений

3

$3.94*

13.3M

3

после исправления

Джеймикс

1

$1.82

6.0M

0

с первого раза

2

$2.58

8.8M

0

с первого раза

3

$2.28

7.7M

1

после исправления

* третий Spring‑прогон единственным за всю серию вышел за наш лимит в 12 млн токенов — как раз на раунде исправления трёх дефектов. Мы это не прячем: до исправлений он стоил $2.78, лимит пробил именно ремонт.

Из таблицы — три вывода.

1. По деньгам разницы нет. Джеймикс — $1.82–2.58, Spring — $2.39–3.94. Прогоны на одном и том же стеке отличаются между собой сильнее, чем Джеймикс отличается от Spring. Страх «скилл‑пак раздует расход» не подтвердился: кэш съедает вес инструкций. Цену на самом деле определяют замечания и паузы, а не выбор фреймворка. Говорить «на Джеймиксе дешевле» наши данные не позволяют — и мы не говорим.

2. А вот по самостоятельности разница есть. Вопрос: доходит ли агент до приёмки сам, без человека? На Джеймиксе — два прогона из трёх дошли без единого замечания. На Spring — ни один. Причём Spring спотыкался каждый раз об одно и то же: права доступа. Это не невезение, это свойство стека, почему, разберём ниже.

3. Кода на Spring больше и он менее предсказуем. Посчитали по всем шести прогонам (только код, написанный агентом, диффом от стартового проекта, без тестов). Медиана: 1826 строк на Джеймиксе против 2304 на Spring больше на 26%.

Куда пошли строки: таблица и цена переписываний

Прогон

Java

Фронтенд

Экраны/миграции

Локализация

Всего

Джеймикс 1

1105

0

568

73

1746

Джеймикс 2

1120

0

640

66

1826

Джеймикс 3

1166

0

719

78

1963

Spring 1

1576

601

211

61

2449

Spring 2

1261

524

185

0

1970

Spring 3

1240

858

197

9

2304

Медианы: 1826 против 2304. Средние: 1845 против 2241. Берём медиану — у Spring разброс большой, среднее его сглаживает.

Считали только код, написанный агентом: то, что генерирует визард Джеймикса, исключено, как и вытащенные агентом «посмотреть» исходники фреймворка. И ещё важный нюанс: статичный подсчёт строк не видит переписываний. Конфиг безопасности на Spring агент переписывал до 14 раз за прогон, главную страницу по 6–10 раз. На Джеймиксе в самом чистом прогоне крутились два файла по 3–4 раза. Строка, написанная один раз, и строка, переписанная четырнадцать, стоят по‑разному.

Скоринг: ничья. Формулы посчитались правильно на обоих стеках: скрытый тест 12 из 12 везде. Чистая бизнес‑логика агенту одинаково по силам что там, что там. Запомните этот факт, он ещё выстрелит.

Что нашли

У агента нет памяти о стеке, и вопрос лишь в том, где это всплывёт

Мы вЫчитали все рассуждения агента по всем прогонам. Общее место: агент не «помнит» ваш стек, он угадывает и проверяет. Этот налог платят оба подхода, но в разных местах.

Spring платит в рантайме. Каждый прогон агент заново наступал на одни и те же грабли: первая запись получает id=0 (и начинается перебор стратегий генерации ключей), ленивая загрузка JPA взрывается на границе REST, процесс приложения умирает в фоне, статические файлы блокируются конфигом безопасности. Всё это выясняется только при запуске, а это дорого и каждый раз заново.

Джеймикс платит на компиляции. Агент так же уверенно угадывает — но неправильный импорт из Vaadin просто не собирается. Ошибка ловится за секунды, до всякого запуска. Компилятор и конвенции фреймворка работают как ограждение: дорогой рантайм‑баг превращается в дешёвый цикл «не собралось — поправил».

Права доступа — ахиллесова пята голого стека

Spring спотыкался на правах в каждом из трёх прогонов. Причина структурная. Безопасность в Spring настраивается URL‑паттернами, и единого источника правды просто нет: часть эндпоинтов неизбежно уезжает под общее правило «пускать всех залогиненных», и агент забывает сузить их до роли. В одном прогоне офицер мог создавать и удалять клиентов — а по заданию имел право только подтверждать заявки. Мы проверили запросами: сервер отвечал 200 там, где должен был отказать. Кнопки в интерфейсе при этом никто не прятал.

На Джеймиксе этот класс ошибок закрыт устройством самого фреймворка: одна декларативная политика управляет и сервером, и видимостью кнопок. Забыть «половину» просто негде.

И как чинится — тоже показательно. Spring‑агент чинит переписыванием: «перепишу конфиг безопасности начисто», «перепишу фронтенд» — до 14 заходов на один файл, один из рерайтов заодно снёс миграцию с данными. Джеймикс‑агент чинит точечно: одна аннотация, сужение одной политики. Размер исправления не растёт с размером приложения.

Честности ради: Джеймикс не броня. Когда агент уходил от идиомы фреймворка в самодельный код (сам писал проверку роли вместо декларации), ошибки возвращались и туда. Фреймворк исключает класс багов ровно до тех пор, пока агент держится его рельсов.

Агент не видит собственных ролевых багов. Вообще

Самая неприятная находка — общая для обоих стеков. Агент завершал каждый прогон уверенным «права настроены, всё работает». Проверяем под обычным пользователем — не работает. Его собственные проверки структурно слепы: тесты гоняются под админом с полным доступом, которому всё можно, и они физически не могут увидеть, что менеджер не может войти, а офицеру доступно лишнее.

Отсюда практическое правило, которое мы вносим в любой процесс с агентами: проверяйте права под каждой ролью отдельно, никогда только под привилегированной. Ровно эта проверка и поймала единственный дефект Джеймикс‑прогонов.

А написал ли агент свой Джеймикс?

Обещанный ответ. Он двоякий и это, пожалуй, самый честный вывод бенчмарка.

Да, написал каркас. На голом Spring агент вручную собрал ровно те слои, которые Джеймикс даёт из коробки: свою подсистему пользователей и ролей, CRUD‑эндпоинты, админку на полтысячи строк JS, слой DTO. По форме — самодельный Джеймикс. Тезис первой статьи подтвердился, только теперь фреймворк пишет не человек годами, а агент за прогон.

Нет, не написал рельсы. Агент воспроизводит структуру, но не гарантии, которые делают её безопасной: его самодельная безопасность течёт каждый прогон, его админка переписывается по десять раз, его грабли не превращаются в опыт. И главное — его каркас одноразовый: следующий прогон изобретает всё заново, с теми же дырами. Человеческий самопал хотя бы накапливается; агентский же выбрасывается и переизобретается.

Готовый фреймворк даёт агенту то, чего тот не может дать себе сам: правила, которые держатся без его памяти.

Неудобный вопрос: а нужен ли тут агент вообще?

Любой, кто работал с Jmix Studio, спросит: модель данных, экраны и роли там накликиваются визардами за десятки минут. Зачем жечь на это агента?

Вопрос болезненно точный, и наши данные его обостряют: все ошибки Джеймикс‑прогонов пришлись ровно на то, что делается визардами (роли, экраны), а в логике, там где визарды бессильны, ошибок не было ни у кого. Гибрид «человек кликает визарды, агент пишет только логику» на нашей задаче дал бы примерно в десять раз меньше затрат по токенам и почти ноль дефектов.

Но и это не приговор агенту. Токены стоят копейки (напомню: весь бенчмарк на 14 ранов съёл $45). Дорогой ресурс — время человека, и гибрид его тратит больше: час ручной работы против двадцати минут присмотра. Один проект — выгодно руками; поток проектов — выгоднее агент. Он масштабируется параллельными запусками, а руки нет. Плюс гибрид требует уметь в Джеймикс, а агентный сценарий интересен как раз тем, кто пока не умеет.

Мы этот гибридный режим не замеряли, это честная прикидка и кандидат на следующий бенчмарк.

Что могло исказить картину

  • Три прогона на стек и один оператор это сконее направленный результат, не статистика. Выводы достаточно качественные качественные.

  • Старт не равный: проект Джеймикса из коробки включает готовую подсистему безопасности, Spring нет. Часть преимущества заслуга стартового проекта, а не скилл‑пака.

  • Мы сравниваем «фреймворк + скилл‑пак» против «голый стек», а не скилл‑пак в вакууме, и его невозможно отделить от фреймворка. Кстати, отдельный контрольный запуск (Spring + общедоступный скилл‑пак по Spring) показал: хорошие инструкции закрывают дыры безопасности и на голом стеке, знание можно принести и туда, вопрос лишь в том, что фреймворк приносит его из коробки.

  • Замеры делались в разное время и частично в разных условиях среды, точные доли процентов между стеками мы не абсолютизируем.

Вместо заключения

В первой части я обещал не размахивать холиварным тезисом, а положить на стол цифры. Кладу.

  • Деньги: ничья. Дешевле не стало и дороже не стало: $1.8–2.6 за прогон там и там.

  • Самостоятельность разница есть: Джеймикс‑агент дважды дошёл до приёмки сам, Spring‑агент ни разу.

  • Классы багов — главное. Голый стек наступает на одну и ту же дыру в правах каждый прогон и чинит её переписыванием всего конфига. Фреймворк закрывает эту дыру устройством, пока агент держится его правил, конечно.

  • И да, агент написал свой Джеймикс — каркас без гарантий, одноразовый, с одинаковыми дырами в каждой реинкарнации.

Тезис первой статьи бенчмарк не опроверг, а расширил: да, ваш агент напишет свой Джеймикс. Тоже плохой. Каждый раз заново. Если вы Java разработчик, то разобраться с новым стеком (JS в этом случае) и провести ревью вам будет сложнее, или вообще невозможно. В Джеймиксе же фронт на Java(VAADIN) + простой для понимания декларативный XML.

Выбор фреймворка в агентную эпоху это не про «меньше кода» и не про «дешевле токены». Это про ответ на один вопрос: на чём будут держаться правила вашего приложения — на конвенциях и компиляторе или на памяти агента, которой не существует? Другими словами: как заставить агента переиспользовать инженерные решения и получать воспроизводимый результат после каждой итерации, экономя ваше время на ревью и фикс скилл‑паков.

Комментарии открыты. Если ваш агент стабильно выдаёт на голом стеке корректную ролевую модель без единого вмешательства, расскажите, как. Методология выше, задача в спойлере, воспроизводите. А пощупать сам фреймворк перед спором можно тут: сайтдокументацияисходники.