yongol — Киль SaaS, написанного с помощью ИИ Image: AI generated Изображение: сгенерировано ИИ

Если ИИ постоянно перезаписывает ваш код, если вайб-кодинг развалился на 200 эндпоинтах, если вы хотите перенести нагрузку ИИ с кода на спецификации — yongol и есть этот киль.

200-й эндпоинт

Вы строите SaaS с помощью вайб-кодинга. Поначалу быстро. 5 таблиц, 12 эндпоинтов — двадцать минут и работает.

Но после 50 эндпоинтов начинается странное. ИИ создаёт сегодня паттерн, противоречащий вчерашнему. После 100 — существующие функции тихо ломаются. После 200 — добавление одной функции стоит в 10 раз дороже первых десяти.

Отчёт DORA 2025 подтвердил это эмпирически — инструменты ИИ увеличивают пропускную способность на 2-18%, но одновременно повышают частоту сбоев при изменениях и объём переделок[1]. ИИ — это «зеркало и усилитель», который усиливает слабые места существующих процессов.

Не потому что модель глупая.


Решения и реализация

В исходном коде переплетены три вещи:

  • Решения пользователя — эта колонка BIGINT, этот эндпоинт только для владельца, пагинация курсорная.
  • Бизнес-логика — ценообразование, воркфлоу, правила жизненного цикла.
  • Детали реализации — имена переменных, порядок вызовов библиотек, оборачивание ошибок.

Когда ИИ читает этот код, он не может определить, какая строка — решение, а какая — деталь. Поэтому при «рефакторинге» или «очистке» он тихо перезаписывает решения, приняв их за детали. Пользователь замечает, только когда поведение уже нарушено. В 1972 году Парнас сказал: «скройте проектные решения, которые могут измениться, за интерфейсами»[2] — он обращался к людям. Но теперь, когда ИИ редактирует код, если различие между решениями и деталями не существует в самом медиуме, никто — ни человек, ни модель — не сможет его поддерживать.

Вот почему вайб-кодинг рушится на 200 эндпоинтах. Более крупная модель не решит проблему. Медиум — сырой код — просто не сохраняет решения. Каждая модель в конечном итоге упирается в ту же стену.


Киль

Киль — это первая кость, которую кладут при постройке корабля. Он несёт вес корпуса, предотвращает бортовую качку, и все остальные конструкции строятся поверх него. Корабль, построенный без киля, плавает в спокойной воде, но деформируется при волнах.

SaaS, построенный вайб-кодингом, — то же самое. Плавает, пока маленький. Деформируется, когда растёт.

yongol — это киль SaaS, написанного с помощью ИИ.

Harness with reins — не более крупная модель, а более точные поводья. Детерминированный валидатор оценивает каждый артефакт, храповик обеспечивает прогресс, и машина решает, завершена ли работа.


Вынести решения из кода

Суть yongol проста. Отделить решения от кода.

Десять декларативных спецификаций (SSOT) каждая отвечает за одну задачу:

SSOTОтветственность
features.yamlКаталог функций — что строить
manifest.yamlКонфигурация проекта — аутентификация, middleware, инфраструктура
OpenAPIКонтракт API — маршруты, параметры, ответы
SQL DDL + sqlcМодель данных — таблицы, колонки, ограничения, запросы
SSaCПоток сервиса — последовательность решений внутри эндпоинта
RegoАвторизация — кто что может делать
Mermaid stateDiagramПереходы состояний — жизненные циклы сущностей
FuncSpecПользовательские функции — логика, не выражаемая через CRUD
HurlТестовые сценарии — трихотомия smoke, scenario, invariant
STMLФронтенд — Semantic Template Markup Language (HTML на основе атрибутов data-*)

Восемь из десяти — отраслевые стандарты (OpenAPI, SQL, sqlc, Rego, Mermaid, Hurl, YAML). Только SSaC и STML — DSL, созданные yongol. Минимизируется то, что ИИ должен выучить с нуля.

Каждый SSOT содержит только решения. Никаких деталей реализации. ИИ редактирует SSOT; yongol generate рендерит код из них. Решения живут постоянно в SSOT; код — одноразовая проекция.


Обеспечение согласованности

Решения теперь распределены по десяти файлам, поэтому могут возникнуть противоречия. DDL говорит BIGINT, а OpenAPI — string? SSaC объявляет @auth, а в Rego нет соответствующего правила? В диаграмме состояний есть переход, а в SSaC нет соответствующей функции?

