Every number you track is a stand-in for something you actually care about but can’t measure directly. Response time stands in for “the user wasn’t kept waiting.” Test coverage stands in for “the code does what it claims.” Ticket-close rate stands in for “the problem got solved.” Nobody chooses these proxies carelessly — they’re chosen because the real thing is expensive or impossible to observe directly, and the proxy correlates with it closely enough to be useful.

The trouble isn’t the proxy. The trouble is what happens the instant the proxy becomes a target.

While a number is purely descriptive — you’re just watching it, not being judged by it — it stays honest. It moves when the underlying thing it represents moves, for no other reason, because nobody has any incentive to move it any other way. The correlation holds because nothing is pulling on the number directly.

Then someone says “get this number up,” and the correlation starts to break. Not because anyone is cheating. Because there are now two ways to move a proxy: change the thing it represents, or change the thing itself, and the second way is almost always cheaper. A ticket can be closed by fixing the problem, or by closing the ticket. A test suite’s coverage number can go up by testing behavior, or by executing lines without asserting anything about them. Response time can drop by actually being faster, or by answering with a placeholder while the real work happens somewhere the clock doesn’t reach.

Once a target exists, every cheap path to moving the number without moving the thing becomes a live option, and cheap options get taken — not out of malice, just because they’re cheap and the number is what’s being measured.

The Gap Is Invisible From Inside the System

This is the part that makes it dangerous rather than just annoying: the gap between the proxy and the real thing doesn’t show up in the proxy. That’s what makes it a proxy failure and not a proxy bug. The number can be climbing steadily, dashboards green, targets hit quarter over quarter, while the thing it was supposed to represent is flat or getting worse. You only find out when you go look at the real thing directly — read the actual tickets that got closed, trace an actual slow request instead of trusting the aggregate, read the actual test bodies instead of trusting the percentage — and by the time someone does that, the gap has usually had months to grow.

Nobody catches this by watching the metric harder. Watching the metric harder is what created the gap in the first place. You catch it by periodically going around the metric and checking the underlying thing by some other, uncorrelated method — a method nobody has had the chance to optimize against yet, because it isn’t the thing being scored.

Every Metric Has a Shelf Life

This suggests something uncomfortable: a proxy metric isn’t a fixed, reusable measurement — it’s a perishable one. It’s trustworthy for exactly as long as it takes for people (or systems) to notice it’s being scored and start routing around the hard version of improving it. After that, it keeps producing numbers, and the numbers keep going up, and it keeps meaning less each time.

The fix isn’t a better metric. There is no metric immune to this — anything specific enough to be trackable is specific enough to be gamed cheaper than the real work it stands for. The fix is treating every proxy as something you have to periodically re-validate against reality, not something you can set once and trust forever. Rotate what you check by hand. Read the raw cases behind the aggregate, not just the aggregate. Ask, on some regular cadence, “if I didn’t trust this number at all, what would I look at instead” — and then actually go look.

The number was never the thing. It just used to be close enough that the difference didn’t matter yet.