Gambar: Dihasilkan AI
Jika AI terus menimpa kode Anda, jika vibe coding runtuh di 200 endpoint, jika Anda ingin mengalihkan beban kerja AI dari kode ke spesifikasi — yongol adalah lunas kapal itu.
Endpoint ke-200
Kamu membangun SaaS dengan vibe coding. Awalnya cepat. 5 tabel, 12 endpoint — dua puluh menit dan berjalan.
Tapi melewati 50 endpoint, hal aneh terjadi. AI menghasilkan pola hari ini yang bertentangan dengan kemarin. Melewati 100, fitur yang ada diam-diam rusak. Melewati 200, menambahkan satu fitur memakan biaya 10 kali lipat dari sepuluh pertama.
Laporan DORA 2025 membuktikan ini secara empiris — alat AI meningkatkan throughput 2-18%, tetapi secara bersamaan meningkatkan tingkat kegagalan perubahan dan pengerjaan ulang[1]. AI adalah “cermin dan pengali” yang memperkuat kelemahan proses yang ada.
Bukan karena modelnya bodoh.
Keputusan dan implementasi
Tiga hal terjalin dalam kode sumber:
- Keputusan pengguna — kolom ini
BIGINT, endpoint ini hanya untuk pemilik, paginasi menggunakan cursor. - Logika bisnis — kebijakan harga, alur kerja, aturan siklus hidup.
- Detail implementasi — nama variabel, urutan pemanggilan library, pembungkusan error.
Ketika AI membaca kode ini, ia tidak bisa membedakan baris mana yang keputusan dan mana yang detail. Jadi ketika “merefaktor” atau “membersihkan”, ia diam-diam menimpa keputusan yang disangka detail. Pengguna baru menyadari setelah perilaku sudah salah. Pada 1972, Parnas berkata “sembunyikan keputusan desain yang mungkin berubah di balik antarmuka”[2] — dia berbicara kepada manusia. Tapi sekarang AI juga mengedit kode, kecuali pembedaan antara keputusan dan detail ada di dalam medium itu sendiri, tidak ada siapa pun — manusia maupun model — yang bisa mempertahankannya.
Inilah mengapa vibe coding runtuh di 200 endpoint. Model yang lebih besar tidak memperbaikinya. Medium — kode mentah — sama sekali tidak melestarikan keputusan. Setiap model akhirnya menabrak dinding yang sama.
Lunas kapal
Lunas adalah tulang pertama yang diletakkan saat membangun kapal. Ia menahan berat lambung, mencegah oleng, dan setiap struktur lain dibangun di atasnya. Kapal yang dibangun tanpa lunas mengapung di air tenang tapi melengkung saat ombak datang.
SaaS yang dibangun dengan vibe coding sama saja. Mengapung saat kecil. Melengkung saat membesar.
yongol adalah lunas kapal SaaS yang dikodekan AI.
Harness with reins — bukan model yang lebih besar, tapi kendali yang lebih presisi. Validator deterministik menilai setiap artefak, ratchet memaksa kemajuan, dan mesin yang memutuskan apakah pekerjaan selesai.
Pindahkan keputusan keluar dari kode
Inti yongol sederhana. Pisahkan keputusan dari kode.
Sepuluh spesifikasi deklaratif (SSOT) masing-masing menangani satu tanggung jawab:
| SSOT | Tanggung Jawab |
|---|---|
| features.yaml | Katalog fitur — apa yang akan dibangun |
| manifest.yaml | Konfigurasi proyek — autentikasi, middleware, infrastruktur |
| OpenAPI | Kontrak API — rute, parameter, respons |
| SQL DDL + sqlc | Model data — tabel, kolom, constraint, query |
| SSaC | Alur layanan — urutan keputusan di dalam endpoint |
| Rego | Otorisasi — siapa boleh melakukan apa |
| Mermaid stateDiagram | Transisi status — siklus hidup entitas |
| FuncSpec | Fungsi kustom — logika yang tidak bisa diekspresikan sebagai CRUD |
| Hurl | Skenario pengujian — trikotomi smoke, scenario, invariant |
| STML | Frontend — Semantic Template Markup Language (HTML berbasis atribut data-*) |
Delapan dari sepuluh adalah standar industri (OpenAPI, SQL, sqlc, Rego, Mermaid, Hurl, YAML). Hanya SSaC dan STML yang DSL buatan yongol. Apa yang harus dipelajari AI dari nol diminimalkan.
Setiap SSOT berisi hanya keputusan. Tanpa detail implementasi. AI mengedit SSOT; yongol generate merender kode darinya. Keputusan hidup permanen di SSOT; kode adalah proyeksi sekali pakai.
Memaksa konsistensi
Keputusan kini tersebar di sepuluh file, jadi kontradiksi bisa muncul. DDL bilang BIGINT tapi OpenAPI bilang string? SSaC mendeklarasikan @auth tapi Rego tidak punya aturan yang cocok? Diagram status punya transisi tapi SSaC tidak punya fungsi yang sesuai?
SSOT yang berkontradiksi adalah keputusan yang tercemar. Sebersih apa pun kodenya, jika keputusan bertentangan, perilaku akan menyimpang.
yongol validate menangkap ini.
✓ 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
Pertama memvalidasi setiap SSOT secara individual, lalu menjalankan pemeriksaan silang antar lapisan. ~287 aturan memeriksa setiap referensi simbolik di antara sepuluh SSOT. Jika ada satu kontradiksi, kompilasi ditolak. Tinjauan literatur sistematis Torres et al.[3] mencatat bahwa sebagian besar alat manajemen model hanya menangani konsistensi dalam satu model, meninggalkan verifikasi silang antar model heterogen sebagai masalah terbuka — yongol validate mengisi tepat celah itu.
AI menulis dengan bebas. Keluar dari rel, validate langsung menangkap. Kebebasan di atas rel.
yongol next — Perintah ratchet
Sementara yongol validate menampilkan semua error sekaligus, yongol next menampilkan error satu per satu. Ini adalah ratchet.
$ 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/`.
Satu-satunya instruksi yang dibutuhkan agen AI adalah satu kalimat: “Jalankan yongol next specs/ dan perbaiki error sampai 0.”
Perbaiki error, yang berikutnya muncul. Lewati semua dan berhenti:
$ yongol next specs/
✓ All validations passed. 0 errors.
Agen tidak menyatakan “saya selesai”. Mesin yang memutuskan “masih tersisa” atau “semua lolos”. Agen tidak punya otoritas atas penilaian terminasi.
Pembuatan proyek dan manajemen fitur
yongol init
Membuat scaffolding SSOT secara otomatis dari features.yaml.
yongol init Myapp features.yaml "My workflow automation SaaS"
cd Myapp && yongol validate specs # 0 errors
Manifest, stub operationId OpenAPI, file stub SSaC, aturan otorisasi Rego, tes smoke Hurl, dan konfigurasi sqlc dihasilkan sekaligus. Proyek dimulai dalam keadaan lolos yongol validate sejak awal.
yongol features add / remove
Menambah atau menghapus fitur:
yongol features add new_features.yaml # Membuat stub SSaC untuk operationId baru
yongol features remove ExportWorkflow --yes # Menghapus operationId + stub SSaC
yongol import
Membuat paket klien Go dari spesifikasi OpenAPI eksternal (Stripe, GitHub, dll.):
yongol import https://api.stripe.com/openapi.yaml ./external/
Memanggil fungsi yang dihasilkan di SSaC dengan @call <pkg>.<Func>({...}).
operationId adalah batu kunci
Bagaimana mengikat sepuluh lapisan bersama? Dengan satu identifier PascalCase.
Masukkan 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
Dari spesifikasi API hingga skema database, dari kebijakan otorisasi hingga transisi status, dari implementasi fungsi hingga skenario pengujian — topologi lengkap satu fitur dalam satu layar. Puluhan grep diganti satu perintah.
operationId adalah batu kunci karena dalam aplikasi full-stack, unit sebuah fitur adalah endpoint API. Pengguna menekan tombol, API dipanggil, dan API itu memotong setiap lapisan lain. Satu nama merantai sepuluh lapisan secara fisik.
SSaC — Mengapa DSL kustom
Delapan dari 10 SSOT yongol adalah standar industri. Hanya SSaC (Service Sequences as Code) dan STML yang dibuat yongol. SSaC menangkap keputusan alur layanan.
Celah yang diisi SSaC. Lihat spektrum alat deklaratif: di satu ujung ada standar kontrak (OpenAPI, SQL, Rego) — mendeklarasikan apa tapi tidak dalam urutan apa. Di ujung lain ada runtime workflow (Temporal, Inngest, Restate) — itu adalah kode. Keputusan dan detail implementasi bergabung kembali dalam file yang sama. SSaC duduk di celah di antaranya: “di dalam satu endpoint, apa yang terjadi, dalam urutan apa, dengan penjaga apa.”
SSaC memiliki total 16 anotasi. Bisa dipelajari dari manual satu halaman.
Daftar lengkap anotasi SSaC
| Anotasi | Peran | Format |
|---|---|---|
@get | Baca DB | Type var = Model.Method({args}) |
@post | Pembuatan baris | Type var = Model.Method({args}) |
@put | Pembaruan baris (tanpa return) | Model.Method({args}) |
@delete | Penghapusan baris | Model.Method({args}) |
@empty | Penjaga nil → 404 | var "message" [STATUS] |
@exists | Penjaga not-nil → 409 | var "message" [STATUS] |
@auth | Pemeriksaan otorisasi | "action" "resource" {inputs} "message" [STATUS] |
@state | Transisi mesin status | diagram {inputs} "transition" "message" [STATUS] |
@call | Pemanggilan fungsi | [Type var =] pkg.Func({args}) |
@eval | Penjaga predikat (true → error) | pkg.Func({args}) "message" STATUS |
@publish | Publikasi ke antrian | "topic" {payload} |
@subscribe | Fungsi pemicu antrian | "topic" |
@verify-password | Login (aman terhadap timing) | Model.col=source Model.hash vs source -> var STATUS "msg" |
@response | Return JSON | { field: var, ... } atau var |
@no-pagination | Pengecualian aturan paginasi | (level fungsi) |
@state-neutral | Pengecualian aturan mesin status | (level fungsi) |
Contoh SSaC — AcceptProposal
Otorisasi + mesin status ganda + escrow + antrian:
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 baris. 10 anotasi. Dua mesin status, otorisasi, escrow, event antrian, respons — setiap keputusan terlihat, setiap detail absen.
Benchmark: ZenFlow
ZenFlow — SaaS otomasi workflow multi-tenant.
| Tahap | Deskripsi | Waktu | Kumulatif |
|---|---|---|---|
| Build awal | 10 endpoint, 6 tabel, auth, mesin status | 13 mnt | 13 mnt |
| + Versioning | klon workflow, daftar versi | 6 mnt | 19 mnt |
| + Webhooks | webhook CRUD, backend antrian | 6 mnt | 25 mnt |
| + Marketplace template | paginasi cursor, klon lintas organisasi | 3 mnt | 28 mnt |
| + Lampiran file | laporan eksekusi, backend file | 4 mnt | 32 mnt |
| + Penjadwalan | penjadwalan cron, backend sesi | 6 mnt | 38 mnt |
| + Log audit | paginasi offset, backend cache | 3 mnt | 41 mnt |
| + Dashboard | join relasi, tipe respons func | 7 mnt | 48 mnt |
| + Operasi batch | insert massal jsonb | 14 mnt | 62 mnt |
| + API eksternal | func geocoding, penambahan kolom | 3 mnt | 65 mnt |
| + Update kondisional | pola sentinel, penugasan otomatis | 4 mnt | 69 mnt |
Final: 32 endpoint, 14 tabel, 47 request Hurl. 11/11 tahap lolos.
Menambah fitur tidak pernah melambat. Tes yang ada tidak pernah rusak. Dinding 200 endpoint tidak ada.
Benchmark Opus 4.7 — 32 endpoint, 14 tabel, 47 request Hurl, ~69 mnt. Benchmark Sonnet 4.6 — 32 endpoint, 9 tabel, 37 request Hurl, ~43 mnt.
yongol agent
LLM secara otomatis memperbaiki file SSOT melalui loop validate-fix.
yongol agent specs/ --model ollama:gemma4:e4b --max-rounds 20
Error validate di-feedback ke LLM, LLM memperbaikinya, dan validasi dijalankan lagi. Loop berulang sampai 0 error. Bekerja bahkan dengan model lokal 4.5B (Gemma4).
Backend yang didukung: ollama (lokal), xai (Grok), gemini.
Bisakah mengedit kode yang dihasilkan
Bisa. yongol generate melestarikan edit pengguna saat dijalankan ulang:
- Setiap file yang dihasilkan mendapat anotasi
//yg:checked llm=yongol-gen hash=<8hex>. - Jika hash berbeda, file ditandai sebagai preserved dan dilewati pada
generateberikutnya. yongol statusmenampilkan file yang preserved dan drift kontrak (errorPRV-01/PRV-02).- Untuk mencatat niat, tambahkan
//yg:preserve reason="..."(opsional). Untuk membatalkan preserve, hapus file.
Fungsi dan model bawaan
Fungsi bawaan (dipanggil via @call di SSaC)
| Paket | Fungsi | Deskripsi |
|---|---|---|
auth | hashPassword, verifyPassword | Hash/verifikasi bcrypt |
auth | issueToken, verifyToken, refreshToken | Token JWT |
auth | generateResetToken | Reset kata sandi |
crypto | encrypt, decrypt | AES-256-GCM |
crypto | generateOTP, verifyOTP | TOTP |
storage | uploadFile, deleteFile, presignURL | S3 |
mail | sendEmail, sendTemplateEmail | SMTP |
text | generateSlug, sanitizeHTML, truncateText | Pemrosesan teks |
image | ogImage, thumbnail | Pembuatan gambar |
Model bawaan (dikonfigurasi via manifest.yaml)
| Paket | Antarmuka | Backend | Penggunaan 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} |
Pembuatan migrasi DDL otomatis
yongol generate mendeteksi perubahan DDL dan secara otomatis menghasilkan file migrasi.
specs/db/
└── users.sql # SSOT — edit di sini
artifacts/db/
├── .latest_schema.sql # Snapshot baseline
└── migrations/
├── 0001_initial.up.sql
├── 0001_initial.down.sql
├── 0002_add_users_email.up.sql
└── 0002_add_users_email.down.sql
Perubahan ambigu (rename kolom, type casting, backfill NOT NULL) disambiguasi dengan hint komentar DDL (-- @rename, -- @cast, -- @backfill, -- @data_migration, -- @allow_destructive). Enam aturan (MIG-001 hingga MIG-006) menjaga perubahan berbahaya. Penerapan aktual di database didelegasikan ke alat standar seperti golang-migrate, flyway, dll.
Mengapa model yang lebih besar bukan jawabannya
“GPT-6 akan menyelesaikan ini.”
Tidak akan. Masalahnya bukan kecerdasan model — tapi medium.
Kode sebagai medium tidak membedakan keputusan dari implementasi. Model apa pun yang membaca kode melihat teks di mana keputusan dan detail terjalin. Sepintar apa pun modelnya, jika medium tidak menyediakan pembedaan, model tidak bisa membedakan.
yongol mengubah medium. Ia memindahkan apa yang diedit AI dari kode ke spesifikasi deklaratif. Karena spesifikasi hanya berisi keputusan tanpa detail implementasi, AI tidak pernah bisa mengira keputusan sebagai detail. Kelangsungan keputusan menjadi independen dari ukuran model.
LLM kecil yang hanya mengedit SSOT, dengan validate memberikan feedback presisi pada setiap kesalahan, bisa mempertahankan integritas keputusan yang sama dengan model yang jauh lebih besar yang mengedit kode mentah. yongol menjembatani kesenjangan itu.
Tes runtime
Tes Hurl semuanya ditulis pengguna. Tulis di specs/tests/ dan yongol generate memirrorkan ke artifacts/tests/. Saat validasi, aturan XOH-01 hingga XOH-09 memeriksa silang Hurl terhadap OpenAPI, mesin status, dan manifest.auth.
hurl --test --variable host=http://localhost:8080 artifacts/my-project/tests/*.hurl
Tiga kategori:
- smoke.hurl — Tes smoke endpoint
- scenario-*.hurl — Tes skenario bisnis
- invariant-*.hurl — Tes invarian lintas endpoint
Status saat ini
Pembuatan backend Go+Gin: Beta — berfungsi end-to-end. Pembuatan frontend React: Alpha (sedang dikerjakan).
Mulai
Metode 1: Instal Skill (Direkomendasikan)
npx skills add park-jun-woo/yongol
Instal skill yongol ke agen AI kamu (Claude Code, Cursor, Copilot, dan lainnya). Agen mempelajari alur kerja saat instalasi.
/yongol Bangun SaaS todo multi-tenant dengan auth dan CRUD.
Metode 2: Instalasi langsung
Membutuhkan Go 1.25+ dan gcc (dependensi cgo: pg_query_go menghubungkan libpg_query untuk parsing DDL).
git clone https://github.com/park-jun-woo/yongol && cd yongol
make install
yongol validate examples/zenflow
0 errors, 0 warnings.
Arahkan AI ke spesifikasi ini dan suruh tambahkan fitur. validate memasang rel; AI berlari di atasnya. Tidak ada dinding.
Referensi
- 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
Artikel terkait
- SSaC — Service Sequences as Code — DSL batu kunci yongol. Mendeklarasikan keputusan di dalam endpoint.
- Feature Chain — Lacak full stack dengan satu operationId — Lacak delapan lapisan melalui satu operationId.
- Ratchet Pattern — Cara membuat agen menyelesaikan pekerjaan — Dasar teoretis bagaimana validate memberi feedback ke agen.
- Kode ratchet yang mengeksploitasi IFEval — Loop pembuatan kode yang mengeksploitasi bias sycophancy, dan Reins.
Kode: github.com/park-jun-woo/yongol
Riwayat perubahan
| Tanggal | Perubahan |
|---|---|
| 2026-05-18 | Publikasi awal |
| 2026-05-19 | Ditambahkan benchmark Opus. Diperbarui 10 SSOT |
| 2026-05-21 | Sinkronisasi README: pembaruan benchmark (Opus 32ep/14tbl/47hurl/69min, Sonnet 32ep/9tbl/37hurl/43min), ditambahkan pernyataan “Harness with reins”, ditambahkan contoh SSaC (AcceptProposal), ditambahkan perintah yongol agent, ditambahkan sistem Preserve, ditambahkan daftar fungsi/model bawaan, ditambahkan pembuatan migrasi DDL otomatis, ditambahkan deskripsi STML, ditambahkan tautan artikel ifeval-ratchet |
| 2026-05-26 | Sinkronisasi v0.6.10: ditambahkan perintah ratchet yongol next, ditambahkan yongol init/features add/features remove pembuatan dan manajemen proyek, ditambahkan yongol import impor OpenAPI eksternal, ditambahkan daftar lengkap anotasi SSaC (16) (@eval, @subscribe, @verify-password, @no-pagination, @state-neutral), trikotomi tes Hurl (smoke/scenario/invariant), bagian tes runtime, detail Preserve (kode error PRV), ekspansi hint migrasi DDL (@data_migration, @allow_destructive, aturan MIG), status saat ini (Go+Gin Beta, React Alpha), instalasi dibagi menjadi 2 metode |