Image : generee par IA
Si l’IA n’arrete pas d’ecraser votre code, si le vibe coding s’est effondre a 200 endpoints, si vous voulez deplacer la charge de travail de l’IA du code vers les specifications – yongol est cette quille.
Le 200e endpoint
Vous construisez un SaaS en vibe coding. Au debut, c’est rapide. 5 tables, 12 endpoints — vingt minutes et ca tourne.
Mais passe 50 endpoints, quelque chose d’etrange se produit. L’IA produit aujourd’hui un pattern qui contredit celui d’hier. Passe 100, les fonctionnalites existantes se cassent silencieusement. Passe 200, ajouter une seule fonctionnalite coute 10 fois ce qu’ont coute les dix premieres.
Le rapport DORA 2025 l’a demontre empiriquement — les outils d’IA augmentent le debit de 2-18%, mais augmentent simultanement le taux d’echec des changements et le retravail[1]. L’IA est un « miroir et multiplicateur » qui amplifie les faiblesses des processus existants.
Ce n’est pas que le modele est stupide.
Decisions et implementation
Trois choses sont entrelacees dans le code source :
- Decisions utilisateur — cette colonne est
BIGINT, cet endpoint est reserve au proprietaire, la pagination est par curseur. - Logique metier — tarification, workflows, regles de cycle de vie.
- Details d’implementation — noms de variables, ordre des appels de bibliotheques, encapsulation d’erreurs.
Quand l’IA lit ce code, elle ne peut pas distinguer quelle ligne est une decision et laquelle est un detail. Alors quand elle « refactorise » ou « nettoie », elle ecrase silencieusement des decisions qu’elle a prises pour des details. L’utilisateur ne s’en rend compte qu’une fois le comportement deja fausse. En 1972, Parnas a dit « cachez les decisions de conception susceptibles de changer derriere des interfaces »[2] — il parlait aux humains. Mais maintenant que l’IA edite du code, a moins que la distinction entre decisions et details n’existe dans le medium lui-meme, personne — humain ou modele — ne peut la maintenir.
C’est pourquoi le vibe coding s’effondre a 200 endpoints. Un modele plus grand ne resout pas le probleme. Le medium — le code brut — ne preserve tout simplement pas les decisions. Chaque modele finit par heurter le meme mur.
La quille
La quille est le premier os pose lors de la construction d’un navire. Elle supporte le poids de la coque, empeche le roulis lateral, et toutes les autres structures sont construites dessus. Un navire construit sans quille flotte en eaux calmes mais se deforme quand les vagues arrivent.
Un SaaS construit en vibe coding, c’est pareil. Il flotte quand il est petit. Il se deforme quand il grandit.
yongol est la quille du SaaS code par IA.
Harness with reins — pas un modele plus grand, mais des renes plus precises. Un validateur deterministe juge chaque artefact, un cliquet impose la progression, et la machine decide si le travail est termine.
Sortir les decisions du code
Le coeur de yongol est simple. Separer les decisions du code.
Dix specifications declaratives (SSOT) gerent chacune une seule responsabilite :
| SSOT | Responsabilite |
|---|---|
| features.yaml | Catalogue de fonctionnalites — quoi construire |
| manifest.yaml | Configuration du projet — authentification, middleware, infrastructure |
| OpenAPI | Contrat API — routes, parametres, reponses |
| SQL DDL + sqlc | Modele de donnees — tables, colonnes, contraintes, requetes |
| SSaC | Flux de service — sequence de decisions a l’interieur d’un endpoint |
| Rego | Autorisation — qui peut faire quoi |
| Mermaid stateDiagram | Transitions d’etat — cycles de vie des entites |
| FuncSpec | Fonctions personnalisees — logique non exprimable en CRUD |
| Hurl | Scenarios de test — trichotomie smoke, scenario, invariant |
| STML | Frontend — Semantic Template Markup Language (HTML base sur les attributs data-*) |
Huit sur dix sont des standards industriels (OpenAPI, SQL, sqlc, Rego, Mermaid, Hurl, YAML). Seuls SSaC et STML sont des DSL crees par yongol. Ce que l’IA doit apprendre de zero est minimise.
Chaque SSOT ne contient que des decisions. Aucun detail d’implementation. L’IA edite les SSOT ; yongol generate produit le code a partir d’eux. Les decisions vivent en permanence dans les SSOT ; le code est une projection jetable.
Imposer la coherence
Les decisions sont maintenant reparties dans dix fichiers, donc des contradictions peuvent apparaitre. Le DDL dit BIGINT mais OpenAPI dit string ? SSaC declare @auth mais Rego n’a pas de regle correspondante ? Le diagramme d’etats a une transition mais SSaC n’a pas la fonction correspondante ?
Un SSOT contradictoire est une decision corrompue. Peu importe la proprete du code, si les decisions sont en conflit, le comportement devie.
yongol validate attrape cela.
✓ 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
Il valide d’abord chaque SSOT individuellement, puis effectue des verifications croisees entre couches. ~287 regles inspectent chaque reference symbolique parmi les dix SSOT. Si une seule contradiction existe, la compilation est refusee. La revue systematique de la litterature de Torres et al.[3] a note que la plupart des outils de gestion de modeles ne traitent que la coherence intra-modele, laissant la verification croisee entre modeles heterogenes comme un probleme ouvert — yongol validate comble precisement cette lacune.
L’IA ecrit librement. Elle sort des rails, validate l’attrape instantanement. Liberte sur rails.
yongol next — La commande cliquet
Alors que yongol validate montre toutes les erreurs d’un coup, yongol next montre les erreurs une par une. C’est le cliquet.
$ 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 seule instruction dont un agent IA a besoin est une phrase : « Execute yongol next specs/ et corrige les erreurs jusqu’a ce qu’il y en ait 0. »
Corrige l’erreur, la suivante apparait. Passe-les toutes et ca s’arrete :
$ yongol next specs/
✓ All validations passed. 0 errors.
L’agent ne declare pas « j’ai fini ». La machine prononce « il en reste » ou « tout est passe ». L’agent n’a pas autorite sur le jugement de terminaison.
Creation de projet et gestion des fonctionnalites
yongol init
Genere automatiquement l’echafaudage SSOT a partir de features.yaml.
yongol init Myapp features.yaml "My workflow automation SaaS"
cd Myapp && yongol validate specs # 0 errors
Manifest, stubs d’operationId OpenAPI, fichiers stub SSaC, regles d’autorisation Rego, tests smoke Hurl et configuration sqlc sont generes en une fois. Le projet demarre en etat de reussite yongol validate des le depart.
yongol features add / remove
Ajouter ou supprimer des fonctionnalites :
yongol features add new_features.yaml # Generer des stubs SSaC pour les nouveaux operationIds
yongol features remove ExportWorkflow --yes # Supprimer operationId + stubs SSaC
yongol import
Generer des packages client Go a partir de specs OpenAPI externes (Stripe, GitHub, etc.) :
yongol import https://api.stripe.com/openapi.yaml ./external/
Appeler les fonctions generees dans SSaC avec @call <pkg>.<Func>({...}).
operationId est la cle de voute
Comment lier dix couches ensemble ? Avec un seul identifiant PascalCase.
Entrez l’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
De la spec API au schema de base de donnees, de la politique d’autorisation aux transitions d’etat, des implementations de fonctions aux scenarios de test — la topologie complete d’une fonctionnalite sur un seul ecran. Des dizaines de grep remplaces par une seule commande.
operationId est la cle de voute parce que dans une application full-stack, l’unite d’une fonctionnalite est l’endpoint API. Un utilisateur appuie sur un bouton, une API est appelee, et cette API traverse toutes les autres couches. Un nom chaine physiquement dix couches.
SSaC — Pourquoi un DSL personnalise
Huit des 10 SSOT de yongol sont des standards industriels. Seuls SSaC (Service Sequences as Code) et STML ont ete crees par yongol. SSaC capture les decisions de flux de service.
Le vide que SSaC comble. Regardez le spectre des outils declaratifs : a une extremite se trouvent les standards de contrat (OpenAPI, SQL, Rego) — ils declarent quoi mais pas dans quel ordre. A l’autre extremite se trouvent les runtimes de workflow (Temporal, Inngest, Restate) — ce sont du code. Decisions et details d’implementation se recombinent dans le meme fichier. SSaC s’assoit dans le vide entre les deux : « a l’interieur d’un seul endpoint, que se passe-t-il, dans quel ordre, avec quels gardes. »
SSaC a un total de 16 annotations. Maitrisable avec un manuel d’une page.
Liste complete des annotations SSaC
| Annotation | Role | Format |
|---|---|---|
@get | Lecture DB | Type var = Model.Method({args}) |
@post | Creation de ligne | Type var = Model.Method({args}) |
@put | Mise a jour de ligne (sans retour) | Model.Method({args}) |
@delete | Suppression de ligne | Model.Method({args}) |
@empty | Garde nil → 404 | var "message" [STATUS] |
@exists | Garde not-nil → 409 | var "message" [STATUS] |
@auth | Verification d’autorisation | "action" "resource" {inputs} "message" [STATUS] |
@state | Transition de machine d’etats | diagram {inputs} "transition" "message" [STATUS] |
@call | Appel de fonction | [Type var =] pkg.Func({args}) |
@eval | Garde predicat (true → erreur) | pkg.Func({args}) "message" STATUS |
@publish | Publication dans file | "topic" {payload} |
@subscribe | Fonction declenchee par file | "topic" |
@verify-password | Login (securise contre timing) | Model.col=source Model.hash vs source -> var STATUS "msg" |
@response | Retour JSON | { field: var, ... } ou var |
@no-pagination | Exemption de regle de pagination | (niveau fonction) |
@state-neutral | Exemption de regle de machine d’etats | (niveau fonction) |
Exemple SSaC — AcceptProposal
Autorisation + double machine d’etats + sequestre + file :
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 lignes. 10 annotations. Deux machines d’etats, autorisation, sequestre, evenement de file, reponse — chaque decision visible, chaque detail absent.
Benchmark : ZenFlow
ZenFlow — un SaaS d’automatisation de workflows multi-tenant.
| Etape | Description | Temps | Cumule |
|---|---|---|---|
| Build initial | 10 endpoints, 6 tables, auth, machine d’etats | 13 min | 13 min |
| + Versionnement | clone de workflow, liste de versions | 6 min | 19 min |
| + Webhooks | webhook CRUD, backend de file | 6 min | 25 min |
| + Marketplace de templates | pagination curseur, clone cross-org | 3 min | 28 min |
| + Pieces jointes | rapports d’execution, backend fichier | 4 min | 32 min |
| + Planification | planification cron, backend session | 6 min | 38 min |
| + Journaux d’audit | pagination offset, backend cache | 3 min | 41 min |
| + Tableau de bord | joins de relation, types de reponse func | 7 min | 48 min |
| + Operations par lots | insertion en masse jsonb | 14 min | 62 min |
| + API externe | func geocodage, ajout de colonne | 3 min | 65 min |
| + Mise a jour conditionnelle | pattern sentinelle, attribution auto | 4 min | 69 min |
Final : 32 endpoints, 14 tables, 47 requetes Hurl. 11/11 etapes reussies.
L’ajout de fonctionnalites n’a jamais ralenti. Les tests existants ne se sont jamais casses. Le mur des 200 endpoints n’existait pas.
Benchmark Opus 4.7 — 32 endpoints, 14 tables, 47 requetes Hurl, ~69 min. Benchmark Sonnet 4.6 — 32 endpoints, 9 tables, 37 requetes Hurl, ~43 min.
yongol agent
Un LLM corrige automatiquement les fichiers SSOT via une boucle validate-fix.
yongol agent specs/ --model ollama:gemma4:e4b --max-rounds 20
Les erreurs de validate sont renvoyees au LLM, le LLM les corrige, et la validation s’execute a nouveau. La boucle se repete jusqu’a 0 erreurs. Fonctionne meme avec un modele local de 4.5B (Gemma4).
Backends supportes : ollama (local), xai (Grok), gemini.
Peut-on editer le code genere
Oui. yongol generate preserve les modifications utilisateur lors de la re-execution :
- Chaque fichier genere porte une annotation
//yg:checked llm=yongol-gen hash=<8hex>. - Si le hash differe, le fichier est marque comme preserved et ignore lors du prochain
generate. yongol statusmontre les fichiers preserves et la derive de contrat (erreursPRV-01/PRV-02).- Pour enregistrer l’intention, ajoutez
//yg:preserve reason="..."(optionnel). Pour annuler la preservation, supprimez le fichier.
Fonctions et modeles integres
Fonctions integrees (appelees via @call dans SSaC)
| Package | Fonction | Description |
|---|---|---|
auth | hashPassword, verifyPassword | Hachage/verification bcrypt |
auth | issueToken, verifyToken, refreshToken | Jetons JWT |
auth | generateResetToken | Reinitialisation de mot de passe |
crypto | encrypt, decrypt | AES-256-GCM |
crypto | generateOTP, verifyOTP | TOTP |
storage | uploadFile, deleteFile, presignURL | S3 |
mail | sendEmail, sendTemplateEmail | SMTP |
text | generateSlug, sanitizeHTML, truncateText | Traitement de texte |
image | ogImage, thumbnail | Generation d’images |
Modeles integres (configures via manifest.yaml)
| Package | Interface | Backend | Usage 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} |
Generation automatique de migrations DDL
yongol generate detecte les changements DDL et genere automatiquement des fichiers de migration.
specs/db/
└── users.sql # SSOT — editez ici
artifacts/db/
├── .latest_schema.sql # Snapshot de reference
└── migrations/
├── 0001_initial.up.sql
├── 0001_initial.down.sql
├── 0002_add_users_email.up.sql
└── 0002_add_users_email.down.sql
Les changements ambigus (renommage de colonnes, casting de types, backfill NOT NULL) sont desambiguises avec des hints en commentaire DDL (-- @rename, -- @cast, -- @backfill, -- @data_migration, -- @allow_destructive). Six regles (MIG-001 a MIG-006) controlent les changements dangereux. L’application reelle a la base de donnees est deleguee a des outils standards comme golang-migrate, flyway, etc.
Pourquoi un modele plus grand n’est pas la reponse
« GPT-6 reglera ca. »
Non. Le probleme n’est pas l’intelligence du modele — c’est le medium.
Le code comme medium ne distingue pas les decisions de l’implementation. Quel que soit le modele qui lit le code, il voit du texte ou decisions et details sont entrelaces. Aussi intelligent soit le modele, si le medium ne fournit pas la distinction, le modele ne peut pas la faire.
yongol change le medium. Il deplace ce que l’IA edite du code vers des specifications declaratives. Comme les specifications ne contiennent que des decisions sans details d’implementation, l’IA ne peut jamais confondre une decision avec un detail. La survie des decisions devient independante de la taille du modele.
Un petit LLM editant uniquement les SSOT, avec validate fournissant un feedback precis a chaque erreur, peut maintenir la meme integrite decisionnelle qu’un modele beaucoup plus grand editant du code brut. yongol comble cet ecart.
Tests d’execution
Les tests Hurl sont tous ecrits par l’utilisateur. Ecrivez-les dans specs/tests/ et yongol generate les recopie dans artifacts/tests/. Lors de la validation, les regles XOH-01 a XOH-09 verifient Hurl de maniere croisee contre OpenAPI, les machines d’etats et manifest.auth.
hurl --test --variable host=http://localhost:8080 artifacts/my-project/tests/*.hurl
Trois categories :
- smoke.hurl — Tests smoke des endpoints
- scenario-*.hurl — Tests de scenarios metier
- invariant-*.hurl — Tests d’invariants cross-endpoint
Etat actuel
Generation backend Go+Gin : Beta — fonctionnel de bout en bout. Generation frontend React : Alpha (en cours de developpement).
Commencer
Methode 1 : Installer le Skill (Recommande)
npx skills add park-jun-woo/yongol
Installez le skill yongol dans votre agent IA (Claude Code, Cursor, Copilot et plus). L’agent apprend le workflow a l’installation.
/yongol Construis un SaaS de taches multi-tenant avec auth et CRUD.
Methode 2 : Installation directe
Necessite Go 1.25+ et gcc (dependance cgo : pg_query_go lie libpg_query pour le parsing DDL).
git clone https://github.com/park-jun-woo/yongol && cd yongol
make install
yongol validate examples/zenflow
0 errors, 0 warnings.
Pointez une IA sur ces specifications et dites-lui d’ajouter une fonctionnalite. validate pose les rails ; l’IA court dessus. Il n’y a pas de mur.
References
- 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
Articles lies
- SSaC — Service Sequences as Code — Le DSL cle de voute de yongol. Declare les decisions a l’interieur des endpoints.
- Feature Chain — Tracer un full stack avec un operationId — Tracer les huit couches a travers un seul operationId.
- Ratchet Pattern — Comment faire finir le travail a un agent — Le fondement theorique de la facon dont validate retourne du feedback aux agents.
- Code ratchet exploitant IFEval — Une boucle de generation de code exploitant le biais de flagornerie, et Reins.
Code : github.com/park-jun-woo/yongol
Historique des modifications
| Date | Modifications |
|---|---|
| 2026-05-18 | Publication initiale |
| 2026-05-19 | Ajout du benchmark Opus. Mise a jour des 10 SSOT |
| 2026-05-21 | Synchronisation README : mise a jour des benchmarks (Opus 32ep/14tbl/47hurl/69min, Sonnet 32ep/9tbl/37hurl/43min), ajout de la declaration “Harness with reins”, ajout de l’exemple SSaC (AcceptProposal), ajout de la commande yongol agent, ajout du systeme Preserve, ajout de la liste des fonctions/modeles integres, ajout de la generation automatique de migrations DDL, ajout de la description STML, ajout du lien vers l’article ifeval-ratchet |
| 2026-05-26 | Synchronisation v0.6.10 : ajout de la commande cliquet yongol next, ajout de yongol init/features add/features remove creation et gestion de projet, ajout de yongol import importation OpenAPI externe, ajout de la liste complete des annotations SSaC (16) (@eval, @subscribe, @verify-password, @no-pagination, @state-neutral), trichotomie des tests Hurl (smoke/scenario/invariant), section tests d’execution, detail Preserve (codes d’erreur PRV), expansion des hints de migration DDL (@data_migration, @allow_destructive, regles MIG), etat actuel (Go+Gin Beta, React Alpha), installation divisee en 2 methodes |
Journal des modifications
- 2026-05-18: Version initiale