Roadmap
Quark is v1.1.4 on the stable v1.x line (v1.1.0 was the hardening minor), with
Phases 0–6 complete. This page separates what already ships from what is planned, so the
docs do not read like a promise that every idea is already in main.
The canonical roadmap (with the phased plan and trade-offs) lives at
docs/ANALISIS_MADUREZ.md §4;
the operational backlog lives at
TASKS.md.
Implemented core (shipping today)
Type-safe builder + composable query AST
- Type-safe
Query[T]builders, immutable composition (clone-per-method). - Typed expression AST (
Col/Lit/Func/And/Or/Not/Cast/In/Exists). - Subqueries composable through
AsSubquery. - CTEs (
With,WithRecursive). - Window functions (
OVER (PARTITION BY … ORDER BY …),RowNumber/Rank/Lag). - Set operators:
UNION,INTERSECT,EXCEPT. - Pessimistic locking:
ForUpdate,ForShare,SkipLocked,NoWait. - Optimistic locking:
quark:"version"tag +ErrStaleEntity. HavingAggregatefor HAVING over aggregates.- Nested
Preloadwith dotted paths ("Orders.Items.Product"). IN(...)chunking respecting dialect limits (Oracle 1000, MSSQL 2100).- Structured
JoinBuilderwithValidateJoinOn.
Six dialects + SQLGuard
- SQLite, PostgreSQL, MySQL, MariaDB, SQL Server, Oracle dialects.
- SQLGuard identifier, operator, raw-query, JSON path, and JOIN-ON validation.
- Dialect-specific upserts (
ON CONFLICT/ON DUPLICATE KEY/MERGE). - Custom dialects via
RegisterDialect.
Rich types
Nullable[T]generic.JSON[T]typed JSON column wrapper.Array[T]typed array wrapper (PG native, JSON-backed elsewhere).RegisterTypeMapperfor extensible mapping (decimal, UUID, etc. as opt-in).time.Durationmapped out of the box.- Per-column timezones via
quark:"tz=..."tag or Client-wideWithDefaultTZ; UTC-always wire contract.
CRUD + dirty tracking + soft delete
Create/Update/UpdateFields/Delete/HardDelete/UpsertBatch/CreateBatch/UpdateBatch/DeleteBatch.- Dirty tracking via
Tracked[T]: snapshot at load,Save()emits UPDATE only over changed fields (closes theisZeroValuetrap ofUpdate(entity)forfalse/0/""). - Soft delete scopes:
WithTrashed,OnlyTrashed,Restore.
Lifecycle hooks (transactional in v0.9)
- Before/After hooks for Create / Update / Delete.
BeforeFind/AfterFind.After*fire post-commit underClient.Tx— undone work no longer fires its side-effects.Tx.OnCommit/Tx.OnRollback+quark.TxFromContextfor arbitrary commit/rollback side-effects.- Savepoint rollback unwinds the queued hooks of that scope (v0.10).
Migrations (schema-as-code)
Migrate,Sync,CreateIndex,AddForeignKey.- Neutral schema introspection (
Client.IntrospectSchema) across all six dialects. - Pure-Go schema diff (
Diff,PlanMigration,ApplyPlan) with round-trip identity:Migrate(model) → PlanMigration(model)returns emptyPlanon all six motors. - Transactional or resumable execution: PG / MSSQL / SQLite
transactional; MySQL / MariaDB / Oracle resumable with
quark_migration_statecheckpoints. - Distributed migration lock: PG
pg_advisory_lock, MySQL/MariaDBGET_LOCK, MSSQLsp_getapplock, OracleDBMS_LOCK(needsGRANT EXECUTE ON DBMS_LOCK, see ADR-0018). SQLite returnsErrUnsupportedFeature. - Orchestrated
Backfillwith PK-based batching and resume tokens inquark_backfill_state. - Per-Client model registry (
Client.RegisterModeland friends). - Versioned Go migrations via
github.com/jcsvwinston/quark/migrate(versioned migration registry still global — see Known boundaries). quarkmigrateplan/verify/apply package (library, embeddable in your ownmigrations/main.go).
Multi-tenancy (four strategies)
DatabasePerTenantwith LRU of Clients.SchemaPerTenant.RowLevelSecurityClient— client-side WHERE injection (all six dialects; works throughOr()thanks tocloneForGroup).RowLevelSecurityNative— PostgreSQL engine-enforced RLS viaset_config('app.tenant_id', …, true)+CREATE POLICY. PG-only; other dialects fail-fast withErrUnsupportedFeature. Includes thequarktenant install-rls-policiesCLI to generate the policy DDL.- Tenant context propagation through association loads/saves.
Production caché (memory + Redis)
- Pluggable
CacheStore(memory, Redis). - Stampede protection:
stampedeStorewrapper (singleflight + ±jitter + Vattani XFetch) — ADR-0011. Knobs:WithCacheJitter,WithCacheXFetchBeta. - Per-row invalidation:
<table>:<pk>tag on top of the table tag; mutations register the affected PKs. - Deterministic, type-tagged, length-prefixed cache keys.
- Redis tag-TTL bug fixed (NX + GT pair takes the MAX, Redis 7+).
Observability
- OpenTelemetry traces (spans with
db.statement/db.operation). - OpenTelemetry metrics: counter
quark.queries.total, histogramsquark.queries.durationandquark.queries.rows. WithSpanRedactionkeeps bind values out of spans by default;IncludeArgsis opt-in for local debug.WithSlowQueryThresholdemits structured slow-query WARNs throughClient.logger.- Query observers and middleware.
Transactional resilience
Client.Tx(...)callback API; nested savepoint-style callbacks; explicit savepoints; isolation levels.WithDeadlockRetry(maxAttempts)onClient.Tx— re-runs the closure on PG 40P01 / MySQL 1213 / MSSQL 1205 / Oracle ORA-00060 with exponential backoff + jitter (opt-in, ctx-aware).
Audit log + event bus (v0.9)
Client.EnableAuditLog(ctx, AuditConfig)— records everyCreate/Update/Deleteintoquark_auditon the same connection/transaction as the write, atomically.Client.UseEventBus(bus)— realEventBuspublishingcreated/updated/deletedevents synchronously post-commit (at-least-once, no outbox — ADR-0013).
Code generation (Phase 6, opt-in)
cmd/quarkbinary with subcommands:gen,init,inspect,migrate,model,seed,sync,tenant,validate.quark genis generally available since v0.11.- Typed scanners on the read path (
List/First/Find). - Typed INSERT binder for single-integer-PK models (
Create). - Typed compile-time column accessors (
<Model>Columns+Query.WhereP) — pure compile-time sugar; column typos and wrong-typed values fail at build time. - Versioned generator contract (
//quark:gen vN) + model-hash drift check; incompatible-version files fall back to reflection by design.
Stored routines
- Stored routine helpers (
routine_builder.go).
Phase 6 — delivered (v1.0.0)
- Read replicas (F6-5) — delivered (v0.13.0):
WithReplicas(replica1, …)routes reads to replicas; writes always go to the primary;Sticky(ctx)pins reads to the primary for read-your-writes. Failover and health cooldown shipped with F6-6. See Read replicas. - Primary failover (F6-6) — delivered (v0.13.0): replica failover + health cooldown (ADR-0015).
- Sharding (
ShardRouter, F6-7) — delivered: routes per query by shard key (ADR-0016), with a runnable example inexamples/sharding/. Scatter-gather and shard-key-from-entity are deferred to v1.2+ (not in v1.1). - Stress testing harness (F6-9) — delivered (v0.13.0) in
benchmarks/stress/. - ent + sqlc in the benchmarks harness (F6-8b) — delivered (v1.0.0): the code-generation tier in the comparison harness; informational, not a v1.0 gate (the ≥3× gate was retired by ADR-0017). See Benchmarks.
- UPDATE / partial / batch binder codegen (F6-3b) — deferred to v1.2+;
measured payoff ~1% (
benchmarks/PROFILING.md), reopened only if motivated by type-safety.
v1.1.0 — delivered (hardening)
The post-v1.0 bug-bash (phases F0–F14, a systematic cross-engine pass over the whole surface) plus the correctness fixes it surfaced:
CreateBatchchunking (BB-10) — large batches now chunk to each dialect's bind-parameter ceiling (SQL Server ~2100; others higher) instead of failing.- Versioned migrations on SQL Server (BB-12) —
Migrator.Initbookkeeping DDL is now per-dialect (wasCREATE TABLE IF NOT EXISTS … TIMESTAMP, invalid on SQL Server). - MariaDB schema-diff false positive (BB-11) — a nullable, no-default column
no longer produces a phantom
OpAlterColumninPlanMigration. - Dialect-aware savepoints (BB-9),
SchemaPerTenantwrite routing (BB-8), and three eager-loading fixes (BB-5/6/7). --rejected in raw queries (BB-13) underAllowRawQueries.- Automatic MariaDB detection and an inbound PostgreSQL
LISTEN/NOTIFYlistener (ADR-0019).
No breaking changes. The 12h RC soak ran clean on the four testcontainers CI engines (PostgreSQL, MySQL, MariaDB, SQL Server); SQLite and Oracle hit only harness-level environment limits — not ORM bugs — which were hardened in #154. Functional correctness still runs across all six dialects on every PR (see boundaries below).
Known current boundaries
- Oracle runs in the blocking
integrationCI matrix (all six dialects validated on every PR). The Oracle job bootsgvenzl/oracle-freeviadocker runinstead of testcontainers, whose lifecycle exited code 1 on hosted runners; the suite gets a DSN viaQUARK_TEST_ORACLE_DSN. - The ADR-0002 ≥3× p99 codegen gate has been retired (ADR-0017).
Scan/bind codegen measured ~2–5% (scan) / ~1% (INSERT); per-op CPU is
dominated by
database/sqland the engine, not reflection, so the gate was unreachable by scan/bind codegen. The codegen layer remains valuable for type-safety (F6-4) and forward compatibility — it is not a speed feature, and v1.0 no longer gates on a speedup target. Seebenchmarks/PROFILING.mdand ADR-0017. - The versioned migration registry in
migrate/migrate.gois still global. The model registry has been per-Client since v0.6 (F3-7); the versioned registry stays as documented debt. LISTEN/NOTIFYis PostgreSQL-only. Both sides ship:Notify(outbound) and the inboundListenerFactory.CreateListenerlistener (ADR-0019). Other dialects returnErrDialectNotSupported. Delivery is fire-and-forget — notifications emitted while the listener connection is down are lost (no durable replay); it is not a substitute for a queue.SelectandWherevalidate simple identifiers; dotted columns and SQL expressions require views or controlled raw SQL via theinternal/guardlayer.DeleteByandDeleteBatchare hard-delete APIs in the current implementation.- Bulk and WHERE-based methods (
CreateBatch,UpdateBatch,DeleteBatch,DeleteBy) do not fireAfter*hooks.Before*hooks run per entity inCreateBatch/UpdateBatchsince v1.1.4.
Long-term goals (post-v1.0)
- Schema-first workflow with reviewable migration generation from a declarative schema (Atlas/Prisma-style). The pure-Go schema diff shipped in v0.6 is the foundation; the schema-first DSL is the next layer if demand justifies it.
- Cross-instance stampede coordination (distributed lock hook for the cache layer). Documented in ADR-0011 §"Cuándo reabrir"; only reopened if user demand surfaces.
- Pluggable ID strategies (UUID v7, ULID, Snowflake) as built-ins.
- Outbound CRUD lifecycle events to NATS/Kafka/Redis Streams as
out-of-the-box
EventBusimplementations beyond the logger/OTel defaults. - More database-native features behind dialect-specific extension points as the user base demands them.