CI for Models: Data Contracts, Behavioral Tests, Golden Sets and a Merge Gate
Hands-on lab · IDE in your browser

CI for Models: Data Contracts, Behavioral Tests, Golden Sets and a Merge Gate

Build the CI suite that guards a fraud model. Enforce the data contract on every batch, write behavioral invariants that check the model obeys rules accuracy cannot see, pin the cases it must never get wrong with a golden set, gate on accuracy regression against the committed baseline, and aggregate all four into one merge decision that blocks a candidate whose sign-flip bug slips past the metrics.

Time
50 min
Checked steps
5
Level
Intermediate
Setup
None
Read step 1

Hands-on labs require Pro · $29.99/mo · cancel anytime

The job

Nimbus scores card transactions for fraud, and a candidate commit is up for merge. Its accuracy looks fine, but one line flipped the sign on the amount feature, so bigger charges now score as less risky. You build the model CI that decides whether it ships: a data contract, behavioral invariants, a golden set, a regression gate and one merge verdict.

5 steps, each checked when you finish it

A check runs your work at the end of every step. Hints and the full solution are there if you get stuck.

  1. 1

    Enforce the data contract

    Nimbus scores card transactions for fraud.

  2. 2

    Behavioral invariants

    Accuracy cannot see everything.

  3. 3

    The golden set

    Every model has a handful of cases it must never get wrong: the textbook fraud, the obvious legitimate customer.

  4. 4

    Regression gate

    The last automatic check is the familiar one: did the candidate lose accuracy against the model already in production.

  5. 5

    Run CI and gate the merge

    Tie it together.

Step 1 as it appears in the lab

The lab’s own text. The hint and the solution stay inside the lab.

Step 1: Enforce the data contract

Enforce the data contract

Nimbus scores card transactions for fraud. Every commit to the model runs a CI suite before it can ship, and the first thing CI checks is that the data still matches its contract: the columns, types and ranges the model was built for. A batch that drifts out of contract is caught here, before it reaches production.

model.py gives you load_data(), load_json(path), load(path) for a model, predict, predict_one, median_record and FEATURES. schema.json declares required columns and, per column, a type ("integer" or "number") and a min/max.

Write schema_check(data, schema): return a list of human-readable violations. Flag a required column that is missing, a column whose values are not its declared type, and any value outside the declared range. An empty list means the batch fits its contract. Run it on the clean holdout and a corrupted batch.

ci.py, the file you edit54 lines
"""The model CI suite. Every commit to the fraud model runs these checks before it can ship: the data still
fits its contract, the model still obeys the rules the business relies on, it still gets the known cases
right, and it has not regressed on the holdout. You write the checks and the runner that gates a merge."""
import model


# ---------- Step 1: data contract ----------

def schema_check(data, schema):
    """Validate a DataFrame against a schema. Return a list of human-readable violations: a required column
    missing, a column whose values are not the declared type (integer or number), or values outside the
    declared min/max. An empty list means the data fits its contract."""
    # TODO (Step 1): for each required column check it exists; for each column spec
    # check the dtype (integer/number) and that min()/max() are within the declared range.
    raise NotImplementedError("Step 1: write schema_check()")


# ---------- Step 2: behavioral invariants ----------

def run_behavioral(entry, base, invariants):
    """Check invariants that must hold whatever the metrics say. For each invariant, sweep its feature over
    its values on the base record and check the rule: not_decrease (score never drops as the value rises),
    not_increase (score never rises), not_change_decision (the hold/allow decision stays put). Return the
    names of the invariants that failed."""
    raise NotImplementedError("run_behavioral() arrives in Step 2")


# ---------- Step 3: golden set ----------

def golden_check(entry, golden):
    """The cases the model must always get right. Return the inputs whose predicted class (score >=
    THRESHOLD) does not match the expected label."""
    raise NotImplementedError("golden_check() arrives in Step 3")


# ---------- Step 4: regression gate ----------

def regression_gate(entry, baseline_acc, data, max_drop=0.03):
    """Accuracy on the holdout, and whether it regressed more than max_drop below the committed baseline.
    Return {"pass", "acc", "drop"}."""
    raise NotImplementedError("regression_gate() arrives in Step 4")


# ---------- Step 5: the CI runner ----------

def run_ci(entry, suite):
    """Run every check and return a report {check_name: {"pass": bool, "detail": ...}}. suite holds the
    schema, data, base record, invariants, golden set, baseline_acc and max_drop."""
    raise NotImplementedError("run_ci() arrives in Step 5")


def merge_ok(report):
    """True only if every check in the report passed."""
    raise NotImplementedError("merge_ok() arrives in Step 5")
Provided for you:baseline.jsoncandidate.jsondata.csvgolden.jsoninvariants.jsonmodel.pyschema.jsontry_it.py

Frequently asked questions

What is behavioral testing of an ML model?

Testing properties the model must satisfy regardless of its metrics: directional expectations (raising a risk feature should not lower the score), invariance (an irrelevant field should not change the decision), and specific known cases. It catches bugs that leave accuracy almost unchanged, like a flipped feature sign.

Why isn't accuracy enough to gate a model release?

A model can keep its overall accuracy while breaking on an important slice or violating a business rule, because those cases barely move the average. A behavioral invariant or a golden case fails outright where a metric only wobbles, which is why model CI runs several kinds of check.

What is a golden set in model testing?

A fixed set of inputs with known correct outputs that the model must always get right. It pins the textbook cases so a refactor cannot silently regress them, and it complements behavioral and metric checks rather than replacing them.

Continuous integration for machine learning models

Testing a model is not like testing ordinary code: accuracy on a holdout can stay flat while the model breaks a rule the business depends on. Model CI adds the checks that catch what a single metric cannot, and runs them on every commit before it ships. In this lab you build that suite: a data contract that rejects an out-of-spec batch, behavioral invariants that check directional and invariance properties, a golden set of cases the model must never get wrong, a regression gate against the committed baseline, and a runner that turns them into one merge decision, blocking a candidate whose bug the metrics missed.