Противоречивый SSOT — это испорченное решение. Каким бы чистым ни был код, если решения конфликтуют — поведение нарушается.

yongol validate ловит это.

✓ manifest        ✓ openapi_ddl       ✓ ssac_rego
✓ openapi         ✓ openapi_ssac      ✓ ssac_authz
✓ ddl             ✓ hurl_openapi      ✓ ssac_sqlc
✓ query           ✓ hurl_statemachine ✓ ddl_statemachine
✓ ssac            ✓ hurl_manifest     ✓ ddl_rego
✓ statemachine    ✓ openapi_manifest  ✓ rego_manifest
✓ rego            ✓ ssac_ddl          ✓ stml_openapi
✓ hurl            ✓ ssac_statemachine
✓ funcspec        ✓ ssac_func

0 errors, 0 warnings

Сначала валидирует каждый SSOT отдельно, затем выполняет перекрёстные проверки между слоями. ~287 правил инспектируют каждую символическую ссылку между всеми десятью SSOT. Если существует хотя бы одно противоречие, компиляция отклоняется. Систематический обзор литературы Torres et al.[3] отметил, что большинство инструментов управления моделями обрабатывают только внутримодельную согласованность, оставляя перекрёстную верификацию между гетерогенными моделями нерешённой задачей — yongol validate заполняет именно этот пробел.

ИИ пишет свободно. Сошёл с рельсов — validate мгновенно ловит. Свобода на рельсах.


yongol next — Команда-храповик

Если yongol validate показывает все ошибки сразу, то yongol next показывает ошибки по одной. Это храповик.

$ yongol next specs/

[ERROR] DDL-003: users.id must be BIGINT, got INT
  file: specs/db/users.sql:2
  ▶ Fix this error. Then run `yongol next specs/`.

Единственная инструкция, которая нужна ИИ-агенту, — одно предложение: «Запусти yongol next specs/ и исправляй ошибки, пока не станет 0.»

Исправь ошибку — появится следующая. Пройди все — остановится:

$ yongol next specs/

✓ All validations passed. 0 errors.

Агент не заявляет «я закончил». Машина выносит вердикт «ещё осталось» или «всё пройдено». Агент не имеет полномочий на решение о завершении.


Создание проекта и управление функциями

yongol init

Автоматически генерирует SSOT-скаффолдинг из features.yaml.

yongol init Myapp features.yaml "My workflow automation SaaS"
cd Myapp && yongol validate specs     # 0 errors

Manifest, заглушки operationId OpenAPI, файлы-заглушки SSaC, правила авторизации Rego, smoke-тесты Hurl и конфигурация sqlc генерируются за один раз. Проект сразу стартует в состоянии прохождения yongol validate.

yongol features add / remove

Добавить или удалить функции:

yongol features add new_features.yaml         # Сгенерировать заглушки SSaC для новых operationId
yongol features remove ExportWorkflow --yes    # Удалить operationId + заглушки SSaC

yongol import

Сгенерировать Go-клиентские пакеты из внешних OpenAPI-спецификаций (Stripe, GitHub и др.):

yongol import https://api.stripe.com/openapi.yaml ./external/

Вызывать сгенерированные функции в SSaC через @call <pkg>.<Func>({...}).


operationId — замковый камень

Как связать десять слоёв? Одним идентификатором в PascalCase.

Введите operationId ExecuteWorkflow:

── Feature Chain: ExecuteWorkflow ──

  OpenAPI    api/openapi.yaml                POST /workflows/{id}/execute
  SSaC       service/workflow/execute_workflow.ssac   @get @empty @auth @state @call @publish @response
  DDL        db/workflows.sql                CREATE TABLE workflows
  DDL        db/execution_logs.sql           CREATE TABLE execution_logs
  Rego       policy/authz.rego               resource: workflow
  StateDiag  states/workflow.md              diagram: workflow → ExecuteWorkflow
  FuncSpec   func/billing/check_credits.go   @func billing.CheckCredits
  FuncSpec   func/billing/deduct_credit.go   @func billing.DeductCredit
  FuncSpec   func/worker/process_actions.go  @func worker.ProcessActions
  FuncSpec   func/webhook/deliver.go         @func webhook.Deliver
  Hurl       tests/scenario-happy-path.hurl  scenario: scenario-happy-path.hurl

От спецификации API до схемы базы данных, от политики авторизации до переходов состояний, от реализации функций до тестовых сценариев — полная топология одной функции на одном экране. Десятки grep заменяются одной командой.

