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:
| SSOT | Responsabilidad |
|---|---|
| features.yaml | Catálogo de funcionalidades — qué construir |
| manifest.yaml | Configuración del proyecto — autenticación, middleware, infraestructura |
| OpenAPI | Contrato API — rutas, parámetros, respuestas |
| SQL DDL + sqlc | Modelo de datos — tablas, columnas, restricciones, consultas |
| SSaC | Flujo de servicio — secuencia de decisiones dentro de un endpoint |
| Rego | Autorización — quién puede hacer qué |
| Mermaid stateDiagram | Transiciones de estado — ciclos de vida de entidades |
| FuncSpec | Funciones personalizadas — lógica que no puede expresarse como CRUD |
| Hurl | Escenarios de prueba — tricotomía smoke, scenario, invariant |
| STML | Frontend — 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ón | Rol | Formato |
|---|---|---|
@get | Lectura DB | Type var = Model.Method({args}) |
@post | Creación de fila | Type var = Model.Method({args}) |
@put | Actualización de fila (sin retorno) | Model.Method({args}) |
@delete | Eliminación de fila | Model.Method({args}) |
@empty | Guarda nil → 404 | var "message" [STATUS] |
@exists | Guarda not-nil → 409 | var "message" [STATUS] |
@auth | Verificación de autorización | "action" "resource" {inputs} "message" [STATUS] |
@state | Transición de máquina de estados | diagram {inputs} "transition" "message" [STATUS] |
@call | Llamada a función | [Type var =] pkg.Func({args}) |
@eval | Guarda predicado (true → error) | pkg.Func({args}) "message" STATUS |
@publish | Publicación en cola | "topic" {payload} |
@subscribe | Función disparada por cola | "topic" |
@verify-password | Login (seguro ante timing) | Model.col=source Model.hash vs source -> var STATUS "msg" |
@response | Retorno JSON | { field: var, ... } o var |
@no-pagination | Exención de regla de paginación | (nivel de función) |
@state-neutral | Exenció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.
| Etapa | Descripción | Tiempo | Acumulado |
|---|---|---|---|
| Construcción inicial | 10 endpoints, 6 tablas, auth, máquina de estados | 13 min | 13 min |
| + Versionado | clonación de workflow, lista de versiones | 6 min | 19 min |
| + Webhooks | webhook CRUD, backend de cola | 6 min | 25 min |
| + Marketplace de plantillas | paginación por cursor, clonación cross-org | 3 min | 28 min |
| + Archivos adjuntos | informes de ejecución, backend de archivos | 4 min | 32 min |
| + Programación | programación cron, backend de sesión | 6 min | 38 min |
| + Logs de auditoría | paginación por offset, backend de caché | 3 min | 41 min |
| + Dashboard | joins de relación, tipos de respuesta func | 7 min | 48 min |
| + Operaciones por lotes | inserción masiva jsonb | 14 min | 62 min |
| + API externa | func geocodificación, adición de columna | 3 min | 65 min |
| + Actualización condicional | patrón centinela, asignación automática | 4 min | 69 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 statusmuestra archivos preservados y deriva de contrato (erroresPRV-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)
| Paquete | Función | Descripción |
|---|---|---|
auth | hashPassword, verifyPassword | Hash/verificación bcrypt |
auth | issueToken, verifyToken, refreshToken | Tokens JWT |
auth | generateResetToken | Restablecimiento de contraseña |
crypto | encrypt, decrypt | AES-256-GCM |
crypto | generateOTP, verifyOTP | TOTP |
storage | uploadFile, deleteFile, presignURL | S3 |
mail | sendEmail, sendTemplateEmail | SMTP |
text | generateSlug, sanitizeHTML, truncateText | Procesamiento de texto |
image | ogImage, thumbnail | Generación de imágenes |
Modelos integrados (configurados via manifest.yaml)
| Paquete | Interfaz | Backend | Uso en SSaC |
|---|---|---|---|
session | SessionModel (Set/Get/Delete + TTL) | PostgreSQL, Memory | session.Session.Get({key: ...}) |
cache | CacheModel (Set/Get/Delete + TTL) | PostgreSQL, Memory | cache.Cache.Set({key: ..., value: ..., ttl: ...}) |
file | FileModel (Upload/Download/Delete) | S3, LocalFile | file.File.Upload({key: ..., body: ...}) |
queue | singleton 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
- Google DORA Team. DORA State of AI-Assisted Software Development 2025. Google Cloud, 2025. dora.dev/dora-report-2025
- 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
- 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
- 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
- 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
- Anton Jansen, Jan Bosch. “Software Architecture as a Set of Architectural Design Decisions.” EWSA 2005, LNCS 3527, Springer, 2005. semanticscholar.org
- Marco Brambilla, Jordi Cabot, Manuel Wimmer. Model-Driven Software Engineering in Practice. 2nd ed., Springer, 2017. doi:10.1007/978-3-031-02546-4
- GitClear. AI Copilot Code Quality 2025. February 2025. gitclear.com
Artículos relacionados
- SSaC — Service Sequences as Code — El DSL piedra clave de yongol. Declara decisiones dentro de endpoints.
- Feature Chain — Rastrea un full stack con un operationId — Rastrea las ocho capas a través de un solo operationId.
- Ratchet Pattern — Cómo hacer que un agente termine el trabajo — La base teórica de cómo validate retroalimenta a los agentes.
- Código ratchet que explota IFEval — Un bucle de generación de código que explota el sesgo de adulación, y Reins.
Código: github.com/park-jun-woo/yongol
Historial de cambios
| Fecha | Cambios |
|---|---|
| 2026-05-18 | Publicación inicial |
| 2026-05-19 | Añadido benchmark Opus. Actualizados 10 SSOTs |
| 2026-05-21 | Sincronizació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-26 | Sincronizació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