Games KPI Cockpit
A ten-tab replacement for a beloved BI workbook that had become too slow to open. Same definitions, same numbers, none of the licence.
- Role
- Requirements, data modelling, and build
- Context
- RCS MediaGroup
- Period
- 2026
- Stack
-
- Python
- FastAPI
- React
- Vite
- Recharts
- BigQuery
- Warehouse cubes margin rows on every dimension
- SQL aggregation pinned to margin rows, never summed
- API with short-TTL cache one query module per tab
- Ten React tabs filters in, reconciled numbers out
Objective
The games vertical was analysed through a BI workbook that had grown to the point where opening it was a coffee-break activity. It also could not be shared outside the licensed seats, which meant the people who ran the product were reading screenshots.
Requirement: same metrics, same definitions, in a browser, fast, for everyone.
Approach
The interesting work was not the UI — it was archaeology. Every tile in the original workbook encoded a definition somebody had agreed to years earlier, and a rewrite that quietly changed one would destroy trust in all ten tabs.
Two examples of what that means in practice. “Number of play sequences” turned out to be
the average of daily unique visitors at a given position, not a sum over the period —
so the default aggregation had to be an average, with the sum available as a toggle. And
several source tables are cubes with TOT margin rows on every demographic dimension:
summing the breakdowns overcounts by roughly fiftyfold. Unfiltered dimensions get pinned
to their margin row instead. Where the upstream extraction produced two twin margin rows
with different values, I reproduced what the original tool did — average them — rather
than pick the one I preferred.
None of that is elegant. All of it is why the numbers reconciled.
Architecture
The service queries the warehouse directly through its storage API, one query module per tab, always aggregating in SQL and never shipping raw rows to the browser. Results are memoised in process with a short TTL, which is enough given how often the underlying tables refresh.
Filters arrive as repeated query parameters and are bound as query parameters, never interpolated; identifiers are only ever taken from a whitelist of allowed dimensions. Cohort snapshots are pinned to a single day by default — averaging users across a date range would count the same person several times.
What I owned
Reconciling the definitions with the people who relied on them, the data modelling against the warehouse, the ten tabs, the design system implementation in light and dark, and the Excel export that mirrors the on-screen tables. Deployment was data engineering’s.
Outcome
The workbook was retired. Product and editorial now read the same ten tabs in a browser, and the reconciliation exercise produced something unplanned: a written definition for every metric, which had previously lived only inside the BI file.