operationId — замковый камень, потому что в полностековом приложении единица функции — это API-эндпоинт. Пользователь нажимает кнопку, вызывается API, и этот API пронизывает все остальные слои. Одно имя физически связывает десять слоёв.


SSaC — Почему кастомный DSL

Восемь из 10 SSOT yongol — отраслевые стандарты. Только SSaC (Service Sequences as Code) и STML были созданы yongol. SSaC фиксирует решения потока сервиса.

Пробел, который заполняет SSaC. Посмотрите на спектр декларативных инструментов: на одном конце — стандарты контрактов (OpenAPI, SQL, Rego) — они объявляют что, но не в каком порядке. На другом конце — среды выполнения воркфлоу (Temporal, Inngest, Restate) — это код. Решения и детали реализации рекомбинируются в одном файле. SSaC занимает пробел между ними: «внутри одного эндпоинта, что происходит, в каком порядке, с какими гардами.»

SSaC содержит всего 16 аннотаций. Можно выучить по одностраничному руководству.

Полный список аннотаций SSaC

АннотацияРольФормат
@getЧтение из БДType var = Model.Method({args})
@postСоздание строкиType var = Model.Method({args})
@putОбновление строки (без возврата)Model.Method({args})
@deleteУдаление строкиModel.Method({args})
@emptyГард nil → 404var "message" [STATUS]
@existsГард not-nil → 409var "message" [STATUS]
@authПроверка авторизации"action" "resource" {inputs} "message" [STATUS]
@stateПереход машины состоянийdiagram {inputs} "transition" "message" [STATUS]
@callВызов функции[Type var =] pkg.Func({args})
@evalПредикатный гард (true → ошибка)pkg.Func({args}) "message" STATUS
@publishПубликация в очередь"topic" {payload}
@subscribeТриггерная функция очереди"topic"
@verify-passwordЛогин (timing-safe)Model.col=source Model.hash vs source -> var STATUS "msg"
@responseВозврат JSON{ field: var, ... } или var
@no-paginationОсвобождение от правила пагинации(уровень функции)
@state-neutralОсвобождение от правила машины состояний(уровень функции)

Пример SSaC — AcceptProposal

Авторизация + двойная машина состояний + эскроу + очередь:

package service

import "github.com/org/project/internal/billing"

// @get Proposal p = Proposal.FindByID({ID: request.id})
// @empty p "Proposal not found" 404
// @get Gig gig = Gig.FindByID({ID: p.GigID})
// @empty gig "Gig not found" 404
// @auth "AcceptProposal" "gig" {ResourceID: request.id} "Forbidden" 403
// @state proposal {status: p.Status} "AcceptProposal" "Cannot accept" 409
// @state gig {status: gig.Status} "AcceptProposal" "Cannot accept on gig" 409
// @put Proposal.UpdateStatus({ID: p.ID, Status: "accepted"})
// @put Gig.AssignFreelancer({ID: gig.ID, FreelancerID: p.FreelancerID, Status: "in_progress"})
// @call billing.HoldEscrowResponse escrow = billing.HoldEscrow({GigID: gig.ID, Amount: gig.Budget})
// @publish "proposal.accepted" {GigID: gig.ID, FreelancerID: p.FreelancerID}
// @get Proposal updated = Proposal.FindByID({ID: p.ID})
// @response { proposal: updated }
func AcceptProposal() {}

16 строк. 10 аннотаций. Две машины состояний, авторизация, эскроу, событие очереди, ответ — каждое решение видно, каждая деталь отсутствует.


Бенчмарк: ZenFlow

ZenFlow — мультитенантный SaaS автоматизации воркфлоу.

ЭтапОписаниеВремяНакопительно
Начальная сборка10 эндпоинтов, 6 таблиц, auth, машина состояний13 мин13 мин
+ Версионированиеклонирование воркфлоу, список версий6 мин19 мин
+ Вебхукиwebhook CRUD, бэкенд очереди6 мин25 мин
+ Маркетплейс шаблоновкурсорная пагинация, клонирование между организациями3 мин28 мин
+ Файловые вложенияотчёты выполнения, файловый бэкенд4 мин32 мин
+ Планированиеcron-планирование, бэкенд сессий6 мин38 мин
+ Журналы аудитапагинация по смещению, бэкенд кэша3 мин41 мин
+ Дашбордjoin-ы связей, типы ответов func7 мин48 мин
+ Пакетные операциимассовая вставка jsonb14 мин62 мин
+ Внешний APIfunc геокодирования, добавление колонки3 мин65 мин
+ Условное обновлениепаттерн-сентинел, автоназначение4 мин69 мин

