yongol — The Keel of AI-Coded SaaS

The 200th Endpoint

You build a SaaS with vibe coding. At first it’s fast. 5 tables, 12 endpoints — twenty minutes and it runs.

But past 50 endpoints, something strange happens. The AI produces a pattern today that contradicts yesterday’s. Past 100, existing features silently break. Past 200, adding a single feature costs 10x what the first ten cost.

It’s not that the model is stupid.


Decisions and Implementation

Three things are tangled in source code:

  • User decisions — this column is BIGINT, this endpoint is owner-only, pagination is cursor-based.
  • Business logic — pricing, workflows, lifecycle rules.
  • Implementation details — variable names, library call order, error wrapping.

When AI reads this code, it cannot tell which line is a decision and which is a detail. So when it “refactors” or “cleans up,” it silently overwrites decisions it mistook for details. The user doesn’t notice until the behavior is already wrong.

This is why vibe coding collapses at 200 endpoints. A larger model doesn’t fix it. The medium — raw code — simply doesn’t preserve decisions. Every model eventually hits the same wall.


The Keel

The keel is the first bone laid when building a ship. It bears the hull’s weight, prevents side-to-side rolling, and every other structure is built on top of it. A ship built without a keel floats in calm water but warps when the waves come.

A SaaS built with vibe coding is the same. It floats when small. It warps when it grows.

yongol is the keel of AI-coded SaaS.


Move Decisions Out of Code

yongol’s core is simple. Separate decisions from code.

Ten declarative specs (SSOTs) each handle a single concern:

SSOTConcern
features.yamlFeature catalog — what to build
manifest.yamlProject config — auth, middleware, infra
OpenAPIAPI contract — routes, parameters, responses
SQL DDL + sqlcData model — tables, columns, constraints, queries
SSaCService flow — decision sequence inside an endpoint
RegoAuthorization — who can do what
Mermaid stateDiagramState transitions — entity lifecycles
FuncSpecCustom functions — logic that can’t be expressed as CRUD
HurlTest scenarios — runtime verification
STMLFrontend — page structure and data binding

Eight of the ten are industry standards (OpenAPI, SQL, sqlc, Rego, Mermaid, Hurl, YAML). Only SSaC and STML are DSLs created by yongol. What the AI must learn from scratch is minimized.

Each SSOT contains only decisions. No implementation details. The AI edits SSOTs; yongol generate renders code from them. Decisions live permanently in the SSOTs; the code is a disposable projection.


Enforcing Consistency

Decisions are now spread across ten files, so contradictions can appear. DDL says BIGINT but OpenAPI says string? SSaC declares @auth but Rego has no matching rule? The state diagram has a transition but SSaC has no corresponding function?

A contradicted SSOT is a corrupted decision. No matter how clean the code is, if the decisions conflict, the behavior is wrong.

yongol validate catches this.

✓ 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

It validates each SSOT individually first, then runs cross-layer checks. ~287 rules inspect every symbolic reference across all ten SSOTs. If a single contradiction exists, compilation is refused.

The AI writes freely. Step off the rails and validate catches it instantly. Freedom on rails.


operationId Is the Keystone

How do you bind ten layers together? With a single PascalCase identifier.

Enter the 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

From API spec to DB schema, authorization policy to state transitions, function implementations to test scenarios — a single feature’s entire topology on one screen. Dozens of greps replaced by one command.

operationId is the keystone because in a full-stack application, the unit of a feature is the API endpoint. A user presses a button, an API is called, and that API cuts through every other layer. One name physically chains ten layers together.


Benchmark: ZenFlow

ZenFlow — a multi-tenant workflow automation SaaS. Claude Sonnet 4.6 wrote the SSOTs; yongol validated them.

StageDescriptionTimeCumulative
Initial buildmulti-tenant, auth, state machine, 6 tables, 10 endpoints23 min23 min
+ Versioningworkflow clone, version list, INSERT…SELECT action copy16 min39 min
+ Webhooksevent publish, webhook CRUD, queue backend8 min47 min
+ Template marketplacecursor pagination, cross-org clone, public endpoints7 min54 min
+ File attachmentsexecution reports, file backend7 min61 min
+ Schedulingsession-based cron schedule, TTL10 min71 min
+ Audit logscache backend, pagination, filters6 min77 min
+ Dashboardaggregate API, relation joins, detail views14 min91 min
+ Batch operationsbulk action save, JSON serialization10 min101 min
+ External APIgeocoding API import, coordinate storage14 min115 min
+ Conditional updateauto-assign, confidence scoring, conditional branching16 min131 min

Final: 30 endpoints, 12 tables, 64 test requests. All green.

Ten features added sequentially. Adding features never slowed down. Existing tests never broke. The 200-endpoint wall didn’t exist.

Running the same spec with Opus: 30 endpoints, 73 test requests, ~76 min. The model changes, the rails stay the same.


Why a Bigger Model Isn’t the Answer

“GPT-6 will fix this.”

It won’t. The problem isn’t model intelligence — it’s the medium.

Code as a medium doesn’t distinguish decisions from implementation. Whatever model reads the code sees text where decisions and details are interleaved. No matter how smart the model is, if the medium doesn’t provide the distinction, the model can’t make it.

yongol changes the medium. It moves what the AI edits from code to declarative specs. Since specs contain only decisions with no implementation details, the AI can never mistake a decision for a detail. Decision survival becomes independent of model size.

A small LLM editing only SSOTs, with validate providing precise feedback on every miss, can maintain the same decision integrity as a much larger model editing raw code. yongol bridges that gap.


Get Started

npx skills add park-jun-woo/yongol

Install the yongol skill into your AI agent (Claude Code, Cursor, Copilot, and more). The agent learns the workflow on install.

To use the CLI directly:

go install github.com/park-jun-woo/yongol/cmd/yongol@latest

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

0 errors, 0 warnings.

Point an AI at these specs and tell it to add a feature. validate lays the rails; the AI runs on them. There is no wall.


Code: github.com/park-jun-woo/yongol