Clase 4

Consejos clave — Con saber esto, ya puedes dar instrucciones

En la Clase 3 aprendimos a prevenir el drift con tests Hurl y Git. Eso es suficiente para hasta 50 endpoints. Pero a mayor escala, surge un nuevo problema. La IA confunde tus decisiones con detalles de implementación y las sobrescribe cuando solo dices “ordénalo.”

Principio fundamental: Saca las decisiones fuera del código. Dentro del código, tus decisiones (“esta columna es un entero”) y los detalles (nombres de variables, manejo de errores) están mezclados. La IA no los distingue. Declarar las decisiones en especificaciones separadas impide que la IA las sobrescriba.

Instalar yongol:

Al agente: “Instala npx skills add park-jun-woo/yongol”

Creemos la funcionalidad Login de forma declarativa:

Al agente: “Declara la funcionalidad Login como SSOT”

La IA genera automáticamente el spec de API, el esquema de DB, el flujo de servicio, la política de autorización y el escenario de pruebas.

Al agente: “Ejecuta yongol validate y llega a 0 errores”

Si aparecen errores, la IA los corrige sola. 287 reglas validan cruzadamente entre las 10 especificaciones. Cuando todas las marcas de verificación están en verde, la generación de código está lista.

yongol actualmente es solo para Go. Pero el principio — “sacar las decisiones del código y detectar contradicciones con validación cruzada” — es independiente del lenguaje. Y los tests Hurl de la Clase 3 ya funcionan sin importar el lenguaje.

Prueba rápida

Pídele al agente que instale el skill de yongol:

Al agente: “Instala npx skills add park-jun-woo/yongol”

Al agente: “Declara la funcionalidad Login como SSOT”

La IA generará 5 archivos. specs/api/openapi.yaml, specs/db/users.sql, specs/service/auth/login.ssac, specs/policy/authz.rego, specs/tests/scenario-login.hurl.

Al agente: “Ejecuta yongol validate specs/”

0 errores significa éxito.

Creemos una contradicción intencionalmente:

Al agente: “Cambia el campo email en OpenAPI a mail”

Al agente: “Ejecuta yongol validate specs/”

Debería aparecer un error como “OpenAPI dice mail pero DDL dice email.” Mirando una sola capa, no hay error. Al cruzar dos capas, aparece la contradicción.

Al agente: “Corrige los errores del validate”

La IA unifica los nombres de campo. Valida de nuevo. 0 errores.


Por qué debes dar instrucciones de esta manera

Repaso de la clase anterior

En la Clase 3 aprendimos tres cosas.

  • Cómo declarar y verificar el comportamiento de la API con Hurl
  • Cómo crear puntos de guardado con Git
  • Cómo automatizar la verificación con CI/CD

Solo con estas tres ya puedes prevenir al peor enemigo del vibe coding — el drift. “Agrega esta nueva funcionalidad. Pero todos los tests Hurl existentes deben pasar.” Esta sola frase es la línea de defensa.

Pero a partir de 50 endpoints, surge un nuevo problema.

¿Qué pasa al superar los 50?

Imagina que construyes un SaaS con vibe coding. Al principio es rápido.

“Crea registro” — 2 minutos. “Crea login” — 1 minuto. “Crea edición de perfil” — 1 minuto.

12 endpoints, 5 tablas. Funcionando en 20 minutos.

Al superar 50, pasan cosas extrañas. La IA usa hoy un patrón diferente al de ayer. Al superar 100, las funcionalidades existentes se rompen silenciosamente. Al superar 200, agregar una sola funcionalidad nueva toma 10 veces más que crear las primeras 10.

¿Por qué?

No porque la IA sea tonta.

Tres cosas mezcladas en el código

Abre el código fuente y hay tres cosas entrelazadas.

Decisiones del usuario — “Esta columna es un entero.” “Esta API es solo para el propietario.” “La paginación usa estilo 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 librerías, código de manejo de errores.

Cuando la IA lee este código, no puede distinguir qué línea es tu decisión y cuál es un detalle. Entonces cuando dices “refactoriza”, confunde tus decisiones con detalles y los sobrescribe silenciosamente.

Piénsalo así. Decidiste “la entrada debe mirar al sur” al construir tu casa. Pero le dijiste al diseñador de interiores “ordena la casa” y cambió la dirección de la entrada. “El flujo de tránsito es mejor así.” Desde la perspectiva del diseñador, es optimización. Desde tu perspectiva, es un desastre.

