Imagem: Gerada por IA
Se a IA continua sobrescrevendo seu código, se o vibe coding colapsou em 200 endpoints, se você quer mudar o alvo de trabalho da IA do código para especificações — yongol é essa quilha.
O 200.o endpoint
Você constrói um SaaS com vibe coding. No início é rápido. 5 tabelas, 12 endpoints — vinte minutos e funciona.
Mas passados 50 endpoints, algo estranho acontece. A IA produz hoje um padrão que contradiz o de ontem. Passados 100, funcionalidades existentes quebram silenciosamente. Passados 200, adicionar uma única funcionalidade custa 10 vezes o que custaram as primeiras dez.
O relatório DORA 2025 demonstrou isso empiricamente — ferramentas de IA aumentam a produtividade em 2-18%, mas simultaneamente aumentam a taxa de falha de mudanças e o retrabalho[1]. A IA é um “espelho e multiplicador” que amplifica as fraquezas dos processos existentes.
Não é que o modelo seja burro.
Decisões e implementação
Três coisas estão emaranhadas no código-fonte:
- Decisões do usuário — esta coluna é
BIGINT, este endpoint é somente para o proprietário, a paginação é por cursor. - Lógica de negócio — precificação, fluxos de trabalho, regras de ciclo de vida.
- Detalhes de implementação — nomes de variáveis, ordem de chamadas de bibliotecas, encapsulamento de erros.
Quando a IA lê este código, não consegue distinguir qual linha é uma decisão e qual é um detalhe. Então, quando “refatora” ou “limpa”, sobrescreve silenciosamente decisões que confundiu com detalhes. O usuário só percebe depois que o comportamento já está errado. Em 1972, Parnas disse “esconda decisões de design propensas a mudança atrás de interfaces”[2] — ele falava para humanos. Mas agora que a IA edita código, a menos que a distinção entre decisões e detalhes exista no próprio meio, ninguém — humano ou modelo — consegue mantê-la.
Esta é a razão pela qual o vibe coding colapsa em 200 endpoints. Um modelo maior não resolve. O meio — código bruto — simplesmente não preserva decisões. Todos os modelos eventualmente batem na mesma parede.
A quilha
A quilha é o primeiro osso colocado ao construir um navio. Ela suporta o peso do casco, previne o balanço lateral, e toda outra estrutura é construída sobre ela. Um navio construído sem quilha flutua em águas calmas mas se deforma quando as ondas chegam.
Um SaaS construído com vibe coding é igual. Flutua quando é pequeno. Se deforma quando cresce.
yongol é a quilha do SaaS programado com IA.
Harness with reins — não um modelo maior, mas rédeas mais precisas. Um validador determinístico julga cada artefato, uma catraca impõe progresso, e a máquina decide se o trabalho está feito.
Mover decisões para fora do código
O núcleo do yongol é simples. Separar decisões do código.
Dez especificações declarativas (SSOTs) lidam cada uma com uma única responsabilidade:
| SSOT | Responsabilidade |
|---|---|
| features.yaml | Catálogo de funcionalidades — o que construir |
| manifest.yaml | Configuração do projeto — autenticação, middleware, infraestrutura |
| OpenAPI | Contrato API — rotas, parâmetros, respostas |
| SQL DDL + sqlc | Modelo de dados — tabelas, colunas, restrições, consultas |
| SSaC | Fluxo de serviço — sequência de decisões dentro de um endpoint |
| Rego | Autorização — quem pode fazer o quê |
| Mermaid stateDiagram | Transições de estado — ciclos de vida de entidades |
| FuncSpec | Funções personalizadas — lógica que não pode ser expressa como CRUD |
| Hurl | Cenários de teste — tricotomia smoke, scenario, invariant |
| STML | Frontend — Semantic Template Markup Language (HTML baseado em atributos data-*) |
Oito das dez são padrões da indústria (OpenAPI, SQL, sqlc, Rego, Mermaid, Hurl, YAML). Apenas SSaC e STML são DSLs criados pelo yongol. O que a IA precisa aprender do zero é minimizado.
Cada SSOT contém apenas decisões. Nenhum detalhe de implementação. A IA edita SSOTs; yongol generate renderiza código a partir deles. Decisões vivem permanentemente nos SSOTs; o código é uma projeção descartável.
Impondo consistência
Decisões estão agora distribuídas em dez arquivos, então contradições podem surgir. DDL diz BIGINT mas OpenAPI diz string? SSaC declara @auth mas Rego não tem regra correspondente? O diagrama de estados tem uma transição mas SSaC não tem a função correspondente?
Um SSOT contraditório é uma decisão corrompida. Não importa quão limpo o código seja, se as decisões conflitam, o comportamento se desvia.
yongol validate captura isso.
✓ 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
Primeiro valida cada SSOT individualmente, depois executa verificações cruzadas entre camadas. ~287 regras inspecionam cada referência simbólica entre todos os dez SSOTs. Se existir uma única contradição, a compilação é recusada. A revisão sistemática da literatura de Torres et al.[3] observou que a maioria das ferramentas de gerenciamento de modelos lida apenas com a consistência dentro de um único modelo, deixando a verificação cruzada entre modelos heterogêneos como um problema aberto — yongol validate preenche exatamente essa lacuna.
A IA escreve livremente. Saia dos trilhos e validate captura instantaneamente. Liberdade nos trilhos.
yongol next — O comando catraca
Enquanto yongol validate mostra todos os erros de uma vez, yongol next mostra erros um de cada vez. Isto é a catraca.
$ 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/`.
A única instrução que um agente de IA precisa é uma frase: “Execute yongol next specs/ e corrija erros até que sejam 0.”
Corrija o erro e o próximo aparece. Passe todos e para:
$ yongol next specs/
✓ All validations passed. 0 errors.
O agente não declara “terminei”. A máquina determina “ainda restam” ou “todos passaram”. O agente não tem autoridade sobre o julgamento de término.
Criação de projetos e gestão de funcionalidades
yongol init
Gera automaticamente o scaffolding SSOT a partir de features.yaml.
yongol init Myapp features.yaml "My workflow automation SaaS"
cd Myapp && yongol validate specs # 0 errors
Manifest, stubs de operationId OpenAPI, arquivos stub SSaC, regras de autorização Rego, testes smoke Hurl e configuração sqlc são gerados de uma vez. O projeto começa em estado de yongol validate aprovado desde o início.
yongol features add / remove
Adicionar ou remover funcionalidades:
yongol features add new_features.yaml # Gerar stubs SSaC para novos operationIds
yongol features remove ExportWorkflow --yes # Excluir operationId + stubs SSaC
yongol import
Gerar pacotes cliente Go a partir de especificações OpenAPI externas (Stripe, GitHub, etc.):
yongol import https://api.stripe.com/openapi.yaml ./external/
Chamar funções geradas em SSaC com @call <pkg>.<Func>({...}).
operationId é a pedra angular
Como unir dez camadas? Com um único identificador PascalCase.
Insira o 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
Da especificação API ao esquema do banco de dados, da política de autorização às transições de estado, das implementações de funções aos cenários de teste — a topologia completa de uma funcionalidade em uma tela. Dezenas de greps substituídos por um único comando.
operationId é a pedra angular porque em uma aplicação full-stack, a unidade de uma funcionalidade é o endpoint API. Um usuário pressiona um botão, uma API é chamada, e essa API atravessa todas as outras camadas. Um nome encadeia fisicamente dez camadas.
SSaC — Por que uma DSL personalizada
Oito dos 10 SSOTs do yongol são padrões da indústria. Apenas SSaC (Service Sequences as Code) e STML foram criados pelo yongol. SSaC captura decisões do fluxo de serviço.
A lacuna que SSaC preenche. Observe o espectro de ferramentas declarativas: em um extremo estão os padrões de contrato (OpenAPI, SQL, Rego) — declaram o quê mas não em que ordem. No outro extremo estão os runtimes de workflow (Temporal, Inngest, Restate) — esses são código. Decisões e detalhes de implementação se recombinam no mesmo arquivo. SSaC se posiciona na lacuna entre eles: “dentro de um único endpoint, o que acontece, em que ordem, com quais guardas.”
SSaC tem um total de 16 anotações. Aprendível com um manual de uma página.
Lista completa de anotações SSaC
| Anotação | Papel | Formato |
|---|---|---|
@get | Leitura DB | Type var = Model.Method({args}) |
@post | Criação de linha | Type var = Model.Method({args}) |
@put | Atualização de linha (sem retorno) | Model.Method({args}) |
@delete | Exclusão de linha | Model.Method({args}) |
@empty | Guarda nil → 404 | var "message" [STATUS] |
@exists | Guarda not-nil → 409 | var "message" [STATUS] |
@auth | Verificação de autorização | "action" "resource" {inputs} "message" [STATUS] |
@state | Transição de máquina de estados | diagram {inputs} "transition" "message" [STATUS] |
@call | Chamada de função | [Type var =] pkg.Func({args}) |
@eval | Guarda predicado (true → erro) | pkg.Func({args}) "message" STATUS |
@publish | Publicação em fila | "topic" {payload} |
@subscribe | Função disparada por fila | "topic" |
@verify-password | Login (seguro contra timing) | Model.col=source Model.hash vs source -> var STATUS "msg" |
@response | Retorno JSON | { field: var, ... } ou var |
@no-pagination | Isenção de regra de paginação | (nível de função) |
@state-neutral | Isenção de regra de máquina de estados | (nível de função) |
Exemplo SSaC — AcceptProposal
Autorização + máquinas de estado duplas + custódia + fila:
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 linhas. 10 anotações. Duas máquinas de estado, autorização, custódia, evento de fila, resposta — cada decisão visível, cada detalhe ausente.
Benchmark: ZenFlow
ZenFlow — um SaaS de automação de workflows multi-tenant.
| Etapa | Descrição | Tempo | Acumulado |
|---|---|---|---|
| Build inicial | 10 endpoints, 6 tabelas, auth, máquina de estados | 13 min | 13 min |
| + Versionamento | clone de workflow, lista de versões | 6 min | 19 min |
| + Webhooks | webhook CRUD, backend de fila | 6 min | 25 min |
| + Marketplace de templates | paginação por cursor, clone cross-org | 3 min | 28 min |
| + Anexos de arquivo | relatórios de execução, backend de arquivo | 4 min | 32 min |
| + Agendamento | agendamento cron, backend de sessão | 6 min | 38 min |
| + Logs de auditoria | paginação por offset, backend de cache | 3 min | 41 min |
| + Dashboard | joins de relação, tipos de resposta func | 7 min | 48 min |
| + Operações em lote | inserção em massa jsonb | 14 min | 62 min |
| + API externa | func geocodificação, adição de coluna | 3 min | 65 min |
| + Atualização condicional | padrão sentinela, atribuição automática | 4 min | 69 min |
Final: 32 endpoints, 14 tabelas, 47 requisições Hurl. 11/11 etapas aprovadas.
Adicionar funcionalidades nunca desacelerou. Testes existentes nunca quebraram. A parede dos 200 endpoints não existiu.
Benchmark Opus 4.7 — 32 endpoints, 14 tabelas, 47 requisições Hurl, ~69 min. Benchmark Sonnet 4.6 — 32 endpoints, 9 tabelas, 37 requisições Hurl, ~43 min.
yongol agent
Um LLM corrige automaticamente arquivos SSOT através de um loop validate-fix.
yongol agent specs/ --model ollama:gemma4:e4b --max-rounds 20
Erros de validate são realimentados ao LLM, o LLM os corrige, e a validação roda novamente. O loop se repete até 0 erros. Funciona até com um modelo local de 4.5B (Gemma4).
Backends suportados: ollama (local), xai (Grok), gemini.
Pode-se editar o código gerado?
Sim. yongol generate preserva edições do usuário ao re-executar:
- Cada arquivo gerado recebe uma anotação
//yg:checked llm=yongol-gen hash=<8hex>. - Se o hash difere, o arquivo é marcado como preservado e pulado no próximo
generate. yongol statusmostra arquivos preservados e deriva de contrato (errosPRV-01/PRV-02).- Para registrar a intenção, adicione
//yg:preserve reason="..."(opcional). Para des-preservar, exclua o arquivo.
Funções e modelos integrados
Funções integradas (chamadas via @call em SSaC)
| Pacote | Função | Descrição |
|---|---|---|
auth | hashPassword, verifyPassword | Hash/verificação bcrypt |
auth | issueToken, verifyToken, refreshToken | Tokens JWT |
auth | generateResetToken | Redefinição de senha |
crypto | encrypt, decrypt | AES-256-GCM |
crypto | generateOTP, verifyOTP | TOTP |
storage | uploadFile, deleteFile, presignURL | S3 |
mail | sendEmail, sendTemplateEmail | SMTP |
text | generateSlug, sanitizeHTML, truncateText | Processamento de texto |
image | ogImage, thumbnail | Geração de imagens |
Modelos integrados (configurados via manifest.yaml)
| Pacote | Interface | Backend | Uso em 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} |
Geração automática de migrações DDL
yongol generate detecta alterações DDL e gera automaticamente arquivos de migração.
specs/db/
└── users.sql # SSOT — edite aqui
artifacts/db/
├── .latest_schema.sql # Snapshot de referência
└── migrations/
├── 0001_initial.up.sql
├── 0001_initial.down.sql
├── 0002_add_users_email.up.sql
└── 0002_add_users_email.down.sql
Alterações ambíguas (renomeação de colunas, casting de tipos, backfill NOT NULL) são desambiguadas com hints em comentários DDL (-- @rename, -- @cast, -- @backfill, -- @data_migration, -- @allow_destructive). Seis regras (MIG-001 a MIG-006) controlam alterações perigosas. A aplicação real no banco de dados é delegada a ferramentas padrão como golang-migrate, flyway, etc.
Por que um modelo maior não é a resposta
“GPT-6 vai resolver isso.”
Não vai. O problema não é a inteligência do modelo — é o meio.
Código como meio não distingue decisões de implementação. Qualquer modelo que leia o código vê texto onde decisões e detalhes estão entrelaçados. Não importa quão inteligente o modelo seja, se o meio não fornece a distinção, o modelo não pode fazê-la.
yongol muda o meio. Ele move o que a IA edita do código para especificações declarativas. Como as especificações contêm apenas decisões sem detalhes de implementação, a IA nunca pode confundir uma decisão com um detalhe. A sobrevivência das decisões torna-se independente do tamanho do modelo.
Um LLM pequeno editando apenas SSOTs, com validate fornecendo feedback preciso a cada erro, pode manter a mesma integridade de decisões que um modelo muito maior editando código bruto. yongol preenche essa lacuna.
Testes em tempo de execução
Testes Hurl são todos escritos pelo usuário. Escreva-os em specs/tests/ e yongol generate espelha em artifacts/tests/. Durante a validação, as regras XOH-01 a XOH-09 verificam cruzadamente Hurl contra OpenAPI, máquinas de estado e manifest.auth.
hurl --test --variable host=http://localhost:8080 artifacts/my-project/tests/*.hurl
Três categorias:
- smoke.hurl — Testes smoke de endpoints
- scenario-*.hurl — Testes de cenários de negócio
- invariant-*.hurl — Testes de invariantes cross-endpoint
Estado atual
Geração de backend Go+Gin: Beta — funcional de ponta a ponta. Geração de frontend React: Alpha (em desenvolvimento).
Começar
Método 1: Instalar o Skill (Recomendado)
npx skills add park-jun-woo/yongol
Instale o skill yongol no seu agente de IA (Claude Code, Cursor, Copilot e mais). O agente aprende o fluxo de trabalho na instalação.
/yongol Construa um SaaS de tarefas multi-tenant com auth e CRUD.
Método 2: Instalação direta
Requer Go 1.25+ e gcc (dependência cgo: pg_query_go linka libpg_query para parsing DDL).
git clone https://github.com/park-jun-woo/yongol && cd yongol
make install
yongol validate examples/zenflow
0 errors, 0 warnings.
Aponte uma IA para estas especificações e diga para adicionar uma funcionalidade. validate traça os trilhos; a IA corre sobre eles. Não há parede.
Referências
- 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
Artigos relacionados
- SSaC — Service Sequences as Code — A DSL pedra angular do yongol. Declara decisões dentro de endpoints.
- Feature Chain — Rastreie um full stack com um operationId — Rastreie oito camadas através de um único operationId.
- Ratchet Pattern — Como fazer um agente terminar o trabalho — A fundamentação teórica de como validate retroalimenta agentes.
- Código ratchet que explora IFEval — Um loop de geração de código explorando viés de bajulação, e Reins.
Código: github.com/park-jun-woo/yongol
Histórico de alterações
| Data | Alterações |
|---|---|
| 2026-05-18 | Publicação inicial |
| 2026-05-19 | Adicionado benchmark Opus. Atualizados 10 SSOTs |
| 2026-05-21 | Sincronização README: atualização de benchmarks (Opus 32ep/14tbl/47hurl/69min, Sonnet 32ep/9tbl/37hurl/43min), adicionada declaração “Harness with reins”, adicionado exemplo SSaC (AcceptProposal), adicionado comando yongol agent, adicionado sistema Preserve, adicionada lista de funções/modelos integrados, adicionada geração automática de migrações DDL, adicionada descrição STML, adicionado link para artigo ifeval-ratchet |
| 2026-05-26 | Sincronização v0.6.10: adicionado comando catraca yongol next, adicionados yongol init/features add/features remove criação e gestão de projetos, adicionado yongol import importação de OpenAPI externo, adicionada lista completa de anotações SSaC (16) (@eval, @subscribe, @verify-password, @no-pagination, @state-neutral), tricotomia de testes Hurl (smoke/scenario/invariant), seção de testes em tempo de execução, detalhe Preserve (códigos de erro PRV), expansão de hints de migração DDL (@data_migration, @allow_destructive, regras MIG), estado atual (Go+Gin Beta, React Alpha), instalação dividida em 2 métodos |