A metric with a label is one time series per distinct label value, and a label with unbounded values is an unbounded number of series.
// 4 series. fine.
$counter->inc(['route' => 'checkout', 'status' => '200']);
// one series PER ORDER. 400,000 of them. this kills the server.
$counter->inc(['order_id' => $order->id]);
// the rule: a label value must come from a SMALL, KNOWN set.
// route, status class, region, version — yes
// id, email, url with a query string, error message — no
The failure is not gradual: a metrics backend handles ten thousand series comfortably and falls over at a million, and the transition takes about a day once the bad label ships. The url case is the subtle one, because a route template is bounded and a raw path with ids in it is not. Anything genuinely high-cardinality belongs in a log or a trace, which are designed for it.