Esto es exactamente lo que la IA hace. Usar un modelo más grande no lo arregla. Porque el medio (código fuente) en sí no puede preservar las decisiones.

Sacar las decisiones fuera del código

La solución es simple. Separar las decisiones del código.

Cómo has estado dirigiendo a la IA:

"Crea API de login" → La IA escribe código → Decisiones y detalles se mezclan

Lo que yongol propone:

Tú declaras decisiones → La IA edita declaraciones → yongol genera código

Las decisiones viven en especificaciones declarativas, el código es una proyección desechable. Cuando las decisiones cambian, editas la declaración y regeneras el código. Cuando los detalles cambian, solo regeneras el código. Nunca se mezclan.

10 tipos de SSOT — Cada uno maneja una sola responsabilidad

yongol separa las decisiones que componen el software en 10 especificaciones declarativas (SSOT: Single Source of Truth). Cada especificación maneja una sola responsabilidad.

No necesitas memorizar estos nombres. Agrupados por rol, son intuitivos:

Definir datos:

SSOTDecisión que manejaEn pocas palabras
features.yamlCatálogo de funcionalidades“Qué construir”
manifest.yamlConfiguración del proyecto“Auth es JWT, DB es PostgreSQL”
SQL DDLModelo de datos“Guardar estas columnas en esta tabla”
sqlcConsultas de DB“Consultar datos con este SQL”

Definir comportamiento:

SSOTDecisión que manejaEn pocas palabras
OpenAPIContrato de API“Enviar estos datos a esta dirección, obtener esta respuesta”
SSaCFlujo de servicio“Procesar en orden: consultar → validar → crear → responder”
Mermaid stateDiagramTransiciones de estado“El pedido cambia: pendiente → aprobado → completado → cancelado”

Verificar:

SSOTDecisión que manejaEn pocas palabras
OPA RegoPolítica de autorización“Solo los admins pueden eliminar”
HurlEscenarios de prueba“Llamar así, debe responder así”

Definir pantallas:

SSOTDecisión que manejaEn pocas palabras
STML (Service Template Markup Language)Frontend“Mostrar estos datos en pantalla así”

¿10 tipos parecen muchos? No hay que preocuparse. Tres datos a saber.

Primero, 8 de los 10 son estándares de la industria. OpenAPI, SQL, sqlc, Rego, Mermaid, Hurl, YAML — herramientas estándar de la industria usadas por desarrolladores profesionales, pero nunca necesitarás usarlas directamente. La IA las conoce. yongol solo creó dos nuevas: SSaC (flujo de servicio) y STML (frontend).

Segundo, no necesitas aprenderlas. La IA las conoce. Tú dices “crea una funcionalidad de registro” y la IA edita las 10 especificaciones. Solo ves el resultado.

Tercero, yongol actualmente es solo para Go. Aún no se puede usar con stacks como React+FastAPI o Next.js. Pero el principio aprendido en la Clase 4 — sacar decisiones del código y detectar contradicciones con validación cruzada — es independiente del lenguaje. Entender el principio significa que puedes aplicarlo inmediatamente cuando la herramienta se expanda. Y los tests Hurl de la Clase 3 ya funcionan sin importar el lenguaje — puedes hacer verificación de contratos API ahora mismo sin yongol.

100,000 líneas vs 12,500 líneas

¿Por qué molestarse en separar las decisiones? Los números lo aclaran inmediatamente.

EscalaEjemploSSOT (solo decisiones)Código de implementación
PequeñaReserva de peluquería~1,500 líneas~10K líneas
MedianaClase Jira, Notion~12,500 líneas~100K líneas
GrandeClase Shopify~30,000 líneas~300K líneas

Tomemos un SaaS mediano. De 100K líneas de código, las decisiones son 12,500 líneas. Las 87,500 líneas restantes son cableado — manejo de errores, llamadas a librerías, boilerplate.

Puedes hacer que la IA lea 100K líneas. Con contexto de 1M tokens, es físicamente posible. Pero poder leer no es lo mismo que poder manejar con precisión. A medida que el contexto crece, la información intermedia se pierde y los tokens innecesarios nublan el juicio.

Separar solo las decisiones produce 12,500 líneas. En un contexto con solo lo esencial y sin ruido, la precisión de la IA mejora. La misma IA haciendo la misma tarea, pero leyendo 1/8 de la cantidad, aumenta la precisión. Un efecto de ~10x de compresión de contexto.

operationId — La clave que enlaza todas las capas

Si las 10 especificaciones funcionan por separado, es caos. Necesitan estar conectadas. ¿Cómo?

