Section C · Serving, quality & governance

Self-Serve & Internal Enablement

A data platform succeeds or fails on whether people can find, trust, and use its data without asking the DPE team for help. This chapter is about the enablement layer — the catalog, the dataset tiers, the access patterns per persona, and the onboarding paths — that turns a warehouse full of tables into a self-serve product. The alternative is a ticket queue that buckles the moment GridDP grows past a handful of analysts.

The ticket-queue anti-pattern

Picture GridDP's first year. There are three data engineers and a dozen analysts. When Finance wants take-rate by region, they Slack the DPE team; someone writes the SQL, pastes a screenshot, and moves on. When Supply ops wants fill-rate by GPU model, same thing. It works — barely — because the data team can hold every table in its collective head.

Now apply the scale numbers from Start Here: tens to low hundreds of internal users across the six consumer groups in that chapter — Product & growth, Finance & execs, Supply ops, Trust & safety, ML & DS, and the AI/NL interfaces. Each of them has questions every day. If every question routes through the data team, the team is the platform — a human API with a queue depth that grows linearly with headcount and a latency measured in days.

ANTI-PATTERN: the DPE team as a human query API ┌─────────┐ "GMV by region?" ┌──────────────┐ │ Finance │ ───────────────────▶ │ │ ├─────────┤ "fill rate?" │ DPE team │ queue │ Supply │ ───────────────────▶ │ (3 people) │ ▒▒▒▒▒▒▒▒ ← grows ├─────────┤ "churn cohort?" │ │ ▒▒▒▒▒▒▒▒ without │ Product │ ───────────────────▶ │ writes SQL │ ▒▒▒▒▒▒▒▒ bound ├─────────┤ "fraud rate?" │ by hand │ │ T&S │ ───────────────────▶ │ │ ⏱ latency: days └─────────┘ └──────────────┘ 😵 single point of failure SELF-SERVE: the platform answers, the team builds the platform ┌─────────┐ ┌──────────────────────────────┐ │ Finance │ ──▶ catalog ───┐ │ certified datasets + semantic│ ├─────────┤ (find) │ │ layer (ch.08) + golden paths │ │ Supply │ ──▶ semantic ──┼────▶│ │ ⏱ latency: minutes ├─────────┤ layer │ │ DPE team builds & curates │ 📈 scales w/ users │ Product │ ──▶ BI / NL ───┘ │ this, doesn't answer each Q │ ├─────────┤ └──────────────────────────────┘ │ T&S │ ▲ └─────────┘ │ team's queue holds only │ NEW datasets & edge cases
The load-bearing reframe

The DPE team's job is not to answer data questions; it is to build the system that lets others answer their own. Every recurring question that still lands in the team's queue is a bug in the platform — a missing dataset, an undiscoverable table, or an un-modeled metric. Self-serve is not a nice-to-have; it is the only architecture that scales sub-linearly with the number of consumers.

Self-serve does not mean "throw everyone raw SQL access and wish them luck." Uncurated access produces the opposite failure: five analysts compute "active rentals" five different ways and walk into the same meeting with five GMV numbers. Real self-serve is a curated product — discoverable data (the catalog), trustworthy data (tiers and contracts), and consistent metrics (the semantic layer from chapter 08). The rest of this chapter builds those pieces.

Data catalog & discovery

The first wall a new analyst hits is not "I can't query the data" — it's "I can't find the data, and when I find a table I don't know whether to trust it." GridDP has thousands of tables across the lakehouse, the warehouse, and the ClickHouse telemetry store. Which one holds the canonical rental record? Is rentals_v2_final or fct_rental the one Finance uses? When was it last refreshed? Who owns it? Without answers, every analyst rediscovers the data model by trial and error — and half of them give up and Slack the data team, recreating the ticket queue.