Итог: 32 эндпоинта, 14 таблиц, 47 Hurl-запросов. 11/11 этапов пройдено.

Добавление функций никогда не замедлялось. Существующие тесты никогда не ломались. Стены 200 эндпоинтов не существовало.

Бенчмарк Opus 4.7 — 32 эндпоинта, 14 таблиц, 47 Hurl-запросов, ~69 мин. Бенчмарк Sonnet 4.6 — 32 эндпоинта, 9 таблиц, 37 Hurl-запросов, ~43 мин.


yongol agent

LLM автоматически исправляет файлы SSOT через цикл validate-fix.

yongol agent specs/ --model ollama:gemma4:e4b --max-rounds 20

Ошибки validate передаются LLM, LLM исправляет их, и валидация запускается снова. Цикл повторяется до 0 ошибок. Работает даже с локальной моделью 4.5B (Gemma4).

Поддерживаемые бэкенды: ollama (локально), xai (Grok), gemini.


Можно ли редактировать сгенерированный код

Да. yongol generate сохраняет пользовательские правки при повторном запуске:

  • Каждый сгенерированный файл получает аннотацию //yg:checked llm=yongol-gen hash=<8hex>.
  • Если хеш отличается, файл помечается как preserved и пропускается при следующем generate.
  • yongol status показывает preserved-файлы и дрейф контракта (ошибки PRV-01/PRV-02).
  • Для записи намерения добавьте //yg:preserve reason="..." (необязательно). Для отмены preserve — удалите файл.

Встроенные функции и модели

Встроенные функции (вызываются через @call в SSaC)

ПакетФункцияОписание
authhashPassword, verifyPasswordХеширование/проверка bcrypt
authissueToken, verifyToken, refreshTokenJWT-токены
authgenerateResetTokenСброс пароля
cryptoencrypt, decryptAES-256-GCM
cryptogenerateOTP, verifyOTPTOTP
storageuploadFile, deleteFile, presignURLS3
mailsendEmail, sendTemplateEmailSMTP
textgenerateSlug, sanitizeHTML, truncateTextОбработка текста
imageogImage, thumbnailГенерация изображений

Встроенные модели (настраиваются через manifest.yaml)

ПакетИнтерфейсБэкендИспользование в SSaC
sessionSessionModel (Set/Get/Delete + TTL)PostgreSQL, Memorysession.Session.Get({key: ...})
cacheCacheModel (Set/Get/Delete + TTL)PostgreSQL, Memorycache.Cache.Set({key: ..., value: ..., ttl: ...})
fileFileModel (Upload/Download/Delete)S3, LocalFilefile.File.Upload({key: ..., body: ...})
queuesingleton Pub/Sub (Publish/Subscribe)PostgreSQL, Memory@publish "topic" {payload}

Автоматическая генерация миграций DDL

yongol generate обнаруживает изменения DDL и автоматически генерирует файлы миграций.

specs/db/
└── users.sql                         # SSOT — редактируйте здесь

artifacts/db/
├── .latest_schema.sql                # Базовый снимок
└── migrations/
    ├── 0001_initial.up.sql
    ├── 0001_initial.down.sql
    ├── 0002_add_users_email.up.sql
    └── 0002_add_users_email.down.sql

Неоднозначные изменения (переименование колонок, приведение типов, backfill NOT NULL) уточняются через подсказки в комментариях DDL (-- @rename, -- @cast, -- @backfill, -- @data_migration, -- @allow_destructive). Шесть правил (MIG-001 по MIG-006) контролируют опасные изменения. Фактическое применение к БД делегируется стандартным инструментам типа golang-migrate, flyway и др.


Почему более крупная модель — не ответ

«GPT-6 это решит.»

Не решит. Проблема не в интеллекте модели — а в медиуме.

Код как медиум не различает решения и реализацию. Какая бы модель ни читала код, она видит текст, в котором решения и детали переплетены. Какой бы умной ни была модель, если медиум не предоставляет различие, модель не может его провести.

yongol меняет медиум. Он переносит то, что редактирует ИИ, с кода на декларативные спецификации. Поскольку спецификации содержат только решения без деталей реализации, ИИ никогда не спутает решение с деталью. Выживание решений становится независимым от размера модели.

Маленькая LLM, редактирующая только SSOT, с validate, дающим точную обратную связь на каждую ошибку, может поддерживать ту же целостность решений, что и гораздо более крупная модель, редактирующая сырой код. yongol восполняет этот разрыв.


