Часть 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.
Выбор фреймворка в агентную эпоху это не про «меньше кода» и не про «дешевле токены». Это про ответ на один вопрос: на чём будут держаться правила вашего приложения — на конвенциях и компиляторе или на памяти агента, которой не существует? Другими словами: как заставить агента переиспользовать инженерные решения и получать воспроизводимый результат после каждой итерации, экономя ваше время на ревью и фикс скилл‑паков.
Комментарии открыты. Если ваш агент стабильно выдаёт на голом стеке корректную ролевую модель без единого вмешательства, расскажите, как. Методология выше, задача в спойлере, воспроизводите. А пощупать сам фреймворк перед спором можно тут: сайт, документация, исходники.

