npx skills add ...
npx skills add samber/developer-platform-skills --skill sql-jdbc-access-design
Design customer-facing SQL access to a product's data - a JDBC/ODBC endpoint, warehouse share, or hosted query surface. Covers the architecture gate by scan frequency (zero-copy share, replicated copy, per-tenant compute isolation), engine-level tenant isolation with secure views and row-level security, an additive-only schema-stability contract enforced in CI, BI-tool connectivity and driver certification, query governance and cost caps, short-lived credential issuance, and the pricing shape. Use whenever the user mentions JDBC, ODBC, SQL access for customers, connecting Tableau or Power BI to product data, or pricing a SQL add-on - even if they never say "SQL access". Do NOT use for bulk file delivery - use samber/developer-platform-skills@bulk-data-sharing-design instead.
npx skills add samber/developer-platform-skills --skill sql-jdbc-access-design
You are a data-platform product designer. Design how a SaaS product exposes its own hosted data to its customers through live SQL - a warehouse share, a read endpoint, or a JDBC/ODBC handle a BI tool connects to - so the surface is isolated, stable, governed, and priced before the first customer connects.
The stakes are asymmetric: an API bug returns a wrong response, but an isolation bug on a live SQL handle returns another customer's rows, and a runaway customer query lands on your infrastructure bill.
The demand side, in a16z's framing: applications are increasingly rebuilt warehouse-native, so governed SQL access is becoming a product category, not a niche feature - analyst positioning for the "why now", not evidence for any implementation choice below.
Memory (advised): When memory lives in a file, consider using developer-platform-context.md; if a different memory system is in use, rely on that instead. The file is an advisory reference, not a mandatory requirement. Separate task info in different sections. Remove finished tasks. Add a date to a task; no date for general project context. Some interview responses may differ between 2 tasks.
Ask these before designing anything; each answer changes a later step. Batch them - this is a tactical design task, not a strategy interview.
psql and notebooks, or an embedded app or agent issuing queries? (drives the BI-connectivity budget)If your harness has persistent memory, store the design's settled decisions (architecture rung with its promotion condition, isolation mechanism and identity function, schema-stability contract and what counts as breaking, credential ladder position, governance caps, pricing shape) so later runs - a new consumer type, a schema change, a repricing - start from the design instead of re-deriving it.
Two splits do the work:
psql, dbt, or a notebook tolerates rough edges but pushes the largest queries.Design for the most demanding consumer type present, and gate the architecture on measured or honestly-estimated scan frequency - never on the org chart of who bought the product.
Each step has a section below, in order. Steps 3 and 6 must both pass their gates before any customer gets a handle; everything else can stage.
Three architectures, ranked. Scan frequency and where the data already lives decide which one wins - not taste.
efficiency: zero-copy share > replicated copy or read endpoint > per-tenant compute isolation
effort: per-tenant compute isolation > replicated copy or read endpoint > zero-copy share
value: per-tenant compute isolation > zero-copy share > replicated copy or read endpoint
Default rung: zero-copy warehouse sharing - when the data already lives in a shared cloud warehouse and customers scan infrequently. The share is a metadata grant, not a copy: lowest build cost, always-fresh data, governance stays centralized at the source. The cost model is the catch: live access is paid per query scan (plus cross-region egress), so the economics hold only while scans stay infrequent.
Step to a replicated copy or dedicated read endpoint when scan frequency inverts the arithmetic.
Promote to per-tenant compute isolation - each tenant gets its own query engine (a per-tenant instance over shared storage, fronted by one endpoint). This is the starved option:
This ranking is a default, not a law. Re-rank against the interview:
Isolation must live in the query engine, never in application code. An app-layer WHERE tenant_id = ? filter is a named failure class for this surface: with an external SQL handle there is no application layer left to intercept the query, so one missed filter - ever, anywhere - leaks another customer's rows. Engine-level policies make a query that omits the filter return nothing instead.
current_account(), Databricks Delta Sharing's current_recipient(). The base and mapping tables never enter the share.CURRENT_ROLE() and CURRENT_USER() return NULL inside a secure view once it crosses an account-to-account share. A policy written against them silently matches nothing - or worse, everything, depending on the predicate's shape. Only the share-aware identity functions are safe across a share boundary.FORCE ROW LEVEL SECURITY, and inject tenant identity into the session context server-side from a verified token - never from client-supplied input. RLS has four well-documented failure modes (policy recursion, missing WITH CHECK, permissive-policy OR-composition, owner-privileged view bypass) plus performance rules; work through references/rls-failure-mode-catalog.md before writing any policy.The moment a customer's dashboard queries your tables, your schema is an API: columns are fields, tables are endpoints, their dashboards are clients. The asymmetry versus API versioning is that schema changes often happen accidentally - a source-system update, a readability rename - and propagate downstream with no deprecation header to warn anyone.
Never expose the live operational schema. Serve a curated mart layer replicated from the system of record, so internal schema churn stops at the replication boundary instead of landing in customer dashboards.
Adopt the additive-only rule: add columns and tables freely; never rename, retype, or drop in place. Evolve via expand/contract:
Amplitude's event schema is a shipped existence proof: it only ever adds tables and columns as new event types arrive, staying backward-compatible by construction.
Enforce the contract in CI, not by convention. A contract that only lives in a doc is a hope:
datacontract CLI validates a contract against a PR before merge.Write the contract in four parts, because a BI dashboard, a dbt job, and an embedded app each depend on a different subset:
When a breaking change is genuinely unavoidable, it exits through deprecation machinery with notice periods - the versioning sibling skill governs that; this contract defines what counts as breaking.
BI-tool support is a certification tax, not a one-time integration - budget it as ongoing engineering, and let question 5 decide how much of it you buy.
The cheapest rung, when your engine allows it: speak an existing wire protocol. A Postgres wire-protocol endpoint gets every BI tool, psql, and every ORM for free, because they all ship a Postgres driver - this is exactly why PostHog fronted its per-tenant engines with one. It costs you dialect fidelity: you support the Postgres dialect and nothing exotic.
A custom dialect is the anti-pattern this rung exists to avoid: PostHog names its own HogQL as a reason direct exposure was a non-starter - "not a language that's already widely supported" means every BI tool integration starts from zero.
Shipping your own driver means:
Details and the certification requirements per tool: references/bi-connectivity-and-credentials.md.
Publish a connection-setup page per supported client. An undocumented connection string is a support ticket per customer:
None of the warehouse cost controls are on by default; every one must be configured before GA, per external warehouse or engine. This differs from API rate limiting in enforcement primitive: you are capping compute seconds and spend on a long-running engine, not counting requests at a gateway. Design it here, and keep request-style limits (connection counts, queries/minute at the endpoint) consistent with the API sibling skill's policy.
The industry is retiring static database passwords - Snowflake enforces this through a staged deprecation schedule that moves all service users onto key-pair, OAuth, PAT, or workload identity - so design credential issuance on that trajectory rather than grandfathering passwords in.
Preference order: short-lived token > managed secret > static password; inline credentials in a connection string are the anti-pattern no documented path should show.
Package SQL access as a paid tier or add-on, never a free feature. The shipped precedents (Stripe Sigma, Heap Connect, Mixpanel Data Pipelines) show customers pay for exactly this capability, and a price is itself a governance control on a surface that is expensive to over-serve. Three shapes, ranked:
efficiency: flat add-on or tier gate > consumption / volume-tiered > provider-pays-compute passthrough
effort: consumption / volume-tiered > provider-pays-compute passthrough > flat add-on or tier gate
value: consumption / volume-tiered > provider-pays-compute passthrough > flat add-on or tier gate
Default rung: the flat add-on or tier gate (Heap Connect, Mixpanel's shape) - SQL access unlocks at a higher plan or as a fixed-price add-on. Near-zero billing engineering; the gate itself throttles casual abuse. Its risk is the unbounded heavy user whose compute costs exceed the flat price - acceptable only with step 6's caps in place.
Promote to consumption / volume-tiered when your own cost exposure is consumption-based or customer data volumes span orders of magnitude. This is the starved option:
Provider-pays-compute passthrough is less a free choice than the posture that arrives with reader accounts and marketplace listings: you pay the compute, then invoice cost plus margin. Take it only with a resource monitor on every account (step 6) - and note the consumer-pays alternative exists at the same rung: HubSpot's Snowflake share bills compute to the customer's own account, gated to its top tier.
This ranking is a default, not a law - re-rank against question 7 and the interview trio:
Pricing precedents with their point-in-time figures: references/vendor-case-studies.md - verify current pricing before quoting any of them to a customer.
Anti-pattern checklist - each is a direct audit finding:
WHERE clause instead of engine policy.CURRENT_ROLE()/CURRENT_USER() (NULL across shares) instead of the share-aware identity functions.WITH CHECK, OR-composition, view bypass): the full catalog with fixes is in references/rls-failure-mode-catalog.md.Iterate until all three gates pass; they are gates, not aspirations. The trends steer pricing and architecture revisits afterwards.
samber/developer-platform-skills@etl-connector-strategy - being a source connector on other people's ETL platforms; this skill is the customer querying you directly.samber/developer-platform-skills@api-integration-surface-strategy - the umbrella decision of whether SQL access should be offered at all, among the other integration surfaces.samber/developer-platform-skills@api-auth-key-management - key formats, rotation UX, and secret hygiene for API credentials; step 7 applies the same discipline to database handles.samber/developer-platform-skills@api-versioning-policy - deprecation machinery and notice periods; step 4's contract defines what counts as breaking for the schema, that skill governs how a breaking change is announced and retired.samber/developer-platform-skills@api-rate-limit-policy - request-counting limits at the API gateway; step 6 caps compute and spend on a query engine instead, and the two published policies must not contradict each other.