A data catalog is the search-and-trust layer over the platform's metadata. Good discovery surfaces, for every dataset:

  • Search & schemas — full-text search over table and column names, types, and descriptions, so "find me the bid table" actually works.
  • Ownership — the team and on-call human responsible (tied to the domains from Start Here).
  • Popularity & usage — how many people query it and how often; a proxy for "is this the real one."
  • Freshness — last successful load and the expected cadence, so a stale table is visibly stale.
  • Sample values & profiling — what the data actually looks like, null rates, distinct counts.
  • Lineage — upstream sources and downstream consumers, so you can trace a number back to the source system and forward to every dashboard it feeds.
  • Tier / certification — the trust badge (next section).
CATALOG ARCHITECTURE — harvest, store, serve ┌───────────────────────────────────────────────────────────────────┐ │ HARVESTERS (pull metadata; the platform pushes events too) │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌──────────┐ ┌────────┐ │ │ │ warehouse │ │ lakehouse │ │ ClickHouse│ │ dbt │ │ BI │ │ │ │ INFO_SCHEMA│ │ Iceberg │ │ system │ │ manifest │ │ tool │ │ │ │ + query │ │ catalog │ │ tables │ │ + tests │ │ (dash, │ │ │ │ history │ │ + lineage│ │ │ │ + docs │ │ owners)│ │ │ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ └────┬─────┘ └───┬────┘ │ └─────────┼─────────────┼─────────────┼────────────┼───────────┼──────┘ └─────────────┴──────┬──────┴────────────┴───────────┘ ▼ ┌───────────────────────────┐ │ METADATA STORE (graph) │ entities: dataset, │ datasets · columns · │ column, owner, tier, │ lineage edges · owners · │ glossary term, dashboard │ tiers · glossary · usage │ └─────────────┬─────────────┘ ▼ ┌────────────────────────────────────┐ │ UI (search, browse, lineage map) │ ← humans │ API (programmatic, feeds NL/AI) │ ← agents (ch.13) └────────────────────────────────────┘

The catalog is itself a small data platform: harvesters crawl each store's system metadata (and the dbt manifest, and the BI tool's API) on a schedule or via push events, a metadata store (usually a graph, because lineage is a graph) holds the unified model, and a UI + API serve it to humans and — increasingly — to the NL/AI layer in chapter 13, which depends on rich catalog metadata to ground its answers.

Tooling, per reference stackCatalog choiceNotes
Stack A — open-source lakehouseDataHub or OpenMetadataSelf-hosted graph catalog; rich lineage, pluggable harvesters; you run it
Stack B — Databricks-centricUnity CatalogGovernance + discovery + lineage native to the platform; less cross-engine reach
Stack C — Snowflake-centricSnowflake HorizonBuilt-in discovery, lineage, tags, data-quality monitors over Snowflake objects
A catalog nobody curates is a phone book of strangers

Auto-harvested metadata gets you schemas, lineage, and usage for free — but descriptions, ownership, and tiers are human inputs. The failure mode is a catalog full of 4,000 tables with blank descriptions that no one trusts. The fix is to make curation a side effect of the workflow: descriptions live in dbt model YAML (so they ship with the code), ownership is inherited from the domain, and certification is a reviewed promotion (next section), not a field someone is supposed to remember to fill in.

Data contracts, operationalized

Chapter 01 introduced the data contract as the versioned agreement between a producing service and the platform — the API for what a source emits. Here we operationalize that idea and extend it one layer up: contracts also govern the datasets the platform publishes to its internal consumers. A contract is the seam that lets producer and consumer evolve independently, and the enforcement mechanism that keeps self-serve from turning into self-sabotage.

Four properties turn a contract from documentation into a load-bearing system:

  • Producer-owned schema. The team that emits the data owns the contract, checked into their repo next to the code that produces it. The platform is a consumer with rights, not the author.
  • CI enforcement. A schema change that violates the contract fails the producer's CI build — before it ships — not the platform's 3 a.m. pipeline. This is the whole point: the breaking change is caught where it's cheapest to fix.
  • An explicit breaking-change policy. Additive changes are free; removals and type changes require a major-version bump and advance notice, so downstream consumers can migrate.
  • The contract as the API boundary. Downstream code depends on the contract's guarantees (this field exists, is non-null, lands within 60s), not on the producer's internal table layout.
