Tribar Studio
← Back to blog
DataArchitectureGCP

Analytics users actually open: BigQuery as a product surface

Engineering Team·

Every product eventually grows an analytics page, and most of them share the same fate: three impressive charts on launch day, two visits per user in month one, zero in month two. The data was fine. The question ownership was wrong.

A dashboard answers the builder's questions — "what can this data show?" A product surface answers the user's questions — "what should I do next?" Getting from the first to the second is an architecture problem, not a charting problem.

Start from decisions, not datasets

Before a single query is written, list the decisions the screen exists to support. A cold-chain operations screen supports things like: Which shipments need attention right now? Is last week's excursion cluster a device problem or a process problem? Are we trending toward compliance risk this month?

Each decision becomes one question the surface can answer, with an explicit freshness requirement. "Current state" and "last week's pattern" are different pipelines with different costs — conflating them is how dashboards get slow and expensive at the same time.

The three-layer pattern

The shape we use in client work separates concerns by time horizon:

raw events → rollup tables (hourly/daily) → semantic views → the product surface
  • Raw events stay in a partitioned, retention-bound table. Nobody queries it directly from the product.
  • Rollups are scheduled aggregations — hourly averages, daily totals, excursion counts — computed once, read many times.
  • Semantic views encode the business language: what counts as an excursion, what "active" means, how shifts are bounded. Views are versioned and reviewed like code, because that is what they are.

The product surface only ever reads views and rollups. When a number looks wrong, there is exactly one place to look: the view definition. Debugging a chart that computes its own SQL in application code is the alternative, and we have all lived it.

Freshness is a product decision

"Weekly numbers update at 06:00 UTC; today's numbers refresh every 15 minutes" is not a technical footnote — it is part of the product contract, and it should be visible in the UI. Users forgive stale data that admits its age. They do not forgive live-looking numbers that silently lag.

That honesty also buys architectural freedom: it is very hard to make everything real-time, and almost never necessary. Most decisions need yesterday, complete and correct — not this second, approximate and expensive.

Instrument the surface itself

Analytics pages need analytics. The questions worth answering about the surface: Which view gets opened most? Which charts are never looked at (delete them — they cost money and attention)? How long until a user finds the number they came for?

The last one is the quiet quality metric. If users export to a spreadsheet to finish the job, the surface has told you exactly where the next feature goes.

The takeaway

Ship analytics the way you ship features: start from the user's decision, make freshness a visible contract, keep raw data away from the product path, and maintain the semantic layer like the code it is. The charts are the easy part — question ownership is the product.