yongol — La quilla del SaaS programado con IA Imagen: Generada por IA

Si estás lidiando con la IA sobreescribiendo tu código, si el vibe coding colapsó en 200 endpoints, si quieres cambiar el objetivo de la IA del código a las especificaciones – yongol es esa quilla.

El endpoint número 200

Construyes un SaaS con vibe coding. Al principio es rápido. 5 tablas, 12 endpoints — veinte minutos y funciona.

Pero al pasar los 50 endpoints, ocurre algo extraño. La IA produce hoy un patrón que contradice el de ayer. Pasados los 100, las funcionalidades existentes se rompen silenciosamente. Pasados los 200, añadir una sola función cuesta 10 veces lo que costaron las primeras diez.

El informe DORA 2025 lo demostró empíricamente — las herramientas de IA aumentan el rendimiento entre un 2-18%, pero al mismo tiempo incrementan la tasa de fallos en cambios y el retrabajo[1]. La IA es un “espejo y multiplicador” que amplifica las debilidades de los procesos existentes.

No es que el modelo sea tonto.


Decisiones e implementación

Tres cosas están entrelazadas en el código fuente:

  • Decisiones del usuario — esta columna es BIGINT, este endpoint es solo para el propietario, la paginación es por cursor.
  • Lógica de negocio — políticas de precios, flujos de trabajo, reglas de ciclo de vida.
  • Detalles de implementación — nombres de variables, orden de llamadas a bibliotecas, envoltorio de errores.

Cuando la IA lee este código, no puede distinguir qué línea es una decisión y cuál es un detalle. Así que cuando “refactoriza” o “limpia”, sobrescribe silenciosamente decisiones que confundió con detalles. El usuario no se da cuenta hasta que el comportamiento ya está mal. En 1972, Parnas dijo “oculta las decisiones de diseño propensas a cambiar detrás de interfaces”[2] — hablaba para humanos. Pero ahora que la IA edita código, a menos que la distinción entre decisiones y detalles exista en el propio medio, nadie — humano o modelo — puede mantenerla.

Esta es la razón por la que el vibe coding colapsa en 200 endpoints. Un modelo más grande no lo resuelve. El medio — código crudo — simplemente no preserva las decisiones. Todos los modelos terminan chocando con la misma pared.


La quilla

La quilla es el primer hueso que se coloca al construir un barco. Soporta el peso del casco, previene el balanceo lateral, y todas las demás estructuras se construyen sobre ella. Un barco construido sin quilla flota en aguas tranquilas pero se deforma cuando llegan las olas.

Un SaaS construido con vibe coding es igual. Flota cuando es pequeño. Se deforma cuando crece.

yongol es la quilla del SaaS programado con IA.

Harness with reins — no un modelo más grande, sino riendas más precisas. Un validador determinista juzga cada artefacto, un trinquete fuerza el progreso, y la máquina decide si el trabajo está hecho.


Sacar las decisiones del código

El núcleo de yongol es simple. Separar las decisiones del código.

Diez especificaciones declarativas (SSOT) manejan cada una una sola responsabilidad:

SSOTResponsabilidad
features.yamlCatálogo de funcionalidades — qué construir
manifest.yamlConfiguración del proyecto — autenticación, middleware, infraestructura
OpenAPIContrato API — rutas, parámetros, respuestas
SQL DDL + sqlcModelo de datos — tablas, columnas, restricciones, consultas
SSaCFlujo de servicio — secuencia de decisiones dentro de un endpoint
RegoAutorización — quién puede hacer qué
Mermaid stateDiagramTransiciones de estado — ciclos de vida de entidades
FuncSpecFunciones personalizadas — lógica que no puede expresarse como CRUD
HurlEscenarios de prueba — tricotomía smoke, scenario, invariant
STMLFrontend — Semantic Template Markup Language (HTML basado en atributos data-*)

Ocho de diez son estándares de la industria (OpenAPI, SQL, sqlc, Rego, Mermaid, Hurl, YAML). Solo SSaC y STML son DSLs creados por yongol. Lo que la IA debe aprender desde cero se minimiza.

Cada SSOT contiene solo decisiones. Sin detalles de implementación. La IA edita los SSOTs; yongol generate renderiza código a partir de ellos. Las decisiones viven permanentemente en los SSOTs; el código es una proyección desechable.


Forzar la consistencia

