
200番目のエンドポイント
バイブコーディングでSaaSを作る。最初は速い。テーブル5つ、エンドポイント12個——20分で動く。
だが50エンドポイントを超えたあたりから、おかしなことが起きる。AIが昨日と矛盾するパターンを今日作る。100を超えると、既存の機能が静かに壊れる。200を超えると、新機能1つの追加に最初の10個の10倍かかる。
モデルが馬鹿なわけではない。
意思決定と実装
ソースコードには3つのものが混在している:
- ユーザーの意思決定 — このカラムは
BIGINT、このエンドポイントはオーナーのみ、ページネーションはカーソル方式。 - ビジネスロジック — 価格設定、ワークフロー、ライフサイクルルール。
- 実装の詳細 — 変数名、ライブラリ呼び出し順、エラーラッピング。
AIがこのコードを読むとき、どの行が意思決定でどの行が詳細かを区別できない。だから「リファクタリング」や「クリーンアップ」をするとき、意思決定を詳細と誤認して静かに上書きする。ユーザーが気づくのは、動作がすでにおかしくなった後だ。
これが200エンドポイントでバイブコーディングが崩壊する理由だ。より大きなモデルを使っても解決しない。媒体——生のコード——が意思決定を保存しないからだ。 どのモデルも結局同じ壁にぶつかる。
竜骨
船を造るとき最初に据える骨格が竜骨だ。船体の重量を支え、左右の揺れを防ぎ、他のすべての構造物は竜骨の上に建てられる。竜骨のない船は穏やかな海では浮くが、波が来ると歪む。
バイブコーディングで作ったSaaSも同じだ。小さいうちは浮く。大きくなると歪む。
yongolはAIコーディングSaaSの竜骨だ。
意思決定をコードの外へ
yongolの核心はシンプルだ。意思決定をコードから分離する。
10の宣言的仕様(SSOT)がそれぞれ1つの関心事を担当する:
| SSOT | 関心事 |
|---|---|
| features.yaml | 機能カタログ — 何を作るか |
| manifest.yaml | プロジェクト設定 — 認証、ミドルウェア、インフラ |
| OpenAPI | APIコントラクト — ルート、パラメータ、レスポンス |
| SQL DDL + sqlc | データモデル — テーブル、カラム、制約、クエリ |
| SSaC | サービスフロー — エンドポイント内部の意思決定順序 |
| Rego | 認可 — 誰が何をできるか |
| Mermaid stateDiagram | 状態遷移 — エンティティのライフサイクル |
| FuncSpec | カスタム関数 — CRUDで表現できないロジック |
| Hurl | テストシナリオ — ランタイム検証 |
| STML | フロントエンド — ページ構造とデータバインディング |
10のうち8つは業界標準(OpenAPI、SQL、sqlc、Rego、Mermaid、Hurl、YAML)だ。SSaCとSTMLだけがyongolが作ったDSLだ。AIがゼロから学ぶ必要があるものは最小化されている。
各SSOTには意思決定だけが入っている。実装の詳細はない。AIがSSOTを編集し、yongol generateがSSOTからコードをレンダリングする。意思決定はSSOTに永続的に生き、コードは使い捨ての投影だ。
整合性を強制する
意思決定が10のファイルに分散したので、矛盾が生じうる。DDLはBIGINTなのにOpenAPIはstring?SSaCが@authを宣言しているのにRegoに対応するルールがない?状態図に遷移があるのにSSaCに対応する関数がない?
矛盾したSSOTは破損した意思決定だ。コードがどれだけきれいでも、意思決定が矛盾すれば動作は間違う。
yongol validateがこれを捕捉する。
✓ 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
まず各SSOTを個別に検証し、次にレイヤー間のクロスチェックを実行する。約287のルールが10のSSOT間のすべてのシンボル参照を検査する。矛盾が1つでもあればコンパイルを拒否する。
AIは自由に書く。レールを外れればvalidateが即座に捕捉する。レールの上の自由。
operationIdがキーストーンだ
10のレイヤーをどう結びつけるか?1つのPascalCase識別子で。
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
APIスペックからDBスキーマ、認可ポリシーから状態遷移、関数実装からテストシナリオまで——1つの機能の全トポロジーが1画面に見える。数十回のgrepが1つのコマンドに置き換わる。
operationIdがキーストーンである理由は、フルスタックアプリケーションにおいて機能の単位がAPIエンドポイントだからだ。ユーザーがボタンを押し、APIが呼ばれ、そのAPIが他のすべてのレイヤーを貫通する。1つの名前が10のレイヤーを物理的にチェーンする。
ベンチマーク:ZenFlow
ZenFlow——マルチテナントのワークフロー自動化SaaS。Claude Sonnet 4.6がSSOTを書き、yongolが検証した。
| 段階 | 内容 | 時間 | 累計 |
|---|---|---|---|
| 初期ビルド | マルチテナント、認証、ステートマシン、テーブル6、エンドポイント10 | 23分 | 23分 |
| + バージョニング | ワークフロー複製、バージョン一覧、INSERT…SELECTアクションコピー | 16分 | 39分 |
| + Webhook | イベント発行、Webhook CRUD、キューバックエンド | 8分 | 47分 |
| + テンプレートマーケットプレイス | カーソルページネーション、クロスOrg複製、公開エンドポイント | 7分 | 54分 |
| + ファイル添付 | 実行レポート、ファイルバックエンド | 7分 | 61分 |
| + スケジューリング | セッションベースcronスケジュール、TTL | 10分 | 71分 |
| + 監査ログ | キャッシュバックエンド、ページネーション、フィルター | 6分 | 77分 |
| + ダッシュボード | 集約API、リレーション結合、詳細ビュー | 14分 | 91分 |
| + バッチ操作 | アクション一括保存、JSONシリアライゼーション | 10分 | 101分 |
| + 外部API連携 | ジオコーディングAPIインポート、座標保存 | 14分 | 115分 |
| + 条件付き更新 | 自動割り当て、信頼度スコアリング、条件分岐 | 16分 | 131分 |
最終結果:30エンドポイント、12テーブル、64テストリクエスト。すべてグリーン。
10個の機能を順次追加した。機能追加で速度が落ちることはなかった。既存のテストが壊れることもなかった。200エンドポイントの壁は存在しなかった。
Opusで同じスペックを実行すると、30エンドポイント、73テストリクエスト、約76分。モデルが変わっても、レールは同じだ。
なぜ大きなモデルが答えではないか
「GPT-6が出れば解決する。」
しない。問題はモデルの知能ではなく媒体だ。
コードという媒体は意思決定と実装を区別しない。どのモデルがコードを読んでも、意思決定と詳細が入り交じったテキストを見る。モデルがどれだけ賢くても、媒体が区別を提供しなければ、モデルは区別できない。
yongolは媒体を変える。AIが編集する対象をコードから宣言的仕様に移す。仕様には意思決定だけがあり実装の詳細がないので、AIが意思決定を詳細と誤認することは決してない。意思決定の生存がモデルサイズから独立する。
小さなLLMがSSOTだけを編集し、validateが毎回のミスに正確なフィードバックを返せば、はるかに大きなモデルが生のコードを編集するのと同じレベルの意思決定整合性を維持できる。yongolがその差を埋める。
はじめる
npx skills add park-jun-woo/yongol
AIエージェント(Claude Code、Cursor、Copilotなど)にyongol skillをインストールすると、エージェントがワークフローを自動的に学習する。
CLIを直接使う場合:
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.
この仕様の上でAIに機能追加を指示してみてほしい。validateがレールを敷き、AIがレールの上を走る。壁はない。
関連記事
- SSaC — Service Sequences as Code — yongolのキーストーンDSL。エンドポイント内部の意思決定を宣言する。
- Feature Chain — 1つのoperationIdでフルスタックを追跡する — 1つのoperationIdで8つのレイヤーを貫通する追跡。
- Ratchet Pattern — エージェントを最後まで走らせる方法 — validateがエージェントにフィードバックする構造の理論的背景。