Modelers & engineers
Own the canonical model — entities, relationships, metrics, access rules, materialization — and review every proposed change before it ships. The what, kept correct and governed.
Data modeling
Your data's meaning is scattered across dashboards, SQL scripts, spreadsheets, and tribal knowledge. Write it down once, in one language, as a governed model — and every agent, dashboard, and application inherits it.
You write the what. The engine owns the how: the transformations, materialization, indexing, and access enforcement underneath. Build it with your coding agent, versioned in git, tested in CI.
Across the data team
One model, and everyone who knows something about the business can improve it. A domain expert describes a change in plain language in Credible; the agent writes the Malloy; an engineer reviews the pull request in GitHub, and merging publishes it to every surface.
Own the canonical model — entities, relationships, metrics, access rules, materialization — and review every proposed change before it ships. The what, kept correct and governed.
Say what should change, in plain language — a new metric, a corrected definition, a rule the data can't settle — and the agent drafts it into the model for review. No Malloy required to contribute.
Watch what actually gets asked and propose fixes — a better doc line, a missing index, a view worth declaring — as changes for a person to review.
Meaning as code
For the people who own the model, Credible is a production-grade engineering workflow. The model is plain text: it diffs, it reviews, it tests, it ships.
Work in Cursor, Claude Code, or VS Code with the Credible extension. Version with git, test and deploy with CI/CD — the rigor you bring to production code, applied to your data's meaning.
A governed model takes two people: one who knows the answers, one who can write them down. The open-source Malloy modeling skills write the modeler's half for an agent to follow — profile the data, prove grain and joins before trusting them, surface what the data can't settle. You supply the answers.
Every definition traces to a decision someone accountable made — or to a flagged assumption, written into the model with a doc string saying so. Inspect the SQL and see a change land downstream before you publish.
How it works
A pipeline stores every node of its dependency graph as a table you schedule and re-read. In the engine, each dependency is just a name — private parsing at the bottom, the business domain at the top — and the compiler decides what, if anything, to materialize.
The bottom of the graph is the rawest shape there is: JSON in a log table. Private dimensions parse it; public ones speak the business domain. The definition of an active user is written exactly once, and weekly and monthly actives, sessions, and retention all inherit it. private and public are the encapsulation software got in the 1990s and data never did: the model shows an interface, not its guts.
source: user_engagement is bq.table('auth_logs.stdout') extend {
// parsing machinery: invisible to everything downstream
private dimension:
raw_user is json_value!(raw_request, '$.tuple_key.user')::string
#(doc) The definition of "active", written once. WAU, MAU, retention inherit it.
is_engaged_action is permission ~ r'^can_' and allowed = 'true'
where: is_engaged_action
// the business domain, one named concept at a time
public dimension:
#(doc) Actor email, normalized. Primary key for user-level analytics.
user is regexp_extract(raw_user, r'^user:(.+)$')
public measure:
#(doc) Distinct engaged users. WAU with a 7-day filter; MAU with 30.
external_users is count(user)
}One annotation turns an expensive source into a managed table — built behind the scenes, refreshed incrementally, shared across model versions; another pre-computes the rollups coarser questions answer straight from. It is the only mention of storage in the file. No orchestrator, no build order — the engine works out the DAG itself, and when a model version is archived, the tables nothing references are garbage-collected.
#@ persist refresh="incremental" watermark="event_timestamp"
source: user_engagement is bq.table('auth_logs.stdout') extend {
public measure:
#@ preaggregate grain="event_timestamp.day"
engaged_actions is count()
}Declare who sees what next to the data it protects: one #(authorize)over secure givens, resolved from each caller's verified identity, gates a whole source or scopes it row by row — enforced at the gateway on every query, from every surface. Sales sees their deals; engineering sees their metrics.
given:
GROUPS :: string[]
#(authorize) 'sales' in $GROUPS
source: deals is app_data extend {
measure: deal_count is count()
}Business rules, edge cases, approved definitions, and their rationale live next to the fields they govern as #(doc); the values worth finding are marked #(index). At publish, the engine compresses both into a concept index, so agents match a question by meaning, not by column name — and the tribal knowledge that lived in Slack threads now lives in the model.
dimension:
#(doc) Customer segment based on lifetime value
#(index)
customer_segment is lifetime_value ?
pick "High" when > 10000
pick "Medium" when > 1000
else "Low"
measure:
#(doc) Excludes internal test accounts per Finance policy (2024 Q3).
#(doc) Board-approved definition — do not modify without CFO sign-off.
monthly_active_users is count(distinct user_id)
{ where: last_activity > now - 30 days, is_internal = false }Why Malloy
Malloy sits above dbt and SQL the way TypeScript sits above C++: you describe what your data means, and the compiler derives how to compute it.
Describe what your data represents instead of hand-constructing joins and queries. The compiler generates optimized SQL for whatever database holds the data, so the model runs on any warehouse and is locked into none.
Bundle calculations, definitions, and metadata with the data they describe. Extend and specialize without duplicating logic.
Symmetric aggregates mean joined totals never double-count — the fan-out bugs that silently corrupt SQL can't even be written. Nesting and level-of-detail primitives handle what routinely breaks SQL.
Succinct enough that a business owner can read a definition, and in the training data, so an agent writes it fluently. The same file serves both.
MIT-licensed, production-ready, and backward-compatible. Your models stay yours, and the Publisher server that serves them is open source too.
Run dbt or LookML today? Start with a governed model over your existing marts — the agent reads your prior definitions as prior art rather than restarting the argument — and climb from there at your own pace. Your project is the input, not a rewrite.
The result
Engineer meaning once and every downstream surface inherits it — delivered as context to agents, definitions to dashboards, and governed APIs to your products.
One versioned package carries the model to every surface — to agents over MCP, the open protocol that connects agents to tools; to dashboards and workspaces; to the products you ship. It is tested as one unit, promoted with zero downtime, and rolled back by pointing at the previous version. And the more it gets asked, the better the model and the faster the engine.
Ready when you are
See how the AI Analytics Engine turns a data model written once into the pipeline, storage, governance, and context underneath every answer.