Skip to content
ML Academy

Core day 19 of 60 · Week 4 · Part I · application depth

MLOps Foundations

A no-show model has run for eight weeks. Last night the input monitor fired (PSI 0.5 on lead time) and the on-call engineer wants to retrain. Which hops from data to decision should you be able to trace, which signal shows the model is hurting decisions, and why can a monitor be loud and wrong, or silent and wrong?

By the end: You trace a prediction from data to decision, say what must be recorded and who owns each hop, distinguish training-serving skew from data drift and concept drift, and choose monitors and a rollback path when labels arrive late.

At a glance

A production model is a chain of hops from data to decision; make each one recorded, released in stages, monitored by timing and reversible.

  • A path

    Data, features, training, registry, serving, decision: each hop has an owner and a record.

  • Skew is a bug

    Same facts, two feature definitions: visible from the first request and fixed in code.

  • Drift is not harm

    Input alarms show movement; only matured outcomes show whether decisions got worse.

  • Release in stages

    Offline gate, shadow, then a canary against a control with stop rules agreed in advance.

  • Roll back by alias

    Production points at an alias; reverting re-points it to a retained, tested version.

Recall · 5 min

Without notes: (1) which timestamps does a data contract need besides the value itself (Day 2)? (2) why must a fitted preprocessing object be shipped with the model (Day 9)? (3) what is the first test for a segment whose error suddenly rises (Day 18)? Then clear your due review cards.

Open due reviews

Sign in to record this block.

Concept · 10 min

A model in production is a system, not a file: source tables, feature code, a trained artifact, a serving path and a decision. Each hop has an owner and can fail without an error message, because a broken model still outputs plausible numbers. MLOps is making every hop reproducible, observable and reversible.

Input drift fires early but may be harmless; concept drift is silent until labels arrive

Predict first

Only the relationship between lead time and no-show changes; the lead-time distribution stays the same. Which signal moves first?

Text description of the figure

A daily timeline (150 days, drift from day 50) with a drift-type switch (covariate, label, concept), controls for drift speed, label delay in days and PSI threshold. Four panels show input PSI, input KS, prediction PSI and delayed log loss, and a readout names which alert fires first. With covariate drift the input monitors fire first; with concept drift only the delayed performance panel moves, after the label delay.

What a production model needs besides the model Overview
  • A contract

    Inputs with meaning, event and load time, freshness and an owner.

  • One feature definition

    Shared by training and serving, computed as of decision time.

  • A registry and an alias

    Versioned models; production points at an alias, so rollback is one change.

  • A staged release

    Shadow, then a canary against a control, with stop rules set in advance.

  • Monitors with known timing

    Fast monitors see inputs; only matured outcomes see harm.

  • A way back

    The previous version retained and tested, and a fallback rule when all else fails.

The path from data to decision, and back Diagram
Source tablescontract: schema,freshness, loadtimeFeaturepipelineone definition, asof decision timeTrainingpipelineversioned data,code, configPasses thegate?beats incumbentRegistryversions + aliaschampionServinglogs servedfeatures, scores,versionDecisionapprove, price,offer, flagMonitorsquality, drift,matured outcomesHarm on maturedlabels?yesaliaslogsretrain
  1. Source tablescontract: schema, freshness, load time
  2. Feature pipelineone definition, as of decision time
    • to Training pipeline
  3. Harm on matured labels?
  4. Training pipelineversioned data, code, config
    • to Passes the gate?
  5. Monitorsquality, drift, matured outcomes
    • back to Harm on matured labels?
  6. Passes the gate?beats incumbent
  7. Registryversions + alias champion
  8. Servinglogs served features, scores, version
    • logs: back to Monitors
  9. Decisionapprove, price, offer, flag

The gate compares with the incumbent overall and by slice; an alert without matured harm is fixed in code, annotated or rolled back, not retrained. Serving calls the same feature code as training. Rollback is not a retrain; it re-points the registry alias to the previous version.

Four things that look like "the model got worse" Comparison
CriterionTraining-serving skewData driftcovariate shiftLabel shiftConcept drift
What changedThe code: one feature, two definitionsThe input populationThe outcome rate; inputs within each class unchangedThe input-to-outcome relationship
When it showsFrom the first requestAfter launch, gradually or at a stepAfter launchAfter launch; inputs may look unchanged
Which signal sees itServed features compared with training features for the same rowsInput PSI or KS; prediction driftMean score against the observed rateOnly matured outcomes, or a validated proxy
What fixes itOne shared definition; log served featuresOften nothing: check matured outcomes firstPrior or threshold adjustment (Day 13)Retrain on matured labels, through shadow and canary

Skew is a defect in your pipeline; the other three are changes in the world. Only the last needs a new model.