Con un solo nombre.

Pulsar un botón envía una solicitud al servidor. Cada una de esas solicitudes se llama endpoint.

En una aplicación full-stack, la unidad de una funcionalidad es un endpoint de API. Cuando un usuario pulsa un botón, se llama una API, esa API ejecuta lógica de servicio, lee la DB, verifica permisos y transiciona estado. El punto de partida de este flujo es el operationId.

Muestra de un vistazo qué archivos abarca una sola funcionalidad.

Veamos qué pasa cuando introduces un operationId llamado 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

No necesitas poder leer esta salida. Lo importante es que un solo operationId puede rastrear todo.

Desde el spec de API hasta el esquema de DB, política de autorización, transiciones de estado, implementación de funciones, escenario de pruebas — el terreno completo de una funcionalidad visible en una sola pantalla.

Por eso yongol llama al operationId un keystone (piedra clave). Así como en arquitectura la última piedra cuña colocada en la cima de un arco sostiene todo el arco, un identificador en PascalCase une físicamente las 10 capas.

SSaC — Captura las decisiones dentro de las funciones

El más singular de los 10 SSOTs es SSaC (Service Sequences as Code).

Los SSOTs existentes: OpenAPI declara “qué solicitudes se reciben y qué respuestas se dan.” SQL DDL declara “qué se almacena.” Pero los internos de las funciones — el flujo de negocio de “consultar → validar → crear → responder” — no tenían dónde declararse. Había que leer el código de implementación para saberlo.

SSaC llena ese vacío.

Veamos un ejemplo real. Una funcionalidad llamada “aceptar una propuesta (AcceptProposal).”

Leyéndolo en lenguaje natural:

  1. Consultar la propuesta
  2. Si la propuesta no existe, devolver error “no encontrado”
  3. Consultar el proyecto vinculado a la propuesta
  4. Si el proyecto no existe, devolver error “no encontrado”
  5. Verificar si el solicitante es el propietario del proyecto
  6. Verificar si el estado de la propuesta permite aceptación
  7. Verificar si el estado del proyecto permite aceptación
  8. Cambiar el estado de la propuesta a “aceptada”
  9. Asignar un freelancer al proyecto y cambiar el estado a “en progreso”
  10. Retener el pago en custodia (escrow)
  11. Publicar evento “propuesta aceptada”
  12. Consultar y devolver la propuesta actualizada

Escrito en SSaC:

El código a continuación es simplemente lo anterior formateado según la especificación. No necesitas leerlo.

// @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() {}

No necesitas leer este código. La IA lo escribe. Tú solo confirmas si las frases en lenguaje natural de arriba son correctas.

16 líneas. 10 anotaciones. Generar código de implementación desde estas 16 líneas produce más de 100 líneas. Manejo de errores, gestión de transacciones, conversión de tipos, formateo de respuestas — todo llenado por la generación de código. Lo único que te importa es la decisión de “en qué orden procesar.”

SSaC tiene menos de 20 anotaciones en total. Se pueden aprender en una página. Y de nuevo, no necesitas aprenderlas tú mismo. La IA las escribe.

yongol validate — 287 reglas detectan contradicciones

Como las decisiones se distribuyen en 10 archivos, pueden surgir contradicciones entre archivos.

  • ¿Qué si DDL dice BIGINT pero OpenAPI dice string?
  • ¿Qué si SSaC declara @auth pero Rego no tiene la regla correspondiente?
  • ¿Qué si el diagrama de estados tiene una transición pero SSaC no tiene la función correspondiente?
  • ¿Qué si Hurl tiene un test que referencia un endpoint que no está en features?

Decisiones contradictorias producen código contradictorio. No importa cuán limpio sea el código, si las decisiones entran en conflicto, el comportamiento falla.

yongol validate detecta esto. No necesitas entender completamente la salida a continuación. Todas las marcas de verificación en verde significan que no hay contradicciones.

✓ 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 validación cruzada entre capas. ~287 reglas verifican todas las referencias simbólicas entre los 10 SSOTs. Si hay una sola contradicción, la generación de código se rechaza.

El punto clave: Las herramientas existentes solo ven su propia capa. Un validador de OpenAPI verifica si el spec de OpenAPI es válido. Un validador de SQL verifica si el DDL es válido. Pero “OpenAPI dice que user_id es string pero DDL dice BIGINT” — nadie detecta este tipo de contradicción entre capas. El valor único de yongol validate está en esta validación cruzada.

La IA escribe libremente. Cuando se sale del carril, validate lo detecta inmediatamente. Libertad sobre rieles.