Тесты времени выполнения

Тесты Hurl полностью пишутся пользователем. Пишите их в specs/tests/, и yongol generate зеркалирует их в artifacts/tests/. При валидации правила XOH-01 — XOH-09 перекрёстно проверяют Hurl против OpenAPI, машин состояний и manifest.auth.

hurl --test --variable host=http://localhost:8080 artifacts/my-project/tests/*.hurl

Три категории:

  • smoke.hurl — Smoke-тесты эндпоинтов
  • scenario-*.hurl — Тесты бизнес-сценариев
  • invariant-*.hurl — Тесты инвариантов между эндпоинтами

Текущий статус

Генерация бэкенда Go+Gin: Beta — работает от начала до конца. Генерация фронтенда React: Alpha (в разработке).


Начало работы

Способ 1: Установить Skill (Рекомендуется)

npx skills add park-jun-woo/yongol

Установите skill yongol в вашего ИИ-агента (Claude Code, Cursor, Copilot и другие). Агент изучает рабочий процесс при установке.

/yongol Построй мультитенантный todo SaaS с аутентификацией и CRUD.

Способ 2: Прямая установка

Требуется Go 1.25+ и gcc (зависимость cgo: pg_query_go линкует libpg_query для парсинга DDL).

git clone https://github.com/park-jun-woo/yongol && cd yongol
make install
yongol validate examples/zenflow

0 errors, 0 warnings.

Направьте ИИ на эти спецификации и скажите добавить функцию. validate прокладывает рельсы; ИИ бежит по ним. Стены нет.


Связанные статьи

Код: github.com/park-jun-woo/yongol


Источники

  1. Google DORA Team. DORA State of AI-Assisted Software Development 2025. Google Cloud, 2025. dora.dev/dora-report-2025
  2. David L. Parnas. “On the Criteria to Be Used in Decomposing Systems into Modules.” Communications of the ACM 15(12): 1053-1058, 1972. doi:10.1145/361598.361623
  3. Weslley Torres, Mark G.J. van den Brand, Alexander Serebrenik. “A Systematic Literature Review of Cross-Domain Model Consistency Checking by Model Management Tools.” Software and Systems Modeling 20(3): 897-916, 2021. doi:10.1007/s10270-020-00834-1
  4. Deepak Babu Piskala. “Spec-Driven Development: From Code to Contract in the Age of AI Coding Assistants.” arXiv:2602.00180, January 2026. arxiv.org/abs/2602.00180
  5. Ehsani et al. “When AI Code Doesn’t Stick: An Empirical Study on Reverted Changes Introduced by AI Coding Agents.” MSR 2026 Mining Challenge, April 2026. 2026.msrconf.org
  6. Anton Jansen, Jan Bosch. “Software Architecture as a Set of Architectural Design Decisions.” EWSA 2005, LNCS 3527, Springer, 2005. semanticscholar.org
  7. Marco Brambilla, Jordi Cabot, Manuel Wimmer. Model-Driven Software Engineering in Practice. 2nd ed., Springer, 2017. doi:10.1007/978-3-031-02546-4
  8. GitClear. AI Copilot Code Quality 2025. February 2025. gitclear.com

История изменений

ДатаИзменения
2026-05-18Первая публикация
2026-05-19Добавлен бенчмарк Opus. Обновлены 10 SSOT
2026-05-21Синхронизация README: обновление бенчмарков (Opus 32ep/14tbl/47hurl/69min, Sonnet 32ep/9tbl/37hurl/43min), добавлено заявление “Harness with reins”, добавлен пример SSaC (AcceptProposal), добавлена команда yongol agent, добавлена система Preserve, добавлен список встроенных функций/моделей, добавлена автоматическая генерация миграций DDL, добавлено описание STML, добавлена ссылка на статью ifeval-ratchet
2026-05-26Синхронизация v0.6.10: добавлена команда-храповик yongol next, добавлены yongol init/features add/features remove создание и управление проектами, добавлен yongol import импорт внешнего OpenAPI, добавлен полный список аннотаций SSaC (16) (@eval, @subscribe, @verify-password, @no-pagination, @state-neutral), трихотомия тестов Hurl (smoke/scenario/invariant), раздел тестов времени выполнения, детали Preserve (коды ошибок PRV), расширение подсказок миграции DDL (@data_migration, @allow_destructive, правила MIG), текущий статус (Go+Gin Beta, React Alpha), установка разделена на 2 метода