Monitoring in layers, fastest first Stack
  1. Service healthLatency, errors, throughput. Seconds. Says the system runs, not that it is right.
  2. Data qualitySchema, freshness, missing rate and ranges against the contract. Minutes.
  3. Input driftPSI or KS per feature against a reference window. Hours. Blind to concept drift.
  4. Prediction driftScore distribution and flagged share. Hours. Can miss shifts that cancel out.
  5. Proxy outcomeA short-delay signal, such as a first payment or a first login, validated against the true label. Days.
  6. Matured outcomeCalibration and error by slice once labels arrive. The first layer that measures harm directly.
  7. Business resultCost per decision against a control group. Slowest and closest to value.

While labels are late, validate a short-delay proxy against matured labels and keep immature weeks out of training. A small random control that never receives the model's decision keeps outcome comparisons clean. Read drift beside feature importance (Day 17) and slice performance (Day 18): a drifted feature the model ignores is noise.

Loud and harmless, silent and harmful Illustration
Input PSI on lead time (seen at once)00.25126101319Weekalert at 0.25covariateconcept

Covariate case: PSI crosses 0.25 in week 12. Concept case: never; its highest weekly PSI is 0.020.

Calibration gap (observed minus predicted)-0.2-0.0506101319Weekdashed: when it happenssolid: when labels show itcovariate

Concept case: the model over-predicts no-shows from week 10, but week 10's labels mature only in week 13. Shaded: harm that no monitor can see yet.

Synthetic weeks of 1,000 bookings, generated as in the application script (lead time Gamma(4, 7.5); change from week 10; labels 3 weeks late). The "model" here is the week 0-5 rule itself; PSI uses the app's drift helpers with 10 reference-quantile bins.

The two scenarios of the application, week by week. Input PSI fires only for the covariate case; the concept case shows only when its labels mature, three weeks after the harm starts. An input alarm says the population moved, not that the model is wrong, and green input monitors do not say it is right.

Key terms: Data contract, Training-serving skew, Data drift (covariate shift), Label shift (prior shift), Shadow deployment and Canary release. The path, the signals and the drift types are in the pictures above; PSI and KS, thresholds, release patterns, delayed labels and retraining triggers are in the optional depth below.

  • Monitor in the order signals arrive: data quality in minutes, input and score drift in hours, matured outcomes after the label delay.
  • Drift is not harm, and no drift is not health. Retrain on matured outcomes, through shadow and canary, with an alias you can flip back.

Assumptions

  • Labels mature on a known schedule; otherwise "recent" performance mixes mature and immature cases.

  • The reference window is representative; one taken after a change hides it.

  • Each monitor has an owner and a runbook; an unowned alert is a log line.

Sign in to record this block.

Application and workplace decision · 10 min

  • The script simulates 20 weeks of bookings for a no-show model trained on weeks 0-5. Labels arrive three weeks late.
  • Covariate case: lead times lengthen from week 10; the lead-time rule is unchanged.
  • Concept case: the rule weakens from week 10; the inputs look the same.
  • It prints the first PSI alert (threshold 0.25), KS, the mean score, the calibration gap (observed minus predicted rate) and when harm becomes visible. Predict both cases before you run it.

Python

import numpy as np
from scipy.stats import ks_2samp
from sklearn.ensemble import HistGradientBoostingClassifier

DELAY, START = 3, 10                                              # labels arrive 3 weeks late; the change starts in week 10

def make_week(rng, w, scenario, n=2000):
    lead = rng.gamma(4, 7.5, n)
    if scenario == "covariate" and w >= START:
        lead *= 1 + 0.12 * (w - START + 1)                        # longer-lead bookings; the lead -> no-show rule is unchanged
    slope = 0.02 if scenario == "concept" and w >= START else 0.06    # a new fee rule weakens the lead-time effect
    return lead[:, None], (rng.random(n) < 1 / (1 + np.exp(-(-3.4 + slope * lead)))).astype(int)

def psi(ref, x, k=10):                                            # bins from the reference quantiles
    edges = np.quantile(ref, np.linspace(0, 1, k + 1)[1:-1])
    e, a = (np.bincount(np.searchsorted(edges, v), minlength=k) / len(v) for v in (ref, x))
    return float(np.sum((a - e) * np.log((a + 1e-6) / (e + 1e-6))))

for scenario in ("covariate", "concept"):
    rng = np.random.default_rng(19)
    data = [make_week(rng, w, scenario) for w in range(20)]
    ref_X, ref_y = np.vstack([d[0] for d in data[:6]]), np.concatenate([d[1] for d in data[:6]])
    model = HistGradientBoostingClassifier(max_iter=60, random_state=0).fit(ref_X, ref_y)
    score = lambda w: model.predict_proba(data[w][0])[:, 1].mean()    # prediction drift: available at once
    gap = lambda w: data[w][1].mean() - score(w)                      # observed - predicted rate: needs labels
    first = lambda f: next((w for w in range(6, 20) if f(w)), None)
    w_psi = first(lambda w: psi(ref_X[:, 0], data[w][0][:, 0]) >= 0.25)
    w_bad = first(lambda w: abs(gap(w)) > 0.05)
    ks = ks_2samp(ref_X[:, 0], data[19][0][:, 0]).statistic
    print(f"{scenario:<9}| PSI alert week {w_psi} | KS week 19 {ks:.2f} | mean score {score(5):.3f} -> {score(19):.3f}"
          f" | calibration gap week 19 {gap(19):+.3f}")
    print(f"{'':<9}| harm starts week {w_bad}, seen with labels in week {None if w_bad is None else w_bad + DELAY}")

