Your ML System Is Lying to You (Unless You Do This)

Why this matters this week
Over the last 12–18 months, many teams quietly crossed a line:
- ML systems moved from “nice-to-have” features to core production dependencies.
- Models now sit on critical paths: search, recommendations, fraud, routing, customer support.
- Inference costs have started to show up as a line item in finance reviews.
What changed this week? Not a flashy paper—budget reviews.
Several orgs I’ve spoken with in the past month hit the same pattern:
- Cloud spend reviews flagged “ML / GPU / inference” as the fastest-growing cost center.
- Product metrics (conversion, latency, CSAT) are now objectively worse than last year, despite “better” models.
- Nobody can confidently answer:
“Is this model still working as intended in production?”
If you can’t answer that in under 5 minutes with a dashboard and a few queries, you don’t have a production ML system. You have a lab experiment that escaped.
Today’s post is about applied machine learning in production: evaluation, monitoring, drift detection, feature pipelines, and cost/perf trade-offs—mechanisms, not aspirations.
What’s actually changed (not the press release)
Three concrete shifts have raised the bar for ML operations:
-
Data volatility has increased
- User behavior is moving faster: new channels, new spam patterns, new content types (especially with generative models in the wild).
- Regulatory and product changes alter labeling rules mid-stream (privacy constraints, policy updates).
- Result: training data distributions get stale faster; drift windows shrank from “yearly retrain” to “monthly or weekly”.
-
Model complexity and coupling increased
- Feature pipelines pull from more systems (logs, events, external APIs).
- LLM-based components are being chained with existing classifiers, rankers, and rule systems.
- Result: more places to silently break. A small schema change now ripples across multiple models and services.
-
Inference cost is now impossible to ignore
- LLMs and large deep models are orders of magnitude more expensive than classic gradient-boosted trees.
- Product leaders can now ask: “Why is this endpoint 20× more expensive than it was last year?”
- Result: ML teams must justify marginal model performance in dollars per request and milliseconds of latency, not leaderboard points.
None of these are solved by buying “ML monitoring SaaS” alone. The new work is about operating ML with the same rigor as databases and APIs.
How it works (simple mental model)
Think about your production ML system as four coupled loops:
-
Prediction loop (online path)
- Request comes in → features computed → model predicts → response returned.
- Observability focus: latency, availability, feature health, per-request cost.
-
Feedback loop (labels & outcomes)
- User interacts with the product post-prediction (click, buy, fraud discovered, ticket closed).
- Those outcomes become delayed, noisy labels.
- Observability focus: label coverage, delay distributions, label quality.
-
Learning loop (training & retraining)
- Training data assembled from past predictions + context + labels.
- Models trained, evaluated, and promoted via explicit policies, not vibes.
- Observability focus: train/test splits, offline metrics, evaluation drift over time.
-
Drift & governance loop
- Continuous monitoring of:
- Input distribution shift (feature drift)
- Relationship change (concept drift)
- Model behavior shift (prediction drift)
- Observability focus: alerts tied to business metrics, not just KL divergences.
- Continuous monitoring of:
A production-ready system has:
- Explicit contracts between these loops (SLIs/SLOs, schemas, and promotion criteria).
- Shared dashboards where product, infra, and ML teams can see the same truth.
- Back-pressure mechanisms: what happens if labels stop flowing? If costs spike? If a feature breaks?
If any of these loops are implicit instead of explicit, you will eventually get blindsided.
Where teams get burned (failure modes + anti-patterns)
1. “Offline-only” evaluation
Pattern:
- Team ships a model that did great on a validation set.
- Six months later, business KPIs are flat or worse.
- Nobody ran controlled experiments or tracked impact post-launch.
Failure modes:
- Train/test contamination due to leaky pipelines.
- Offline validation target doesn’t match actual business objective.
- Hidden dataset shift: training data is from last year’s product design.
Anti-pattern: “We compared ROC-AUC and it went up, so we shipped.”
What to do instead:
- Treat offline eval as gate 0, not the final gate.
- Require an A/B test or shadow deployment for major models.
- Track aligned online metrics: revenue per session, fraud dollars prevented, CSAT, etc.
2. Metric monoculture
Pattern:
- Everything is optimized for a single metric (e.g., CTR or accuracy).
- No guardrail metrics, no long-term checks.
Real-world example:
- A recommender team optimized hard for short-term CTR.
- Users ended up in “clickbait traps,” engagement looked great.
- Over 6 months, retention dropped; users were less satisfied but more addicted.
- By the time they noticed, it was a product and brand problem, not a model problem.
Anti-pattern: “We track one metric, it’s simpler.”
What to do instead:
- Always define:
- Primary metric (what you’re directly optimizing)
- Guardrail metrics (what must not regress: latency, retention, complaint rate, abuse rate)
- Sanity metrics (data health, label coverage)
3. Feature pipeline entropy
Pattern:
- Feature code is scattered: some in batch ETL, some in real-time services, some in notebook scripts.
- Training features and serving features diverge over time.
Real-world example:
- Fraud model trained using a complex engineered feature:
"avg_txn_amount_last_30_days_normalized". - The online pipeline had a subtle bug and effectively used 7 days.
- Offline performance: excellent. Online: far worse. It took months to diagnose.
Anti-patterns:
- Duplicated feature logic in Python notebooks + production code.
- “We’ll clean the feature store later.”
What to do instead:
- Define features as code in a single place; use that for both training and serving.
- Enforce schema versioning: every feature has a type, owner, and deprecation policy.
- Monitor feature null rates, cardinality, and ranges in production.
4. No cost/performance contract
Pattern:
- Model complexity and infra scale linearly with ambition.
- No ceiling on inference cost; latency creep is tolerated because “it’s ML”.
Real-world example:
- Support automation team replaced a tuned intent classifier + FAQ ranker with an LLM for all tickets.
- Automation rate improved slightly (~4–5%), but:
- Latency jumped from 200ms P95 to 3s P95.
- Cost per ticket grew ~30×.
- When infra did a cost review, they had to heavily throttle usage and backtrack.
Anti-pattern: “We’ll optimize later; let’s just get it working.”
What to do instead:
- For each endpoint, document:
- Max acceptable P95 latency
- Max acceptable cost per 1K requests
- Minimum measurable improvement required to justify a more expensive model
- Compare alternative models in cost-normalized terms:
- “+1% uplift for +5× cost” may not be acceptable.
- “-0.5% uplift for -10× cost” might be a win.
5. Drift detection theater
Pattern:
- Team implements clever statistical drift monitors (KS tests, population stability index).
- Alerts fire constantly, get muted, and are ignored in practice.
Real-world example:
- A risk scoring system had an elaborate drift dashboard.
- In production, alerts fired weekly due to seasonality and product launches.
- Nobody tied drift to actual business failures, so the system trained everyone to ignore it.
- When a real distributional change hit (new user acquisition channel), fraud cases spiked and went unnoticed for weeks.
Anti-pattern: “We have drift detection, box checked.”
What to do instead:
- Only alert on drift that:
- Is persistent (not just weekend/holiday effects)
- Correlates with metric degradation (e.g., fraud loss, error rate)
- Maintain post-mortems that link drift episodes to actual impacts.
Practical playbook (what to do in the next 7 days)
The goal isn’t “perfect MLOps.” It’s to reach basic operational competence fast.
Day 1–2: Establish a minimal scorecard
For each production model, write down (in a doc or repo):
- Owner(s) and on-call rotation.
- Primary business metric(s) it is intended to move.
- Key constraints: max cost per 1K predictions, P95 latency budget.
- Current best-known metrics:
- Offline: AUC/F1/NDCG/etc.
- Online: A/B test results, or at least pre/post comparisons.
This clarifies what “good” means and exposes orphans.
Day 2–3: Ship the “boring but critical” dashboards
For each model, build one shared dashboard with:
- Traffic & availability: requests per second, error rate, latency histogram.
- Feature health for top 10 features:
- Null percentage
- Cardinality
- Basic distribution (mean/std or bucket counts)
- Prediction health:
- Score distribution over time
- Class balance (for classification)
- Cost:
- Requests × per-request cost (even if rough) → daily cost.
This alone surfaces 30–50% of latent issues.
Day 3–4: Add basic drift monitoring with clear thresholds
Start simple:
- For numeric features: track moving mean and standard deviation, compare vs training baseline.
- For categorical: track top-k category frequencies.
Set thresholds based on relative change, not just stats:
- E.g., alert if:
- Any key feature’s mean shifts >20% for >24h.
- New category appears in top-10 that wasn’t in training.
Connect drift dashboards to business metrics on the same screen so engineers can reason about impact.
Day 4–5: Introduce an evaluation & rollout policy
Write a one-page doc defining:
- When
