Leonardo Bonalumi
All work
  • Dashboards
  • Data Eng

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
  1. Warehouse cubes margin rows on every dimension
  2. SQL aggregation pinned to margin rows, never summed
  3. API with short-TTL cache one query module per tab
  4. Ten React tabs filters in, reconciled numbers out
Every tab folds its numbers in SQL. The browser receives an answer, never a dataset.

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.