Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

artefact
A saved file a release needs, such as the model, the code that prepares its inputs or the exact package versions.
batch
A run that scores many rows at once on a schedule and writes the results to a table.
canary
A release shown to a small random slice of traffic first, so a bad model reaches few users.
drift
A change in the inputs a model sees, or in the link between those inputs and the outcome it predicts.
evaluation gate
A check before release that stops a model when its measured quality falls below a set bar.
feature contract
An agreed promise about one input column — its type, range, missing values, uniqueness, freshness and owner — checked as data arrives.
incident
An event that degrades a service beyond its agreed level and needs a coordinated response.
inference contract
The agreed inputs, outputs and error cases for a model's prediction service, written down and tested.
latency budget
The time a request may spend in the model path, after network and rendering are subtracted from what users will tolerate.
model card
A short document describing what a model is for, how it was trained, its limits and who owns it.
monitoring
Watching inputs, outputs, service health and later outcomes so a change is noticed before users report it.
rollback
Returning to the last known good version, using a saved copy and a command that has been rehearsed.
serving
Running a model to answer requests in production, either one at a time or in scheduled groups.
training-serving skew
When an input is computed one way for model fitting and another way at request time, so the model reads different data.

MLOps from the ground up

From notebook to service

A notebook is a sequence of experiments. A service is a function with a contract, an artefact and a way to be wrong safely.

Part 1 of 10

Beginner15 min readBack to Courses

Two different programs

A notebook is optimised for exploration: cells run out of order, state accumulates, and the interesting output is a number on screen. A service is optimised for repetition: the same input gives the same output, there is no hidden state, latency is bounded and failure has a defined shape. Moving between them is a rewrite of the boundary, not a copy of the cells.

The most common mistake is to import the notebook into the service. It works on the author’s machine because the missing cells were run earlier. In production the first request fails on a name that was never defined, and the traceback points at a line that looks correct.

The audit is simple: restart the kernel and run every cell in order. If the notebook fails, it was never a program. If it succeeds, the run order is still implicit, so the next step is to move the logic into functions and call them from one entry point. A cell that depends on a variable created three cells earlier is a function argument waiting to be written down.

The inference contract

The service promises an input schema, an output schema, a latency budget and an error behaviour. The schema is not decoration: it decides what happens to a missing field, a string where a number was expected, or a category the model has never seen. Validate at the edge and reject with a reason.

The output contract matters just as much. A prediction without its model version, its units and its confidence is hard to debug and impossible to compare after a rollback. Return the score, the label and the version together.

Errors need their own contract. A malformed request is the caller’s fault and should be a 4xx with a field name; a model that cannot load is the service’s fault and should be a 5xx that pages someone. Returning 200 with a default prediction for a bad row hides the failure until a human notices the numbers are wrong.

A typed endpoint with FastAPI

from fastapi import FastAPI
from pydantic import BaseModel

class Request(BaseModel):
    user_id: str
    amount: float

app = FastAPI()

@app.post("/score")
def score(request: Request) -> dict:
    return {"user_id": request.user_id, "risk": model.predict(request.amount)}

Three functions and no globals

Run the service-shaped script, then change the training rows and confirm that only train needs to change. Predict and main should stay untouched.

Output
Click "Run" to execute your code

Separate training from serving

Training may read a warehouse, take hours and use a GPU. Serving reads one row, takes milliseconds and must not import the training notebook. Share the feature code and the model definition; do not share the globals, the file paths or the plotting.

A good split is three functions: train returns the parameters, predict takes the parameters and one row, and main wires them together. The first two are testable without a network or a file system, which is the whole point.

The shared part is the feature transformation, not the training loop. Put it in a small library with its own tests, import it from both paths, and version it with the model. If the two paths need different code, that difference is a decision to document, not a detail to discover in production.

A notebook that only works when its cells are run top to bottom is not a service. If the order matters, that order is hidden state, and hidden state is the first thing a request will expose.

The artefact is the handoff

A model is code plus weights plus preprocessing plus the version of the library that produced it. Save all four together, give the bundle an immutable name, and load it by name. A pickle without its preprocessing is a bug waiting for the first real request.

The artefact also gives you a rollback. If the previous bundle still exists and still loads, undoing a bad release is a pointer change. If it does not, the rollback is a rebuild under pressure, which is a different and much worse operation.

Name the artefact with the model, the data snapshot and the commit, and load it through a registry rather than a path. The registry gives you a list of what exists, who promoted it and when. A directory of pickles with names like final_v2 is a registry that has stopped being honest.

Reject with a reason

Run the validator and fix the rejected rows one at a time. Then add a field that the schema does not know and decide whether the service should ignore it or refuse it.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

Why is importing a notebook into a service fragile?