Las decisiones están ahora distribuidas en diez archivos, así que pueden surgir contradicciones. ¿El DDL dice BIGINT pero OpenAPI dice string? ¿SSaC declara @auth pero Rego no tiene regla correspondiente? ¿El diagrama de estados tiene una transición pero SSaC no tiene la función correspondiente?

Un SSOT contradictorio es una decisión corrupta. No importa lo limpio que sea el código, si las decisiones entran en conflicto, el comportamiento se desvía.

yongol validate atrapa esto.

✓ 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

Primero valida cada SSOT individualmente, luego ejecuta verificaciones cruzadas entre capas. ~287 reglas inspeccionan cada referencia simbólica entre los diez SSOTs. Si existe una sola contradicción, la compilación se rechaza. La revisión sistemática de la literatura de Torres et al.[3] señaló que la mayoría de las herramientas de gestión de modelos solo manejan la consistencia dentro de un solo modelo, dejando la verificación cruzada entre modelos heterogéneos como un problema abierto — yongol validate llena precisamente ese vacío.

La IA escribe libremente. Si se sale de los rieles, validate lo atrapa al instante. Libertad sobre rieles.


yongol next — El comando trinquete

Mientras yongol validate muestra todos los errores a la vez, yongol next muestra errores uno por uno. Esto es el trinquete.

