Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Metrics

Business metrics, declared rather than instrumented. Nobody writes code to fill these in — which is the point, because the person who wants the number is rarely the person who can deploy a change to get it.

# modules/sales/metrics.yaml
metrics:
  - name: sales.orders.written
    description: Orders created or changed, by the status they ended up in.
    entity: sales.Order
    on: [create, write]
    labels: [status]

  - name: sales.orders.cancelled
    entity: sales.Order
    on: [create, write]
    when: status = 'Cancelled'

Three orders later, in whatever the platform team runs:

sales.orders.written_total{status="Placed"}     2
sales.orders.written_total{status="Cancelled"}  1
sales.orders.cancelled_total                    1

The fields

FieldWhat
nameLower case, dot-separated. Namespace it yourself
entityWhose commits it counts
oncreate, write, delete. Empty counts nothing
whenOptional condition on the committed row
labelsAttributes to break down by. Enums and booleans only

when uses the same expression language as a row constraint, but evaluated against the row that just committed rather than compiled into a query. So no traversal (Customer/tier) and no $currentUser: there is no query to traverse in, and no request to ask about. Validation refuses both rather than letting them silently never match.

Counters are emitted after the transaction commits, so a rolled-back write never appears.

Why labels are so restricted

A label value is part of a time series’ identity. Ten statuses is ten series; ten thousand customer names is ten thousand, multiplied by every other label, and again by every replica. The failure is slow, expensive, and lands on the platform team rather than on you.

So a label must be an enum or a boolean — the only types whose value set your model declares, and therefore the only ones whose cardinality can be checked by reading the file:

error[M210]: `companyName` is a `string`, which has no bounded set of values
  --> modules/sales/metrics.yaml (metrics[0].labels)
  help: a label value becomes part of a time series' identity, so only `enum`
        and `boolean` attributes may be labels…

The model does not load. That is deliberate: the alternative is discovering it in production, on the day of the traffic that caused it.

When the restriction is in your way

You want a breakdown by an enum on another entity — revenue by customer tier, counted on Order. Not available: a label cannot traverse. Denormalise the enum onto the row being written. That is a real cost and it is visible in your model, which is better than a metrics bill that is not.

You want a breakdown by something high-cardinality — by customer, by order number. Then the answer is not a metric at all. Every write is already in the audit stream with its entity and row identifier, and that goes to a log backend, which is built for exactly this. Ask your platform team for it.

What is not here yet

Counters only. No gauges, no histograms, and no value: naming an attribute to sum — so “orders placed” works and “revenue booked” does not. A metric on a delete can count but cannot label, because there is no row afterwards.