This code is executed in the repository's checks (pnpm lesson:py / lesson:sql). It is not run on this site; copy it into your notebook.

Sign in to record this block.

Mini-assessment · 8 min

Your first answer is your evidence. You can re-answer later in practice mode; that is labelled practice.

Question 1 · easy

Offline, a model scores well. Online, its inputs look different from training for the same bookings because the training SQL treated a missing fare as zero and the serving code treats it as the route average. What is this?
Options

How confident are you? (optional, used for calibration)

Question 2 · medium

A feature's reference shares in three bins are 0.5, 0.3 and 0.2. This week the shares are 0.3, 0.3 and 0.4. What is the PSI? Give two decimals.
PSI

How confident are you? (optional, used for calibration)

Question 3 · hard

For eight weeks every input PSI is below 0.1 and the score distribution is stable. Labels arrive three weeks late. Operations reports more denied boardings than expected on bookings the model scored as low-risk. What is the best next step?
Options

How confident are you? (optional, used for calibration)

Question 4 · medium

A monitor runs a two-sample KS test on each of 40 input features every day on 800 rows and flags a feature when p < 0.05. Nothing in the world changes, the features are independent, and each test has its nominal 5% false-positive rate. What is the probability that at least one feature is flagged on a given day? Give two decimals.
probability

How confident are you? (optional, used for calibration)

Question 5 · hard

A no-show model uses `route_noshow_7d`. Training features were built from the warehouse after it was complete, by event date. At serving time the feature is computed from what has been loaded by 06:00, and about 30% of the last week's outcomes arrive 2 to 3 days late. From the first day (illustrative figures), live AUC is 0.66 against 0.74 offline, scores run lower than offline, and the served feature's PSI against training is 0.18, below the team's 0.25 alert. What is the best diagnosis and next step?
Options

How confident are you? (optional, used for calibration)

Sign in to complete lessons and keep your progress.

Technical depth · optional · 42 min in total

Open only what you need. Nothing here is required to complete the day; the assessment covers the core above.

PSI: definition, derivation as a divergence, a computation by handadvanced8 minThe formula, why it equals KL both ways and is never negative, four bins computed on paper, and how much PSI noise alone produces.
Population stability index Formula
PSI=∑i=1k(ai−ei) ln⁡aiei\mathrm{PSI} = \sum_{i=1}^{k} (a_i - e_i)\,\ln\frac{a_i}{e_i}
eie_i
share of the reference window in bin i (bins from reference quantiles)
aia_i
share of the current window in the same bin
kk
number of bins

Bins from reference quantiles keep every e_i equal, so no bin starts nearly empty; an empty current bin needs a small constant or the log is infinite. Thresholds are choices: Evidently documents KS up to 1,000 rows, normed Wasserstein above, and 0.1 for PSI.

Derivation: PSI is a symmetric divergence between two histograms Sequence
  1. Two histograms

    Cut the feature into k bins from reference quantiles. e_i = reference share in bin i, a_i = current share. Both sum to 1.

  2. KL one way

    KL(a || e) = sum of a_i ln(a_i / e_i): the extra log-loss of describing current data with the reference shares.

  3. KL the other way

    KL(e || a) = sum of e_i ln(e_i / a_i) = - sum of e_i ln(a_i / e_i).

  4. Add them

    KL(a || e) + KL(e || a) = sum of (a_i - e_i) ln(a_i / e_i) = PSI. It is the symmetric (Jeffreys) divergence.

  5. Every term is positive

    If a_i > e_i both factors are positive; if a_i < e_i both are negative. A bin that shrinks adds as much as one that grows.

  6. Small changes

    ln(a / e) is close to (a - e) / e, so PSI is close to the sum of (a_i - e_i)^2 / e_i: a chi-square statistic divided by n.

  7. What it cannot do

    It has no direction, no notion of which bins matter to the model, and it is never exactly zero on a finite sample.

  8. Properties

    Zero only when the histograms match; symmetric in the two windows; unchanged by monotone transforms; blind inside a bin.

Standard information-theory identities. The chi-square link gives the no-drift noise level in the equation further down.

PSI by hand: monthly data usage, four quartile bins Table
Bin (reference quartile)Reference share eThis month: count, share aln(a / e)Term (a - e) ln(a / e)
Lowest 25%0.2530 of 200, 0.15ln 0.6 = -0.511(-0.10)(-0.511) = 0.051
Second0.2540, 0.20ln 0.8 = -0.223(-0.05)(-0.223) = 0.011
Third0.2550, 0.25ln 1 = 00
Highest 25%0.2580, 0.40ln 1.6 = 0.470(0.15)(0.470) = 0.071
Total1.00200, 1.00-PSI = 0.133