$ 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/`.

La única instrucción que necesita un agente de IA es una frase: “Ejecuta yongol next specs/ y corrige errores hasta que queden 0.”

Corrige el error y aparece el siguiente. Pasa todos y se detiene:

$ yongol next specs/

✓ All validations passed. 0 errors.

El agente no declara “he terminado”. La máquina dictamina “aún quedan” o “todos pasados”. El agente no tiene autoridad sobre el juicio de terminación.


Creación de proyectos y gestión de funcionalidades

yongol init

Genera automáticamente el andamiaje SSOT a partir de features.yaml.

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

Manifest, stubs de operationId OpenAPI, archivos stub SSaC, reglas de autorización Rego, pruebas smoke Hurl y configuración sqlc se generan de una vez. El proyecto arranca en estado de yongol validate aprobado desde el inicio.

yongol features add / remove

Añadir o eliminar funcionalidades:

yongol features add new_features.yaml         # Generar stubs SSaC para nuevos operationIds
yongol features remove ExportWorkflow --yes    # Eliminar operationId + stubs SSaC

yongol import

Generar paquetes cliente Go a partir de especificaciones OpenAPI externas (Stripe, GitHub, etc.):

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

Llamar funciones generadas en SSaC con @call <pkg>.<Func>({...}).


operationId es la piedra clave

¿Cómo se unen diez capas? Con un único identificador PascalCase.

Introduce el 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

Desde la especificación API hasta el esquema de base de datos, desde la política de autorización hasta las transiciones de estado, desde las implementaciones de funciones hasta los escenarios de prueba — la topología completa de una funcionalidad en una sola pantalla. Decenas de greps reemplazados por un solo comando.

operationId es la piedra clave porque en una aplicación full-stack, la unidad de una funcionalidad es el endpoint API. Un usuario pulsa un botón, se llama a una API, y esa API atraviesa todas las demás capas. Un nombre encadena físicamente diez capas.


SSaC — Por qué un DSL personalizado

Ocho de los 10 SSOTs de yongol son estándares de la industria. Solo SSaC (Service Sequences as Code) y STML fueron creados por yongol. SSaC captura las decisiones del flujo de servicio.

El vacío que SSaC llena. Observa el espectro de herramientas declarativas: en un extremo están los estándares de contrato (OpenAPI, SQL, Rego) — declaran qué pero no en qué orden. En el otro extremo están los runtimes de workflow (Temporal, Inngest, Restate) — esos son código. Las decisiones y los detalles de implementación se recombinan en el mismo archivo. SSaC se sienta en el vacío intermedio: “dentro de un solo endpoint, qué ocurre, en qué orden, con qué guardas.”

SSaC tiene un total de 16 anotaciones. Aprendible con un manual de una página.

Lista completa de anotaciones SSaC

AnotaciónRolFormato
@getLectura DBType var = Model.Method({args})
@postCreación de filaType var = Model.Method({args})
@putActualización de fila (sin retorno)Model.Method({args})
@deleteEliminación de filaModel.Method({args})
@emptyGuarda nil → 404var "message" [STATUS]
@existsGuarda not-nil → 409var "message" [STATUS]
@authVerificación de autorización"action" "resource" {inputs} "message" [STATUS]
@stateTransición de máquina de estadosdiagram {inputs} "transition" "message" [STATUS]
@callLlamada a función[Type var =] pkg.Func({args})
@evalGuarda predicado (true → error)pkg.Func({args}) "message" STATUS
@publishPublicación en cola"topic" {payload}
@subscribeFunción disparada por cola"topic"
@verify-passwordLogin (seguro ante timing)Model.col=source Model.hash vs source -> var STATUS "msg"
@responseRetorno JSON{ field: var, ... } o var
@no-paginationExención de regla de paginación(nivel de función)
@state-neutralExención de regla de máquina de estados(nivel de función)

Ejemplo SSaC — AcceptProposal

Autorización + doble máquina de estados + escrow + cola:

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 líneas. 10 anotaciones. Dos máquinas de estados, autorización, escrow, evento de cola, respuesta — cada decisión visible, cada detalle ausente.


Benchmark: ZenFlow

ZenFlow — un SaaS de automatización de workflows multi-tenant.

EtapaDescripciónTiempoAcumulado
Construcción inicial10 endpoints, 6 tablas, auth, máquina de estados13 min13 min
+ Versionadoclonación de workflow, lista de versiones6 min19 min
+ Webhookswebhook CRUD, backend de cola6 min25 min
+ Marketplace de plantillaspaginación por cursor, clonación cross-org3 min28 min
+ Archivos adjuntosinformes de ejecución, backend de archivos4 min32 min
+ Programaciónprogramación cron, backend de sesión6 min38 min
+ Logs de auditoríapaginación por offset, backend de caché3 min41 min
+ Dashboardjoins de relación, tipos de respuesta func7 min48 min
+ Operaciones por lotesinserción masiva jsonb14 min62 min
+ API externafunc geocodificación, adición de columna3 min65 min
+ Actualización condicionalpatrón centinela, asignación automática4 min69 min

Final: 32 endpoints, 14 tablas, 47 solicitudes Hurl. 11/11 etapas superadas.

Añadir funcionalidades nunca ralentizó el proceso. Las pruebas existentes nunca se rompieron. La pared de los 200 endpoints no existió.

Benchmark Opus 4.7 — 32 endpoints, 14 tablas, 47 solicitudes Hurl, ~69 min. Benchmark Sonnet 4.6 — 32 endpoints, 9 tablas, 37 solicitudes Hurl, ~43 min.


yongol agent

Un LLM corrige automáticamente archivos SSOT mediante un bucle validate-fix.

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

Los errores de validate se retroalimentan al LLM, el LLM los corrige, y la validación se ejecuta de nuevo. El bucle se repite hasta 0 errores. Funciona incluso con un modelo local de 4.5B (Gemma4).

Backends soportados: ollama (local), xai (Grok), gemini.


¿Se puede editar el código generado?

Sí. yongol generate preserva las ediciones del usuario al re-ejecutar:

  • Cada archivo generado lleva una anotación //yg:checked llm=yongol-gen hash=<8hex>.
  • Si el hash difiere, el archivo se marca como preservado y se omite en el siguiente generate.
  • yongol status muestra archivos preservados y deriva de contrato (errores PRV-01/PRV-02).
  • Para registrar la intención, añade //yg:preserve reason="..." (opcional). Para des-preservar, elimina el archivo.

Funciones y modelos integrados

Funciones integradas (llamadas via @call en SSaC)

PaqueteFunciónDescripción
authhashPassword, verifyPasswordHash/verificación bcrypt
authissueToken, verifyToken, refreshTokenTokens JWT
authgenerateResetTokenRestablecimiento de contraseña
cryptoencrypt, decryptAES-256-GCM
cryptogenerateOTP, verifyOTPTOTP
storageuploadFile, deleteFile, presignURLS3
mailsendEmail, sendTemplateEmailSMTP
textgenerateSlug, sanitizeHTML, truncateTextProcesamiento de texto
imageogImage, thumbnailGeneración de imágenes

Modelos integrados (configurados via manifest.yaml)

PaqueteInterfazBackendUso en 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}

Generación automática de migraciones DDL

yongol generate detecta cambios DDL y genera automáticamente archivos de migración.

specs/db/
└── users.sql                         # SSOT — editar aquí

artifacts/db/
├── .latest_schema.sql                # Snapshot de referencia
└── migrations/
    ├── 0001_initial.up.sql
    ├── 0001_initial.down.sql
    ├── 0002_add_users_email.up.sql
    └── 0002_add_users_email.down.sql

Los cambios ambiguos (renombrado de columnas, casting de tipos, backfill NOT NULL) se desambiguan con hints en comentarios DDL (-- @rename, -- @cast, -- @backfill, -- @data_migration, -- @allow_destructive). Seis reglas (MIG-001 a MIG-006) controlan los cambios peligrosos. La aplicación real en la base de datos se delega a herramientas estándar como golang-migrate, flyway, etc.


Por qué un modelo más grande no es la respuesta

“GPT-6 lo resolverá.”

No lo hará. El problema no es la inteligencia del modelo — es el medio.

El código como medio no distingue decisiones de implementación. Cualquier modelo que lea el código ve texto donde decisiones y detalles están entrelazados. Por inteligente que sea el modelo, si el medio no proporciona la distinción, el modelo no puede hacerla.

yongol cambia el medio. Traslada lo que la IA edita del código a especificaciones declarativas. Como las especificaciones solo contienen decisiones sin detalles de implementación, la IA nunca puede confundir una decisión con un detalle. La supervivencia de las decisiones se vuelve independiente del tamaño del modelo.

Un LLM pequeño editando solo SSOTs, con validate proporcionando retroalimentación precisa en cada fallo, puede mantener la misma integridad de decisiones que un modelo mucho más grande editando código crudo. yongol cierra esa brecha.


Pruebas en tiempo de ejecución

Las pruebas Hurl son todas escritas por el usuario. Escríbelas en specs/tests/ y yongol generate las replica en artifacts/tests/. Durante la validación, las reglas XOH-01 a XOH-09 verifican cruzadamente Hurl contra OpenAPI, máquinas de estado y manifest.auth.

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

Tres categorías:

  • smoke.hurl — Pruebas smoke de endpoints
  • scenario-*.hurl — Pruebas de escenarios de negocio
  • invariant-*.hurl — Pruebas de invariantes cross-endpoint

Estado actual

Generación de backend Go+Gin: Beta — funcional de extremo a extremo. Generación de frontend React: Alpha (en desarrollo).


Comenzar

Método 1: Instalar el Skill (Recomendado)

npx skills add park-jun-woo/yongol

Instala el skill yongol en tu agente de IA (Claude Code, Cursor, Copilot y más). El agente aprende el flujo de trabajo al instalarse.

/yongol Construye un SaaS de tareas multi-tenant con auth y CRUD.

Método 2: Instalación directa

Requiere Go 1.25+ y gcc (dependencia cgo: pg_query_go enlaza libpg_query para el parseo DDL).

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

0 errors, 0 warnings.

Pon a una IA sobre estas especificaciones y dile que añada una funcionalidad. validate pone los rieles; la IA corre sobre ellos. No hay pared.


Referencias

  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

Artículos relacionados

Código: github.com/park-jun-woo/yongol


Historial de cambios

FechaCambios
2026-05-18Publicación inicial
2026-05-19Añadido benchmark Opus. Actualizados 10 SSOTs
2026-05-21Sincronización README: actualización de benchmarks (Opus 32ep/14tbl/47hurl/69min, Sonnet 32ep/9tbl/37hurl/43min), añadida declaración “Harness with reins”, añadido ejemplo SSaC (AcceptProposal), añadido comando yongol agent, añadido sistema Preserve, añadida lista de funciones/modelos integrados, añadida generación automática de migraciones DDL, añadida descripción STML, añadido enlace a artículo ifeval-ratchet
2026-05-26Sincronización v0.6.10: añadido comando trinquete yongol next, añadidos yongol init/features add/features remove creación y gestión de proyectos, añadido yongol import importación de OpenAPI externo, añadida lista completa de anotaciones SSaC (16) (@eval, @subscribe, @verify-password, @no-pagination, @state-neutral), tricotomía de pruebas Hurl (smoke/scenario/invariant), sección de pruebas en tiempo de ejecución, detalle Preserve (códigos de error PRV), expansión de hints de migración DDL (@data_migration, @allow_destructive, reglas MIG), estado actual (Go+Gin Beta, React Alpha), instalación dividida en 2 métodos

Registro de cambios

  • 2026-05-18: Versión inicial