yongol agent — Incluso modelos de 4.5B convergen a 0 errores

Validate detecta — genial. ¿Pero una persona necesita corregir los errores?

No. La IA lo hace.

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

Este solo comando hace que la IA repita validate → verificar errores → corregir → validar de nuevo → corregir de nuevo. Hasta 0 errores.

Hay resultados experimentales. Para un endpoint de Login, varios modelos escribieron 9 archivos SSOT:

ModeloTamañoEntornoResultado
Grok 4.3GrandeAPI0 errores al primer intento
Gemini 2.5 FlashMedianoAPI (gratis)0 errores con 1 retroalimentación
Gemma44.5BLocal (16GB VRAM)0 errores con 1 retroalimentación
Qwen38BLocal0 errores con 1 retroalimentación

Incluso un modelo local de 4.5B funciona. Costo $0. Sin conexión. Sin internet.

¿Por qué funcionan los modelos pequeños? Porque la retroalimentación de validate es un hecho determinístico. “line 41: field name mismatch, expected ‘user_id’, got ‘userId’” — esto no es una opinión. Es un hecho. No hay margen para que la IA adule sobre hechos. Acepta “sí, lo corrijo” y corrige.

No es el IQ del modelo sino la precisión de la retroalimentación lo que determina el resultado.

Benchmark: ZenFlow — 32 endpoints en 69 minutos

No es teoría. Son resultados medidos.

ZenFlow — un SaaS de automatización de flujos de trabajo multitenente. Construido desde cero enteramente con yongol.

FaseContenidoTiempoAcumulado
Construcción inicial10 endpoints, 6 tablas, auth, máquina de estados13 min13 min
+ VersionadoClonación de workflow, lista de versiones6 min19 min
+ WebhooksCRUD de webhooks, backend de cola6 min25 min
+ Marketplace de plantillasPaginación cursor, clonación cross-org3 min28 min
+ Archivos adjuntosReportes de ejecución, backend de archivos4 min32 min
+ ProgramaciónProgramación cron, backend de sesiones6 min38 min
+ Logs de auditoríaPaginación offset, backend de caché3 min41 min
+ DashboardJoins de relación, tipos de respuesta func7 min48 min
+ Operaciones en loteInserción lote jsonb14 min62 min
+ API externaFunc geocodificación, adición de columna3 min65 min
+ Actualizaciones condicionalesPatrón sentinel, asignación automática4 min69 min

Final: 32 endpoints, 14 tablas, 47 solicitudes Hurl. 11/11 fases completadas.

Lo más importante de estos números no es “69 minutos.” Es que la velocidad no disminuyó al agregar funcionalidades.

La primera funcionalidad (construcción inicial) tomó 13 minutos. La undécima (actualizaciones condicionales) tomó 4 minutos. El fenómeno llamado “el muro de los 200 endpoints” en vibe coding — donde el costo de agregar funcionalidades crece exponencialmente — no existió.

Los tests existentes tampoco se rompieron. Las 47 solicitudes Hurl pasaron en cada fase.

¿Se puede editar el código generado?

“Si el código se genera automáticamente, ¿se perderán mis ediciones manuales?”

Se puede. yongol generate preserva las ediciones del usuario al re-ejecutar.

  • Todos los archivos generados tienen una anotación //yg:checked llm=yongol-gen hash=<8hex>.
  • Si modificas el código, el hash cambia.
  • Los archivos con hash cambiado se marcan como preserved y se saltan en el siguiente generate.
  • yongol status muestra archivos preservados y drift de contratos.

El SSOT es la verdad, el código generado es una proyección, pero tus dibujos sobre la proyección se preservan.

Por qué modelos más grandes no son la respuesta

“GPT-6 lo arreglará.”

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

El medio del código no distingue decisiones de implementación. Cualquier modelo que lea código ve texto donde decisiones y detalles están entrelazados. No importa cuán inteligente sea el modelo, si el medio no proporciona distinción, no puede distinguir.

yongol cambia el medio. Mueve el objetivo de edición de la IA del código a especificaciones declarativas. Las especificaciones contienen solo decisiones y ningún detalle de implementación, así que la IA no puede confundir decisiones con detalles.

No un modelo más grande, sino una estructura más precisa es la respuesta.

Flujo de trabajo del agente — Todo lo que ves es el resultado

El flujo real de usar yongol:

1. Dices "crea una funcionalidad de reserva"
2. La IA edita los SSOTs en specs/
3. yongol validate specs/ — verifica consistencia
4. Si hay errores → La IA corrige el SSOT relevante → vuelve al paso 3
5. 0 errores → yongol generate — genera código
6. Los tests Hurl se ejecutan automáticamente
7. Pasan → commit. A la siguiente funcionalidad.

No necesitas leer código. Ni siquiera necesitas leer los SSOTs. “Constrúyelo” → “¿Listo?” → “Listo” — ese es el bucle. Lo que cambia es que nada se rompe detrás de escena.

La experiencia del vibe coding se mantiene igual. Solo desaparece el muro de los 200 endpoints.

Resumen — Qué recordar de esta clase

  1. El código tiene decisiones y detalles mezclados. La IA no los distingue. Esta es la causa raíz del drift.

  2. Saca las decisiones fuera del código. 10 especificaciones declarativas (SSOT) cada una maneja una sola responsabilidad. 8 de 10 son estándares de la industria.

  3. operationId es el keystone. Un nombre atraviesa las 10 capas. Una sola Feature Chain muestra el terreno completo de una funcionalidad.

  4. 287 reglas detectan contradicciones entre capas. Las herramientas existentes solo ven su propia capa. yongol validate detecta grietas entre capas.

  5. Incluso modelos de 4.5B convergen a 0 errores. No es el IQ del modelo sino la precisión de la retroalimentación lo que determina los resultados.

Ejercicio: Declarar un endpoint de Login como SSOT y detectar contradicciones

Objetivo: Experimentar la validación cruzada de yongol validate de primera mano.

Paso 1: Preparación del entorno

Copia y ejecuta el siguiente comando en tu terminal para instalar la funcionalidad yongol.

npx skills add park-jun-woo/yongol

Instala el skill de yongol en tu agente de IA.

Paso 2: Declarar el endpoint de Login

Dile a la IA: “Declara la funcionalidad Login como SSOT.”

La IA generará 5 archivos:

  • specs/api/openapi.yaml — POST /auth/login
  • specs/db/users.sql — CREATE TABLE users
  • specs/service/auth/login.ssac — @get → @empty → @call → @response
  • specs/policy/authz.rego — Política de autorización
  • specs/tests/scenario-login.hurl — Test de login

Paso 3: Validar

yongol validate specs/

0 errores significa éxito.

Paso 4: Crear una contradicción intencionalmente

Dile a la IA “cambia el campo email en OpenAPI a mail.” Deja DDL y SSaC como están.

yongol validate specs/

Debería aparecer un error. “OpenAPI dice mail pero DDL dice email.”

Esto es validación cruzada. Mirando una sola capa, no hay error. Cruza dos capas y la contradicción aparece.

Paso 5: Resolver la contradicción

Dile a la IA: “Corrige los errores del validate.” La IA unificará el nombre del campo. Valida de nuevo. 0 errores.

Paso 6: Generación de código

yongol generate specs/ artifacts/

Se genera el código completo para la funcionalidad Login. Más de 100 líneas de código de implementación desde 16 líneas de SSaC.

Lo que deberías haber sentido en este ejercicio:

  • La sensación de que decisiones (SSOT) e implementación (código generado) están separados
  • El momento en que la validación cruzada detecta una contradicción entre capas
  • El proceso donde la IA converge siguiendo la retroalimentación del validate cuando le dices “corrige”

Artículos relacionados


Curso completo de Reins Engineering

ClaseTítulo
Clase 1Cómo dirigir la IA
Clase 2Cómo desconfiar de la IA
Clase 3Aplicaciones irrompibles
Clase 4Decisiones fuera del código
Clase 5IA con riendas
Clase 6Si pasa, se bloquea
Clase 7Invertir la adulación
Clase 8La fábrica de agentes
Clase 9Automatización más allá del código
Clase 10La ley de los datos

Fuentes

  • Benchmark ZenFlow — 32 endpoints, 14 tablas, 47 solicitudes Hurl en 69 minutos. 11/11 fases completadas. Sin degradación de velocidad al agregar funcionalidades
  • Experimento de modelos yongol agent — Grok 4.3 (0 errores al primer intento), Gemini 2.5 Flash (0 errores con 1 retroalimentación), Gemma4 4.5B (0 errores con 1 retroalimentación), Qwen3 8B (0 errores con 1 retroalimentación)
  • yongol validate — 287 reglas, validación cruzada entre 10 SSOTs
  • Comparación de tamaño de código SaaS mediano — SSOT 12,500 líneas vs código de implementación 100K líneas (~10x de compresión de contexto)