Telecom customers' monthly data usage (illustrative). KL one way is 0.067, the other 0.066; the chi-square approximation gives 0.140. Noise alone, with 200 current and 1,000 reference rows, would give about 3 x (1/200 + 1/1000) = 0.018. Verified in Python.

PSI bin by bin: four equal bins drift to 10, 20, 30 and 40% Bridge
  1. Identical histograms0 PSI
  2. Bin 1: 25% to 10%a shrinking bin adds a large term+0.137 PSI
  3. Bin 2: 25% to 20%+0.011 PSI
  4. Bin 3: 25% to 30%+0.009 PSI
  5. Bin 4: 25% to 40%+0.071 PSI
  6. Total PSI0.228 PSI

Every term is positive whichever way the share moves, so the bridge only climbs; PSI says that inputs moved, not that it matters.

How much PSI to expect when nothing changed Formula
PSI≈∑i=1k(ai−ei)2ei,E[PSI∣no drift]≈(k−1)(1n+1m)\mathrm{PSI} \approx \sum_{i=1}^{k} \frac{(a_i - e_i)^{2}}{e_i}, \qquad \mathbb{E}\left[\mathrm{PSI} \mid \text{no drift}\right] \approx (k - 1)\left(\frac{1}{n} + \frac{1}{m}\right)
(ai−ei)2/ei(a_i - e_i)^{2} / e_i
for small differences the log ratio is close to a chi-square term
kk
number of bins; more bins
n,mn, m
rows in the current and the reference window

A large-sample approximation. With 10 bins and 100 current rows it is about 0.09, close to a common 0.1 threshold; calibrate thresholds on no-drift windows of the same size.

PSI is never zero, and small windows inflate it Illustration
PSI between two samples from the same distribution (10 bins)00.050.10.15Rows in the current window (log scale); reference fixed at 5,0001002005001,0002,0005,000a common threshold, 0.1
  • median over 80 windows (bar: full range)
  • 95th percentile
  • dashed: approximation (k - 1)(1/n + 1/m)

Synthetic data: reference and current windows are both standard-normal draws, so every PSI here is pure sampling noise. Computed with the app's PSI helpers; the dashed line is a large-sample approximation, not a guarantee.

No drift at all, yet with 100 current rows the median PSI is 0.086 and 36% of windows reach 0.1. With 5,000 rows the 95th percentile is 0.008. Set thresholds per sample size, or test against the no-drift spread.

Both windows come from the same distribution. The PSI that sampling noise alone produces falls roughly as 1 / n, so a threshold that is quiet on a large store fires on a small one. Compute PSI on pairs of no-drift windows of the production size: an alert level below that noise fires every day.

KS: the largest gap between two empirical CDFs6 minThe statistic and its test, five values per window computed by hand, why its threshold shrinks with the window size, and how it differs from PSI.
The two-sample Kolmogorov-Smirnov statistic Formula
D=sup⁡x∣ F^ref(x)−F^cur(x) ∣,reject at level α if D>c(α)n+mn mD = \sup_x \big|\, \hat F_{\mathrm{ref}}(x) - \hat F_{\mathrm{cur}}(x) \,\big|, \qquad \text{reject at level } \alpha \text{ if } D > c(\alpha)\sqrt{\tfrac{n + m}{n\,m}}
F^(x)\hat F(x)
an empirical CDF: the share of the sample at or below x
sup⁡x\sup_x
the largest vertical gap between the two step functions, wherever it occurs
n,mn, m
rows in the reference and the current window
c(α)c(\alpha)
about 1.36 at alpha = 0.05 (large-sample value); exact tables or scipy for small samples

No bins, no direction, and a p-value under the null of identical distributions. Its threshold shrinks like 1 / sqrt(n), which is its main property in monitoring.

KS by hand: five basket values before and after Table
Basket value x (EUR)Reference ECDF (10, 20, 30, 40, 50)Current ECDF (30, 40, 50, 60, 70)Gap
100.20.00.2
200.40.00.4
300.60.20.4
400.80.40.4
501.00.60.4
601.00.80.2
701.01.00.0

Online-shop order values (illustrative). D = 0.4, first reached at x = 20. The 5% threshold for 5 and 5 rows is about 1.36 x sqrt(10 / 25) = 0.86, and scipy's exact p-value is far above 0.05: a whole shift of two steps is invisible at this size. Verified with scipy.stats.ks_2samp.

The KS threshold shrinks with window size: everything is significant at scale Table
Rows in each windowD needed for p below 0.05Reading
5about 0.86Only gross changes are detectable
1000.19Matches a 4,000-run simulation of no drift (0.19)
10,0000.019A shift of 0.05 standard deviations gave D = 0.020, p = 0.03
1,000,0000.0019Practically any change, harmless or not, is flagged

Large-sample formula with c = 1.358, checked by simulation. Monitor the size of D (or PSI) against a tolerance agreed in advance, not the p-value.

