A KPI system you can trust
Launch a new business metric without writing code. One shared engine reads each metric from configuration. Screens and AI agents get the same answer.
The problem
Every new metric meant new code, a review and a release. Hundreds of metric functions had grown up this way. The same metric was often written more than once, in slightly different ways. Nobody could say which version was right. Nobody could easily tell which tables, columns and filters a metric used.
The old engine had limits that teams worked around. It could group by only two fields, so reports glued columns together to fake a third. Each team built its own workaround for these limits.
Constraints
- Strict data access rules. A user sees only the rows and columns they are allowed to see, and the engine must never widen that.
- Many teams in several regions ship metrics at the same time.
- Existing dashboards must keep working while the engine underneath changes. A number may move only where the old engine was wrong.
What was built
- Metrics as configuration. One row says what to count, on which data, with which stored filters and which formula. A caller sends the metric code, its own filters and the shape it wants. It never writes a formula.
- A metric as a small tree of values. Each value is one count, sum or average. Pairs of values combine with a few operations: add, subtract, multiply, divide, percent, growth and share of total.
- Growth on any shape. A card, a trend or a two-way table can compare each cell with the same cell in the prior period. It can also compare it with the same period last year. Rolling windows, year to date and quarter to date are built in.
- Filters that only narrow. The metric’s own rule, the user’s choice and the access hierarchy all apply together. The old engine merged filters on the same field with OR, so a user’s choice could make a metric bigger.
- Honest answers. A conflict returns an empty result with a message, and the message never changes the number. Bad settings fail with a reason, never a silent zero. Results built on too few rows are refused. A debug mode shows the filters and queries used.
- One library, one contract. The engine is one shared library. Each team adds a thin layer of its own settings and takes new versions on its own schedule. Screens get the same shape every time, so a new metric needs no front-end change.
- A safe path off the old engine. Each old function becomes a configuration row. The row is compared with the old function on the same filters. Every number that moves is listed with its reason, and the metric’s owner signs off before callers switch.
Key decisions
- Config over code. A metric is data, not a program. Duplicates become visible, and review moves to a definition that people can read.
- Fix it once. The workarounds move into the engine. Fix a fault in the library, and every team gets the fix.
- Narrow, never widen. A filter can only make a metric smaller. That makes a whole class of wrong numbers impossible.
- Fail loudly. An empty or refused answer says why. A silent zero looks like data and misleads people.
- Tests before code. The tests came first and defined a correct answer. Code was done when they passed and the answers matched the old engine, or each difference had a reason.
Where it is now
The new engine is in beta, and the general launch is on the roadmap. Teams are moving their metrics across one at a time, with each change listed and agreed. In our tests, complex metrics answered much faster, because the engine sends a metric’s queries at the same time, not one after another. Each definition shows which tables, columns and filters it uses.
An unplanned benefit: a safe base for AI
The engine was not designed for AI, but it fits. An agent that writes its own query must guess the formula, the filters and the dates. Its answers can drift. An agent that calls the engine only has to find the right metric in an approved list. Then it fills in a request: filters, shape, period and growth. The engine does the maths, so the agent gives the same number as the dashboard.
The agent can also answer views that nobody configured, such as growth against last year on a governed metric. Drafting new metric rows with agents, tested by the same checks, is on the roadmap.
What to do differently next time
Build the side-by-side checks and a test request for every metric first, before the new engine. Many metrics had no test request, and finding that out late slowed the move.