data-contract: catalog metadata for a published dataset — fct_rental
dataset: marketplace.fct_rental
owner: marketplace-domain          # ties to the domain in 00-START-HERE
tier: certified                     # raw | staged | certified (see below)
domain: marketplace
description: >
  One row per rental (a client's instance lifecycle on a host).
  The canonical rental grain. Use this, not rentals_v2_final.
sla:
  freshness: "<= 30 min behind source"
  availability: 99.5%
  on_call: "#marketplace-data-oncall"
schema:
  rental_id:    { type: string,    required: true, unique: true }   # grain key
  account_id:   { type: string,    required: true }                 # FK -> dim_account
  machine_id:   { type: string,    required: true }                 # FK -> dim_machine
  started_at:   { type: timestamp, required: true }                 # event_time, UTC
  ended_at:     { type: timestamp, required: false }                # null = still active
  gpu_hours:    { type: decimal,   required: true, min: 0 }
  revenue_usd:  { type: decimal,   required: true, min: 0 }         # reconciles to ledger
governance:
  pii: false
  certified_metrics: [gmv, take_rate, active_rentals]               # served via semantic layer (ch.08)
breaking_change_policy: major-version-bump + 2-week notice
tests:                                                              # enforced in CI (ch.10)
  - not_null: [rental_id, account_id, started_at]
  - unique: [rental_id]
  - relationships: { account_id: dim_account.account_id }
Contracts only work if they have teeth

A contract that isn't enforced in CI is a wish. The mechanism that makes it real: the contract file is the single source of truth, a CI check on both the producer repo and the platform repo validates schema and tests against it, and a violation blocks the merge. Without that gate, contracts decay into stale YAML and you're back to discovering breakages in production. Chapter 10 wires the test execution and alerting; here, the discipline is "no published dataset without a contract, no merge that breaks one."

Dataset tiers & certification

Not every table deserves the same trust. The raw landing zone for the telemetry firehose and a Finance-reconciled GMV table are both "data," but depending on them carries wildly different risk. If the catalog treats them identically, analysts can't tell a battle-tested gold table from someone's abandoned scratch query. The answer is an explicit tier ladder — a visible trust signal that says how much weight a dataset can bear.

THE CERTIFICATION LADDER — trust increases as you climb ┌────────────────────────┐ │ ✅ CERTIFIED / GOLD │ exec dashboards, │ owned · SLA · tested · │ finance, board, ▲ │ contract · semantic- │ the NL/AI layer │ │ layer metrics │ ┌───────┴────┴────────────────────────┘ │ 🟡 STAGED / SILVER │ analyst day-to-day, │ cleaned, conformed, modeled, but │ dashboards w/ caveats ▲ │ no formal SLA owner │ ┌───────┴───┴──────────────────────────────────────┘ │ ⚠ RAW / BRONZE │ exploration only, │ source-faithful landing; use at your own risk; │ "is the data even │ may be late, malformed, or change without notice │ here?" questions └───────────────────────────────────────────────────┘ promotion = a reviewed PR: add owner, SLA, tests, contract, description → badge in catalog

Promotion up the ladder is a deliberate act, not a default. A table earns the certified badge by acquiring an owner, an SLA, a contract, passing tests, and a human description — reviewed in a PR, the same way code earns a release tag. The badge then renders in the catalog as a trust signal: an analyst searching for "rental revenue" sees the certified fct_rental at the top and knows it's safe to put in front of the CFO.

TierWhat it isGuaranteesWho can depend on it
Raw / Bronze ⚠Source-faithful landing zone (CDC output, raw events, raw telemetry)None. May be late, malformed, or re-keyed without notice.DPEs and DS for exploration only. Use at your own risk.
Staged / Silver 🟡Cleaned, deduped, conformed, modeled (dimensions & facts, ch. 06)Schema is stable; tested for nulls/uniqueness; best-effort freshness.Analysts for day-to-day work and dashboards that carry caveats.
Certified / Gold ✅An owned, contracted data product feeding the semantic layerNamed owner, freshness & availability SLA, CI-enforced contract, on-call.Anyone — exec dashboards, Finance, the board, and the NL/AI layer (ch. 13).
Tiers map cleanly onto the medallion you already built

If you followed the bronze/silver/gold medallion architecture in the transformation chapter, the tier ladder is the same layers wearing a trust badge. The new idea here isn't the layering — it's making the trust level visible in the catalog and gating it behind a certification review. A gold-layer table that nobody promoted is still just silver with ambitions.

How different personas access data

"Self-serve" means different things to a SQL analyst and a CFO. The platform has to meet each consumer group from Start Here on the interface they already live in — and crucially, route them all through the same certified datasets and the same semantic layer, so that five interfaces still produce one GMV number.

PERSONA → INTERFACE, all grounded on ONE certified core + semantic layer analysts ──▶ SQL / warehouse ──┐ DS ──▶ notebooks ────────┤ PMs/execs──▶ BI dashboards ────┼──▶ ┌─────────────────────────────┐ ops ──▶ reverse-ETL ──────┤ │ SEMANTIC LAYER (ch.08) │ everyone ──▶ NL / AI (ch.13) ──┘ │ certified metrics + dims │ └──────────────┬──────────────┘ ▼ ┌─────────────────────────────┐ │ CERTIFIED / GOLD datasets │ └─────────────────────────────┘ one definition of GMV, served five different ways ─ never five definitions
Persona (consumer group)Primary interfaceWhat they need from itGrounded on
Analysts (Product, Finance, Supply)SQL / warehouse consoleAd-hoc querying, joins, full SQL power over modeled tablesStaged + certified marts
ML & data scienceNotebooks (Python/Spark)Feature engineering, training data, exploration at scaleRaw → certified + feature store (ch. 12)
PMs & execsBI dashboardsGoverned dashboards, no SQL, one trustworthy number per metricSemantic layer metrics
Supply / revenue opsReverse-ETLCertified metrics pushed back into operational tools (CRM, Sheets)Certified datasets only
Trust & safety / riskSQL + low-latency viewsFresh signals, audited access to sensitive data (ch. 11)Certified + restricted domains
Everyone (incl. non-technical)NL / AI in Slack (ch. 13)"What was GMV last week?" answered correctly in plain languageSemantic layer + catalog metadata

The design rule across the whole table: the interface varies, the source of truth does not. Reverse-ETL must read the certified metric, not re-derive it; the BI tool must hit the semantic layer, not hand-rolled SQL; the NL/AI bot must resolve "GMV" through the same metric definition the CFO's dashboard uses. The moment any interface computes its own version of a governed metric, the platform has forked its truth — exactly the failure self-serve was meant to prevent.

Onboarding internal users

A self-serve platform that takes a new analyst two weeks to figure out is not self-serve. The metric that captures this is time-to-first-query — how long from "new hire's first day" to "ran a useful query against a certified dataset, alone." Treating the platform as a product (the mindset from the DPE-role chapter) means optimizing that onboarding funnel as deliberately as the product team optimizes signup.

The enablement toolkit that drives time-to-first-query down:

  • Golden paths — one blessed, documented way to do each common task: "how to build a dashboard," "how to add a dbt model," "how to query telemetry." Not ten options; one paved road that works.
  • Starter templates — a dbt model scaffold, a notebook template pre-wired to the warehouse, a dashboard skeleton. New work starts from a known-good shape, not a blank page.
  • Docs-as-code — documentation lives in the repo next to the code and ships with it, so it can't rot independently. Dataset descriptions in dbt YAML flow straight into the catalog.
  • A "Datasets 101" guide — the canonical model, the tiers, the certified marts, and the three fact grains from Start Here, in one onboarding doc every new user reads.
  • Office hours — a recurring, low-friction channel for the questions the docs don't cover. The trick is to treat each office-hours question as a signal: a recurring one means a missing golden path or dataset, and gets fixed at the source.
Platform-as-product, applied

The product mindset means the DPE team's users are internal, but treated like customers: they have a funnel (discover → understand → query → trust), they churn (give up and Slack the team), and they have a satisfaction score. Office hours, golden paths, and starter templates aren't overhead — they're the platform's onboarding flow and docs. A platform team that ships a powerful warehouse but no paved paths has built a product with no UX, and its adoption metrics will say so.

Federated ownership vs central platform

As the platform grows, one question gets sharper: who owns which datasets? The centralized model — one DPE team owns every pipeline and every table — is the ticket queue's cousin. It works early, then the central team becomes the bottleneck for every new dataset, because they're the only ones who can build one. The federated answer is data mesh: the domain teams own their data as products, and the central platform team provides the paved roads — the ingestion framework, the catalog, the semantic layer, the CI for contracts — on which domains build.

WHO OWNS WHAT — central paved roads, domain-owned products ┌─────────────────────────────────────────────────────────────────┐ │ CENTRAL PLATFORM TEAM (DPE) — the paved roads │ │ ingestion framework · catalog · semantic layer · contract CI · │ │ storage & compute · tier/certification process · access control │ └───────────┬───────────────┬───────────────┬──────────────────────┘ │ build on │ build on │ build on ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ │marketplace│ │ billing │ │ supply │ ... 6 domains │ domain │ │ & finance│ │ / fleet │ │ owns: │ │ owns: │ │ owns: │ │ fct_rental│ │ fct_meter │ │ fleet_ │ │ fct_bid_event │ │ dim_ │ │ health │ │ (products)│ │ invoice │ │ (products)│ └───────────┘ └───────────┘ └───────────┘ central owns the ROADS; domains own the PRODUCTS that ride on them

Mesh is a powerful organizing principle — but it has a real cost, and at GridDP's scale (tens to low hundreds of users, six domains, a data team of a handful), full federation can hurt more than it helps. Each domain owning a data product requires that domain to have someone who can build and operate one; spread that thin and you get six half-maintained pipelines instead of one good central one.

When mesh helps vs hurts at this scale

Helps when a domain has both deep data expertise and a backlog the central team can't service — e.g. the ML team owning feature datasets, or billing owning the ledger-reconciled marts, because nobody understands them better. Hurts when you federate ownership to teams without data capacity, fragmenting standards and re-creating the swamp. The pragmatic path at GridDP: the central team owns the roads and the shared core (the canonical model, the cross-domain marts), while a few capable domains own their own products. Federate the willing and able; don't mandate mesh org-wide because a blog post said to.

Measuring enablement

Enablement is a product, so you measure it like one. These metrics tell the DPE team whether the platform is actually getting more self-serve over time — or whether the ticket queue is quietly creeping back.

MetricWhat it measuresHealthy direction
Time-to-first-queryNew user's day-1 to first useful self-serve query↓ Down (hours, not weeks)
% questions answered self-serveShare of data questions resolved without the DPE team writing SQL↑ Up (toward 80%+)
Catalog coverage% of datasets with owner, description, and tier filled in↑ Up (toward 100% for certified)
Certified-dataset adoptionShare of queries / dashboards hitting certified vs raw tables↑ Up (trust concentrates on gold)
Ticket-volume trendAd-hoc data requests landing in the team's queue over time↓ Down per-user (and recurring ones → fixed)
Active self-serve usersDistinct humans querying / building each week↑ Up (breadth of adoption)
Watch the per-user trend, not the absolute

Raw ticket volume can rise while the platform is improving — more users simply ask more questions. The signal that matters is ticket volume per active user trending down, and the share of tickets that are genuinely novel (a new dataset request) versus recurring (a question the platform should already answer). A flat per-user trend with mostly-recurring tickets is the alarm: self-serve has stalled, and the team is sliding back into being the query API.

✓ Check yourself

Answer from memory before opening each one.

  1. Recurring Slack requests for take-rate by region keep reaching the platform team. Leadership proposes hiring a second analyst to answer them faster. What do you propose instead, and how would you know it worked?

    Show answer

    Treat each recurring question as a platform bug: a missing certified dataset, metric or golden path. Fix the cause so people answer it themselves, and watch tickets per active user fall and the share of questions answered self-serve rise. A second human query API only moves the bottleneck. See the ticket-queue anti-pattern and measuring enablement.

  2. Your catalog holds 4,000 tables, harvested automatically, and analysts still say they cannot find or trust anything. Which metadata came free, which did not, and what do you do about the second kind?

    Show answer

    Schemas, lineage and usage arrive free from harvesting. Descriptions, owners and tiers are human inputs, so an uncurated catalog is a phone book of strangers. Require owner, description and tier for certified datasets and track coverage, rather than asking everyone to document everything. See data catalog and discovery.

  3. A producing team renames a column in a source table. Compare the cost of catching it in the producer's CI with catching it in the platform's overnight pipeline, and say what the contract must contain for CI to catch it.

    Show answer

    In the producer's CI the build fails before the change ships and the person who made it fixes it, while overnight it breaks pipelines and dashboards and someone else pages. CI needs a producer-owned schema in the producer's repo, a check against it, and a breaking-change policy that sends removals and type changes through a major version with notice. A contract not enforced in CI is a wish. See data contracts, operationalized.

  4. A new table is landing useful data and an executive dashboard already depends on it. Should it be certified now? What must it acquire, and who reviews?

    Show answer

    Not yet. Certification is a reviewed promotion in a pull request that requires a named owner, a freshness and availability target, a CI-enforced contract, passing tests and a human description. Until then the dashboard is depending on an unguaranteed table, which is the risk the tiers make visible. See dataset tiers and certification.

  5. Analysts use SQL, executives use dashboards, sales uses the CRM and everyone wants to ask questions in Slack. Do you build four metric definitions? Explain what varies and what does not.

    Show answer

    No. The interface varies by persona and the source of truth does not. Every interface reads the same certified core and semantic layer, and reverse ETL reads the certified metric rather than re-deriving it. See how different personas access data.

  6. The ML team has deep data expertise and a backlog the central team cannot serve. A VP proposes mandating data mesh for all six domains. What do you argue?

    Show answer

    Federate ownership to the domains that are willing and able, such as ML owning feature datasets, and keep the central team on the paved roads and shared core. Mandating mesh at tens to low hundreds of users adds coordination cost and duplicated effort for domains without the staff to run it. See federated ownership versus central platform.

Takeaway

  • The ticket queue — the DPE team as a human query API — does not scale past a handful of analysts; self-serve is the only architecture that grows sub-linearly with GridDP's tens-to-low-hundreds of users.
  • Self-serve is a curated product: discoverable data (catalog), trustworthy data (tiers, certification, contracts), and consistent metrics (the semantic layer from ch. 08) — not raw SQL access for all.
  • The catalog harvests metadata from every store into a graph and serves search, lineage, ownership, freshness, and trust badges to humans and the NL/AI layer alike.
  • Data contracts, enforced in producer CI with an explicit breaking-change policy, are the API boundary that lets producers and consumers evolve independently.
  • Tiers (raw → staged → certified) make trust visible; certification is a reviewed promotion, not a default. Meet each persona on its own interface, but ground them all on one certified core.
  • Federate ownership to the willing and able domains; keep the central team owning the paved roads and shared core. Don't mandate mesh org-wide at this scale.
  • Measure enablement like a product: time-to-first-query, % self-serve, catalog coverage, certified adoption, and the per-user ticket trend.

Self-serve only works if the data underneath is actually correct — and stays correct. Next we make trust enforceable rather than aspirational: tests, freshness, anomaly detection, and incident response. → Data Quality & Observability

Further reading