PSI measures distance, KS runs a test: different blind spots Comparison
CriterionPSIKS
AnswersHow far apart two histograms areWhether two samples share one distribution
Needs binsYes, from reference quantilesNo
Sensitive toAny bin's share; blind inside a binThe middle; weak in the tails, so a top-1% change can pass
Window sizeSmall windows inflate it with noiseLarge windows make harmless shifts significant
CategoriesWorks on categories directlyContinuous only: use chi-square or PSI

Monitor the size of either statistic against a tolerance agreed in advance; the p-value mostly measures the window size.

Many monitors: the family-wise false-alarm rate4 minDerive the chance that at least one of k quiet monitors fires, the union bound behind Bonferroni, and a worked production-line example.
Why many quiet monitors still alarm every day Formula
P(at least one alarm)=1−(1−α)k  ≤  k α,E[alarms]=k αP(\text{at least one alarm}) = 1 - (1 - \alpha)^{k} \;\le\; k\,\alpha, \qquad \mathbb{E}[\text{alarms}] = k\,\alpha
α\alpha
each monitor's false-alarm rate on a day when nothing changed
kk
number of monitors evaluated that day
(1−α)k(1 - \alpha)^{k}
chance that all k stay quiet, if the monitors are independent
kαk \alpha
the union bound: true for dependent monitors too, and the basis of the Bonferroni correction alpha / k

Derivation: each monitor is quiet with probability 1 - alpha; independence multiplies them; the complement is at least one alarm. Correlated features alarm together, so the true rate sits between alpha and the bound. The same algebra applies to slices (Day 18), A/B metrics and repeated daily looks.

25 sensor monitors on a production line, worked Table
RulePer-monitor alphaP(at least one false alarm a day)Over a 30-day month
Each at 1%0.011 - 0.99^25 = 0.22 (0.25 alarms a day)1 - 0.78^30: almost certain, about 7 to 8 alarms
Bonferroni, 1% / 250.00041 - 0.9996^25 = 0.0100About 0.3 alarms in the month

Manufacturing IoT, illustrative, independent monitors on a stable line. Bonferroni buys quiet days by losing sensitivity; a count of drifted features or a drift score weighted by the model's reliance (Day 17) keeps both. Prefer a few monitors tied to the decision: an alert nobody acts on trains the team to ignore the next one.

Monitoring layers and label delay4 minSeven layers from service health to business result, what each can see, and what to do while labels are late.
Monitoring in layers, fastest first Stack
  1. Service healthLatency, errors, throughput. Seconds. Says the system runs, not that it is right.
  2. Data qualitySchema, freshness, missing rate and ranges against the contract. Minutes.
  3. Input driftPSI or KS per feature against a reference window. Hours. Blind to concept drift.
  4. Prediction driftScore distribution and flagged share. Hours. Can miss shifts that cancel out.
  5. Proxy outcomeA short-delay signal, such as a first payment or a first login, validated against the true label. Days.
  6. Matured outcomeCalibration and error by slice once labels arrive. The first layer that measures harm directly.
  7. Business resultCost per decision against a control group. Slowest and closest to value.

While labels are late, validate a short-delay proxy against matured labels and keep immature weeks out of training. A small random control that never receives the model's decision keeps outcome comparisons clean. Read drift beside feature importance (Day 17) and slice performance (Day 18): a drifted feature the model ignores is noise.

The lifecycle, hop by hop3 minWhat each hop from source table to decision must record, how it fails without an error message, and the loop the decision closes.
Each hop, what it must record, and how it fails silently Table
HopRecordsFails silently when
Source and contractSchema, meaning, event and load time, freshness, ownerA supplier redefines a field or a feed lands late
Feature pipelineFeature definitions and versions; as-of time of each valueTraining and serving compute the feature differently
Training pipelineData snapshot, code commit, config, environment, seeds, metricsA run cannot be reproduced or compared
RegistryModel version, lineage, alias, reason for promotionNobody knows which version is live or why
ServingModel version, served features, score and time per row or requestA job fails and yesterday's scores are reused
DecisionAction taken, threshold, manual overridesThe model changes the data it will later learn from

Sculley et al. (2015) argue that much of the maintenance cost of ML systems sits in data dependencies, glue code and configuration rather than model code. Breck et al. (2017) turn these hops into 28 tests in four groups (data, model, infrastructure, monitoring) as a readiness checklist.

The decision changes the data the next model learns from Loop
a small randomholdoutmeasures it1Model scoreswho looks risky orpromising2Decision actscredit granted, offersent, booking flagged3Outcomes observedmostly for those thedecision let through4Next training setalready filtered by theold modeldrivesshapesbecomeretrains
  1. Model scoreswho looks risky or promisingdrives ↓
  2. Decision actscredit granted, offer sent, booking flaggedshapes ↓
  3. Outcomes observedmostly for those the decision let throughbecome ↓
  4. Next training setalready filtered by the old modelretrains ↓
  5. ↺ back to 1 · Model scores

