Nullhaus
4–6 minutes

Drift is a product event before it is a metric

A dark, empty members club interior at night, lit only by a faint warm gold rim light

The standard drift story goes like this: you monitor the input distribution, a statistical test flags a shift, you retrain, the metric returns to baseline. It is a tidy loop and it is genuinely useful. It is also almost never how anyone finds out that their model has stopped working.

What actually happens is that support volume creeps up in one category. A power user mentions in passing that they have started double-checking the output. Retry rates on one endpoint climb four percent and get attributed to a flaky client. Six weeks later somebody opens the drift dashboard for an unrelated reason and finds that a feature has been shifting since March.

The distribution metric was not wrong. It was just the slowest observer in the building.

Three different things get called drift

Conflating them is why teams reach for retraining as a reflex, and why retraining so often fails to help.

Covariate shift. The inputs changed, the relationship between input and correct output did not. Your fraud model now sees traffic from three countries it barely saw before. Retraining genuinely helps here, because the model simply lacks density in the new region of the space.

Concept shift. The inputs look similar, but the correct answer changed. A refund policy was rewritten, so transactions that were legitimately flagged in January are legitimate in June. Retraining helps only if your labels reflect the new policy, and labels usually lag policy by exactly as long as it takes someone to notice.

Population shift. The users changed. Not the data, not the labels: who is showing up and what they are trying to do. This is the one that reads as a modelling problem and is actually a product event, and it is the most common of the three in any product that is still growing.

A model tuned on early adopters who tolerated ambiguity will look like it degraded the month a mainstream cohort arrives. Nothing degraded. The bar moved, because the new cohort will not do the work of interpreting a hedged answer.

Statistical drift monitoring watches the data. The failure it is trying to catch happens to people.

The earliest reliable signal is a workaround

Users do not file a ticket saying the model is miscalibrated. They adapt, silently and immediately, and the adaptation is measurable if you decided in advance to measure it.

The behaviours worth watching, in rough order of how early they appear:

  • Immediate re-query. The same user asks a near-identical question within thirty seconds. They did not get what they needed and they are rephrasing. This is the fastest signal you will get and it costs nothing to compute.
  • Result abandonment. The answer rendered, nobody acted on it, the session ended. Distinguish this from a satisfied read, which is hard, but the trend is informative even when the absolute number is not.
  • Manual override rate. Wherever a human can accept or edit the model’s suggestion, the edit rate is the single most honest quality metric in the product. It needs no labels and no annotators.
  • Escape to a different tool. The user copies the output and pastes it somewhere. Or opens the raw data view immediately after seeing the summary. They are verifying you.
  • Support tickets that mention the feature without complaining about it. “How does the ranking decide this?” is a quality signal disguised as a documentation request.

None of these require a labelled evaluation set, which is the point. They are available on day one, in production, for the actual population you serve rather than the one your test set describes.

Instrument the decision, not the inference

Most model telemetry records the inference: input hash, latency, model version, confidence. Useful for debugging, nearly useless for detecting drift, because it says nothing about whether the answer was any good.

The event you want has the decision and its aftermath in one record:

event            suggestion_resolved
model_version    ranker-4.2.1
cohort           signup_month=2026-05, plan=free
suggested        [ item_a, item_b, item_c ]
resolution       edited          # accepted | edited | ignored | abandoned
edit_distance    2               # how far the human moved it
time_to_resolve  41s
re_query_within  false

With cohort on the event you can answer the question that separates the three drift types: did quality fall for everyone, or did the mix of users change while each cohort stayed flat? That single breakdown will save you more wasted retraining cycles than any statistical test.

Retraining is one response out of four

When quality drops, retraining is the expensive option and frequently the wrong one. The cheaper responses are worth trying first, and sometimes they are simply correct.

Change the threshold. If a new cohort is less tolerant of wrong answers, abstaining more often is a better product than being confidently wrong more often. This is a configuration change, deployable in an hour.

Change the interface. Population shift is often best answered in the interface rather than the model. Showing the two runner-up options, or the evidence behind a ranking, converts a wrong answer into a useful starting point. The model did not improve; the product did.

Change the routing. If one segment is systematically poorly served, route it somewhere else: a different model, a rules path, a human. Uniform treatment is a modelling convenience, not a product requirement.

Then retrain, once you know which of the three shifts you have and therefore what data would actually help. Retraining on the last ninety days without answering that question mostly re-learns the same model at a different point in the noise.

Why this keeps getting missed

Because drift monitoring is owned by whoever owns the model, and the behavioural signals live in the product analytics stack, owned by someone else, in a different schema, reviewed in a different meeting. The data exists. The join does not.

The cheapest fix is not a tool. It is putting model_version on the product events and cohort on the model events, so the two halves can be queried together by whoever gets there first. Do that and drift stops being a quarterly discovery and becomes something you notice in the week it starts.


Respond

Corrections are welcome.

Nullhaus keeps a library, not a comment thread. If something here is wrong, out of date, or simply worth arguing with, send it. Substantive corrections are folded into the piece itself, with credit if you want it.

← Back

Thank you for your response. ✨

Received. Corrections are read by a person, and if this changes the piece, the piece changes.

Or write directly to contact@nullhaus.org



Everything Nullhaus publishes is free to read and free to reuse with attribution. Browse the whole library or join, free.

Discover more from Nullhaus

Subscribe now to keep reading and get access to the full archive.

Continue reading