A hidden feedback loop (Sculley et al., 2015): if the model changes who gets credit or an offer, the next training set reflects the model. A random holdout that never receives the model's decision shows what the outcomes would have been.

Batch or online serving3 minPick the serving pattern from two questions - does the decision wait, and how fresh must the features be.
Two questions pick the serving pattern Map

Online scoring

A request-time call reading batch-built features from a low-latency store.

Online plus streaming features

Request-time decisions on seconds-old facts: the most to run and test.

Batch scoring

A scheduled list from warehouse tables: simplest to test, re-run and roll back.

Frequent batch runs

A schedule tight enough for the freshness needed, still re-runnable.

Choose batch unless the decision waits for the score or needs facts fresher than the schedule.

Batch or online serving Comparison
CriterionBatch scoringOnline scoring
Use whenThe decision runs on a schedule, like a nightly list of customers to callThe decision waits for the answer, like a card authorization or an insurance quote
Latency budgetMinutes to hoursMilliseconds per request
Features come fromWarehouse tables as of the run timeThe request plus a low-latency feature store
Typical failureThe job fails or runs twice; stale scores are servedTimeouts; features that differ from the training definition
SafeguardIdempotent writes keyed by run date; a freshness checkA timeout with a fallback rule; logged served features

Choose batch unless the decision waits for the score. Batch is simpler to test, re-run and roll back.

Training-serving skew and point-in-time features4 minWhere skew comes from, the as-of rule that prevents it, and three habits that catch it.
Where training-serving skew comes from Overview
  • Two code paths

    SQL for training, application code for serving: one feature, two definitions.

  • Late-arriving rows

    Training reads the completed table; serving reads only what has loaded by scoring time.

  • Missing values

    A missing income is zero in one path and the branch median in the other.

  • Overwritten lookups

    A joined table is corrected after training and history is overwritten, not versioned.

  • Refit preprocessing

    An encoder refit at serving instead of shipping the fitted object (Day 9).

Three habits catch it (Google's Rules of ML 29, 32 and 37): log the feature values used at serving and train from those logs where you can; reuse one feature implementation in both pipelines; regularly recompute training features for served rows and compare.

A point-in-time feature Formula
xi(t)=f({ r:eventr≤t, loadedr≤t })x_i(t) = f\left(\{\, r : \mathrm{event}_r \le t,\ \mathrm{loaded}_r \le t \,\}\right)
xi(t)x_i(t)
the feature for entity i as it could be known at decision time t
eventr\mathrm{event}_r
when the fact in row r happened (a payment missed last month)
loadedr\mathrm{loaded}_r
when row r became available to the scoring job
ff
the one feature definition that both training and serving call

Filtering on event time alone gives training a complete history that serving never sees. Filtering on load time too reproduces serving.

Shadow, canary, A/B: releasing with gates4 minStages of a release, what each stage can and cannot tell you, how big a canary must be, and how the way back works.
A release in stages Sequence
  1. Offline gate

    Beats the incumbent on a time-ordered holdout, overall and per slice, on a frozen evaluation set.

  2. Shadow

    Scores live inputs; outputs are logged, not used. Tests plumbing, latency and agreement.

  3. Canary

    A small, time-limited share of decisions against a control, with stop rules agreed in advance.

  4. Ramp

    Widen the share in steps while the guard metrics hold.

  5. Full release

    The alias points at the new version; the old one stays registered and tested.

  6. Record

    Why it was promoted, on which evidence, and what would trigger a revert.

Shadow, canary and A/B test answer different questions Comparison
CriterionShadowCanaryA/B test
Affects decisionsNo: outputs are only loggedYes, for a small shareYes, in randomized arms
AnswersDoes it run, how fast, and how often does it disagree with the incumbent?Does it break something or cause visible harm quickly?The effect on the business outcome, with an interval
Cannot answerAny effect on outcomes; the incumbent still decidesSmall effects: the sample is too small to detect themAnything before its planned duration ends
Typical size and lengthAll traffic or a full batch, daysA few percent, hours to daysSized for power, often weeks

A canary is a safety check against a control (Google SRE workbook), not a substitute for a sized experiment (Day 5).

What a canary can and cannot see Illustration
Chance of detecting +1 point of bad outcomes00.50.810714Days of canary1% canary5% canary20% canary

A 1-point rise in the bad-outcome rate: a 1% canary reaches 80% power after 29 days, a 20% canary after 2.0.

Days to 80% power by size of harm (5% canary)+0.5 points24 days+1.0 points6.3 days+2.0 points1.8 days+5.0 points12 hbar length on a log scale

Days until 80% power, 5% canary. A 5-point jump shows within a day; a half-point harm needs an experiment sized for it, not a canary.

Computed, not simulated: power of a one-sided two-proportion z-test at the 5% level (normal approximation), base bad-outcome rate 10% (no-shows, defaults or returns), 20,000 decisions a day split between canary and control. Volumes and rates are illustrative assumptions.

Power of a canary against a large control to detect a rise in a bad-outcome rate (no-shows, defaults, returns). Breakage shows within a day; a small harm needs weeks, which is an experiment, not a canary.

Size the canary from the smallest harm worth stopping for Formula
ncanary≈(zα+zβ)2 p (1−p)δ2n_{\text{canary}} \approx \frac{(z_{\alpha} + z_{\beta})^{2}\, p\,(1 - p)}{\delta^{2}}
zαz_{\alpha}
about 1.64 for a one-sided test at 5%
zβz_{\beta}
about 0.84 for 80% power
pp
the bad-outcome rate under the incumbent
δ\delta
the smallest rise in that rate worth stopping for, chosen before launch

Rule of thumb against a control much larger than the canary. With p = 0.10 and delta = 1 point: 6.18 x 0.09 / 0.0001, about 5,600 canary rows, roughly 6 days at 1,000 rows a day, as in the demo. Pick the stop metric and delta first; share and length follow from them, not from habit.

Rollback is re-pointing an alias; fallback is a rule Diagram
Alias championwhat productionreadsVersion 7new, in canaryGuard metricbreached?Ramp upnext stage of thereleaseRe-point toversion 6retained, tested,same contractFallback rulebaseline if inputsleave range orservice failspoints atnoyesv6 unsafe too
  1. Alias championwhat production reads
  2. Version 7new, in canary
  3. Guard metric breached?
    • no: to Ramp up
  4. Re-point to version 6retained, tested, same contract
    • v6 unsafe too: to Fallback rule
  5. Ramp upnext stage of the release
  6. Fallback rulebaseline if inputs leave range or service fails

A rollback is only fast if the old version still runs on today's features. Test it like a release.

Retraining: triage, triggers and schedules3 minRoute every alert through the same questions, and choose what may trigger a retrain.
Triage an alert before you retrain Diagram
Alert firesContract orfeed broken?Fix thepipelinea code defect, notdriftHarm on maturedlabels?Annotate andkeepdrift without harmTrain acandidateon matured labelsonlyShadowalias ready torevertPromote andrecordyesnonoyesgates pass
  1. Alert fires
  2. Contract or feed broken?
    • yes: to Fix the pipeline
  3. Harm on matured labels?
    • no: to Annotate and keep
  4. Train a candidateon matured labels only
    • to Shadow
  5. Fix the pipelinea code defect, not drift
  6. Annotate and keepdrift without harm
  7. Shadowalias ready to revert
  8. Promote and record
Retraining: on a schedule or on a trigger Comparison
CriterionOn a scheduleOn matured harmOn input drift
Acts onThe calendar: weekly or monthlyCalibration or error beyond tolerance on matured labelsA PSI or KS alarm
StrengthPredictable; the pipeline stays exercisedActs on actual harmThe earliest signal
RiskRetrains when nothing is wrong and is late when something isWaits for the label delayActs on movement, not harm; use it as a prompt to check

Whatever triggers it, a retrained model is a new candidate and passes the same gates, shadow and canary.

Reproducibility, model cards and decision recordsadvanced3 minWhat to version so a run can be recovered, and what to write down so someone else can operate the model.
What to version so a run can be recovered Table
WhatExampleWhy
DataSnapshot id, or query plus as-of timeRetrain on exactly the same rows
CodeCommit of feature and training codeKnow which definition produced a score
ConfigurationHyperparameters, thresholds, feature listChanging a threshold is a release too
EnvironmentLibrary versions, container imageThe same code can give different results across versions
RandomnessSeeds; note non-deterministic stepsTell a real change from run-to-run noise
ArtifactThe fitted pipeline with preprocessing, and its input signatureServe exactly what was evaluated (Day 9)

Reproducible means recoverable within a stated metric tolerance, not bit-identical; many stacks differ across hardware and thread counts. Gate releases on a frozen evaluation set with a tolerance sized to run-to-run noise, and record that tolerance with the run.

The paperwork that makes a model operable Overview
  • Intended use

    The decision, its users and out-of-scope uses (model cards, Mitchell et al. 2019).

  • Metrics and evaluation data

    Which split, which metric, against which baseline, overall and by slice.

  • Factors

    Groups where performance may differ: segment, region, channel.

  • Caveats

    Known failure modes, label delay, input ranges outside which it must not be used.

  • Decision record

    Why this version was promoted, the alternatives, and what would trigger a revert.

  • Runbook

    An owner, threshold and action for each monitor, and the rollback steps.

Registries such as MLflow's give each version lineage and an alias that production reads; the pattern matters, not the tool.

Memory anchor

Early monitors see inputs, late monitors see harm; shadow, canary, then an alias you can flip back.

Useful for

  • Reviewing a credit, fraud, churn or demand model before launch with a path, owners, monitors and a rollback plan.

  • Writing the monitoring section of a design document for a model with delayed labels.

  • Triaging an alert as pipeline defect, data drift, concept drift or noise.

Not suitable for

  • Choosing a vendor or a tool; the lesson fixes concepts and leaves tool selection to your constraints and the documentation.

  • Replacing a runbook; thresholds, owners and actions must be set for each model and decision.

Traps

Treating drift as failure

An input alarm triggers a retrain, although outcomes are unchanged. The team spends effort, risks a new defect and learns to ignore alerts.

How to avoid it

Pair every input monitor with a matured-outcome check and act on harm, not on movement alone.

Calling green monitors healthy

Input and score monitors stay flat while the relationship changes, and harm runs for the label delay or longer.

How to avoid it

Add outcome monitors with a stated delay, a short-delay proxy and a canary against a held-out control.

Senior reasoning

A senior starts from the decision and the label delay, lists what each monitor can and cannot see, and sets actions for each alert in advance: pipeline defects are fixed in code, harmless drift is annotated, harmful drift triggers a candidate through shadow and canary with a tested rollback. They record why each model version was promoted and prefer a system whose failures are visible over one that is slightly more accurate.

Interview question

"How would you monitor a churn model whose labels arrive 90 days after the prediction?" How would you structure the answer?

Recall cards scheduled after completion

  • How do training-serving skew, data drift and concept drift differ, and how does each show up?
  • What do shadow, canary and rollback each give you, and what makes rollback fast?

Reviewed on a 1 · 3 · 7 · 14 · 30-day schedule; a miss returns the card to 1 day.

References

  • Hidden Technical Debt in Machine Learning Systems — D. Sculley, Gary Holt, Daniel Golovin, Eugene Davydov, Todd Phillips, Dietmar Ebner, Vinay Chaudhary, Michael Young, Jean-François Crespo, Dan Dennison, 2015core

    Names ML-specific maintenance risks (boundary erosion, entanglement, hidden feedback loops, undeclared consumers, data dependencies, configuration and changes in the external world) that justify tracking, contracts and monitoring.

  • The ML Test Score: A Rubric for ML Production Readiness and Technical Debt Reduction — Eric Breck, Shanqing Cai, Eric Nielsen, Michael Salib, D. Sculley, 2017core

    A rubric of 28 specific tests and monitoring needs for production readiness, used as the checklist behind the release and monitoring steps.

  • ML Model Registry (MLflow documentation) — MLflow projectoptional

    Describes model versions with lineage, aliases that production workloads can point at, tags and descriptions, which underlie promotion records and alias-based rollback.

  • GX Core overview (Great Expectations documentation) — Great Expectationsoptional

    An Expectation is a verifiable assertion about data, run through validation definitions and checkpoints in production, matching the validate-before-train-and-score step.

  • Customize Data Drift (Evidently documentation) — Evidently AIoptional

    Documents default drift methods and thresholds (KS up to 1,000 rows, normed Wasserstein above, PSI available with threshold 0.1) and shows that detected drift depends on method, sample size and threshold.

  • Canarying Releases (The Site Reliability Workbook, chapter 16) — Alec Warner, Stepan Davidovic et al., Googleoptional

    Defines a canary as a partial, time-limited deployment evaluated against a control, with metric selection and rollback decisions made in advance.

  • Failing Loudly: An Empirical Study of Methods for Detecting Dataset Shift — Stephan Rabanser, Stephan Günnemann, Zachary Lipton, 2019optional

    Treats shift detection as two-sample testing and separates detecting a shift from judging whether it is harmful.

Optional sections

Deep dive · 10 min

What "reproducible" means. Fix the recoverable inputs: code commit, data snapshot id, configuration, library versions and seeds. Many training stacks are not bit-exact across hardware or thread counts, so define success as a tolerance on the evaluation metric, and gate releases on a frozen golden set with a tolerance sized to run-to-run noise. Record the tolerance with the run.

Skew in practice. Typical sources are two code paths for one feature (SQL for training, application code for serving), a joined table that changes between training and serving (a late-arriving correction), and different handling of missing values. Rules of ML (Rules 29, 32 and 37) recommend logging served features, reusing code between training and serving, and measuring skew across stages. Compare logged serving features with the training pipeline run on the same rows.

Delayed labels. Options in order of cost: monitor input and score drift as early indicators; build a proxy outcome with a short delay (a check-in event, a cancellation within 24 hours) and validate it against the matured label; hold out a small control population that never receives the model's decision, so outcome comparisons are not confounded by the model's own actions; exclude immature labels when training.

PSI is a tool, not a verdict. It depends on bin choices (quantiles of the reference), is sensitive to empty bins (hence the small constant in the code), and says nothing about which feature matters to the model. Weigh each feature's drift by its importance (Day 17) and check performance on slices (Day 18).

Hidden feedback loops. If the model's output changes which data you collect (a lender observes repayments only for approved applicants), future training data reflect the model's own behavior. Sculley et al. list this as a hidden feedback loop; keep a randomized holdout to measure it.

Interview practice · 10 min

Production shows precision falling for a fraud model, and a colleague says "it is data drift, let us retrain". How do you respond?

Content version 6 · last verified 2026-10-11 · depends on scikit-learn, scipy, mlflow, dvc, great-expectations, evidently