Most machine learning models never ship. They live in a Jupyter notebook, hit a respectable validation score, and die in a handoff meeting. MLOps is the discipline that closes that gap. It applies DevOps principles to the full model lifecycle: data versioning, training pipelines, deployment, monitoring, and retraining. This guide breaks down each stage, the tools that matter, and the exact commands to get a model serving real traffic.
Quick Takeaways
- MLOps is the set of practices that automates training, deployment, and monitoring of ML models, so they behave reliably in production.
- A production pipeline has five stages: data versioning, experiment tracking, CI/CD, serving, and monitoring.
- Models degrade over time through data drift and concept drift. Automated retraining triggers are not optional.
- LLMOps extends MLOps with prompt versioning, evaluation suites, guardrails, and token-cost tracking.
| Maturity Level | Training | Deployment | Monitoring | Typical Team |
|---|---|---|---|---|
| Level 0 | Manual, notebook-based | Manual handoff | None | Solo data scientist |
| Level 1 | Automated pipeline | Manual trigger | Basic metrics | Small ML team |
| Level 2 | Automated + triggered | Full CI/CD | Drift + alerts | Platform team |
What Is MLOps?
MLOps (Machine Learning Operations) combines ML engineering, data engineering, and DevOps. Traditional software has one moving part: code. ML systems have three: code, data, and the model artifact. A change to any of them can break production.
That is the core reason standard CI/CD is not enough. A model can pass every unit test and still fail because the live data distribution shifted last Tuesday.
Why Notebooks Fail in Production
Notebooks are excellent for exploration. They are poor for production because they:
- Hide execution order and state, so results are not reproducible.
- Mix data loading, feature logic, and training in one place.
- Cannot be unit tested or code reviewed cleanly.
- Make dependency management implicit.
The fix is not abandoning notebooks. It is extracting logic into versioned, testable modules.
The MLOps Lifecycle: Five Stages
1. Data Versioning and Validation
Reproducibility starts with data. Git cannot handle multi-gigabyte datasets, so teams use DVC, lakeFS, or Delta Lake to version data alongside code.
# Initialize DVC and track a dataset
pip install dvc dvc-s3
dvc init
dvc add data/train.parquet
git add data/train.parquet.dvc data/.gitignore
git commit -m "Track training data v1"
dvc remote add -d storage s3://my-bucket/dvc-store
dvc push
Add automated validation before any training run. Great Expectations or Pandera catch schema breaks, null spikes, and out-of-range values early.
import pandera as pa
schema = pa.DataFrameSchema({
"age": pa.Column(int, pa.Check.in_range(18, 100)),
"income": pa.Column(float, pa.Check.ge(0), nullable=False),
"churned": pa.Column(int, pa.Check.isin([0, 1])),
})
validated_df = schema.validate(df) # Raises SchemaError on failure
2. Experiment Tracking and Model Registry
Every training run should log hyperparameters, metrics, code version, and data version. MLflow is the most widely adopted open-source option. Weights & Biases offers a stronger hosted UI.
import mlflow
import mlflow.sklearn
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import f1_score
mlflow.set_experiment("churn-prediction")
with mlflow.start_run():
params = {"n_estimators": 300, "max_depth": 12, "random_state": 42}
model = RandomForestClassifier(**params).fit(X_train, y_train)
f1 = f1_score(y_val, model.predict(X_val))
mlflow.log_params(params)
mlflow.log_metric("f1", f1)
mlflow.sklearn.log_model(
model, "model", registered_model_name="churn-classifier"
)
The model registry gives each version a stage: Staging, Production, Archived. Promotion becomes an auditable action instead of a file copied over Slack.
3. Automated Training Pipelines
Orchestrators run your steps as a directed acyclic graph (DAG). Each step is isolated, cached, and retryable.
| Tool | Best For | Hosting | Learning Curve |
|---|---|---|---|
| Kubeflow Pipelines | Kubernetes-native teams | Self-hosted | High |
| Apache Airflow | General workflow orchestration | Self-hosted / managed | Medium |
| Prefect | Python-first, dynamic flows | Cloud / self-hosted | Low |
| Dagster | Asset-based, data-aware pipelines | Cloud / self-hosted | Medium |
| Vertex AI / SageMaker Pipelines | Managed cloud ML | Cloud | Medium |
A minimal Prefect example:
from prefect import flow, task
@task(retries=2)
def load_data(path: str):
...
@task
def train(df):
...
@task
def evaluate(model, df) -> float:
...
@flow(name="training-pipeline")
def run_pipeline():
df = load_data("s3://bucket/train.parquet")
model = train(df)
score = evaluate(model, df)
if score < 0.80:
raise ValueError(f"F1 {score:.2f} below 0.80 threshold")
if __name__ == "__main__":
run_pipeline()
The quality gate at the end is the critical piece. A pipeline that cannot fail on bad metrics is just a script on a schedule.
4. CI/CD for Machine Learning
ML CI/CD has two loops: continuous integration of code and continuous training (CT) of models. A practical GitHub Actions workflow:
name: ml-ci
on:
pull_request:
paths: ["src/**", "data/*.dvc", "params.yaml"]
jobs:
test-and-train:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install -r requirements.txt
- run: pytest tests/ -q
- run: dvc pull && python src/train.py
- run: python src/check_metrics.py --min-f1 0.80
Test three layers: data tests (schema, nulls), model tests (minimum accuracy, behavioral invariance), and infrastructure tests (the API actually starts and responds).
5. Model Serving
Choose a serving pattern based on latency needs.
| Pattern | Latency | Use Case | Example Tools |
|---|---|---|---|
| Batch inference | Hours | Nightly scoring, recommendations | Spark, Airflow |
| Real-time API | 10-200 ms | Fraud detection, personalization | FastAPI, BentoML, KServe |
| Streaming | Sub-second | Event-driven scoring | Kafka + Flink |
| Edge / on-device | Under 10 ms | Mobile, IoT | ONNX Runtime, TFLite |
A containerized real-time endpoint with FastAPI:
from fastapi import FastAPI
from pydantic import BaseModel
import mlflow.pyfunc
import pandas as pd
app = FastAPI()
model = mlflow.pyfunc.load_model("models:/churn-classifier/Production")
class Features(BaseModel):
age: int
income: float
tenure_months: int
@app.post("/predict")
def predict(f: Features):
df = pd.DataFrame([f.model_dump()])
return {"churn_probability": float(model.predict(df)[0])}
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Use canary or shadow deployments for risky releases. Route 5% of traffic to the new model, compare outputs, then ramp up.
Monitoring: Where Most Teams Fall Short
Deployment is the midpoint, not the finish line. Models decay.
Types of Drift
| Drift Type | What Changes | Example | Detection Method |
|---|---|---|---|
| Data drift | Input feature distribution | Average order value rises after a price change | PSI, KS test |
| Concept drift | Relationship between inputs and target | Fraud tactics evolve | Performance tracking on labels |
| Label drift | Target distribution | Churn rate spikes seasonally | Chi-squared test |
| Prediction drift | Output distribution | Model scores cluster oddly | Distribution comparison |
The Population Stability Index (PSI) is a standard drift metric. A common rule of thumb: PSI below 0.1 is stable, 0.1 to 0.25 warrants investigation, and above 0.25 signals major drift.
from evidently.report import Report
from evidently.metric_preset import DataDriftPreset
report = Report(metrics=[DataDriftPreset()])
report.run(reference_data=train_df, current_data=live_df)
report.save_html("drift_report.html")
What to Monitor
- Operational metrics: latency (p50, p95, p99), throughput, error rate, GPU/CPU utilization.
- Data quality: null rates, schema violations, out-of-range values.
- Model quality: accuracy, precision, recall, and business KPIs once ground truth arrives.
- Fairness: performance across demographic slices.
Common stack: Prometheus + Grafana for infrastructure, Evidently, WhyLabs, or Arize for model-specific monitoring.
LLMOps: MLOps for Large Language Models
Foundation models change the workflow. You often call an API instead of training weights, so the artifacts become prompts, retrieval indexes, and evaluation sets.
| Dimension | Classic MLOps | LLMOps |
|---|---|---|
| Primary artifact | Trained weights | Prompts, RAG index, fine-tuned adapters |
| Evaluation | Accuracy, F1, AUC | LLM-as-judge, groundedness, human review |
| Cost driver | Compute hours | Tokens (input + output) |
| Main failure mode | Drift | Hallucination, prompt injection |
| Versioning | Model + data | Prompt + model version + retrieval config |
| Monitoring | Drift metrics | Trace logging, token usage, refusal rates |
A minimal LLM call with logging for cost tracking:
import anthropic, time
client = anthropic.Anthropic()
start = time.time()
resp = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
temperature=0.2,
messages=[{"role": "user", "content": "Summarize this support ticket: ..."}],
)
latency = time.time() - start
print({
"input_tokens": resp.usage.input_tokens,
"output_tokens": resp.usage.output_tokens,
"latency_s": round(latency, 2),
})
Treat prompts like code. Store them in Git, version them, and run an evaluation suite of 50 to 200 golden examples on every change. Set temperature low (0.0 to 0.3) for extraction and classification, higher for creative tasks.
Real-World Workflows
Scenario 1: Fraud Detection at a Fintech
- Transactions stream into Kafka. Features compute in a feature store (Feast) so training and serving use identical logic.
- A nightly pipeline retrains an XGBoost model and logs it to MLflow.
- A challenger model runs in shadow mode for 7 days.
- If precision at a fixed recall holds, the registry promotes it automatically.
- PSI alerts above 0.25 trigger an immediate retrain.
Scenario 2: RAG Support Assistant With Hallucination Controls
- Documents are chunked at 512 tokens with 64-token overlap and embedded into a vector database.
- Each release runs an evaluation set measuring groundedness and answer relevance.
- Production traces log retrieved chunks, prompts, and outputs.
- Low-confidence answers route to a human agent.
Scenario 3: Prompt Template for Reliable Structured Output
You are a support-ticket classifier.
Return ONLY valid JSON matching this schema:
{"category": "billing|bug|feature|other", "urgency": 1-5, "summary": "<=20 words"}
Ticket: {{ticket_text}}
Validate every response against the schema in code. Never trust raw model output.
Choosing Your Stack
| Need | Lightweight Option | Enterprise Option |
|---|---|---|
| Experiment tracking | MLflow | Weights & Biases, Databricks |
| Orchestration | Prefect | Kubeflow, Airflow |
| Feature store | Feast | Tecton, Vertex Feature Store |
| Serving | FastAPI + Docker | KServe, SageMaker, Seldon |
| Monitoring | Evidently + Grafana | Arize, Fiddler |
Start small. A solo team does not need Kubernetes. Add components when a specific pain justifies them.
Common MLOps Mistakes
- Training-serving skew: features computed differently offline and online. Use a shared feature layer.
- No rollback path: always keep the previous model version deployable in minutes.
- Monitoring only infrastructure: a healthy server can still return bad predictions.
- Ignoring ground-truth latency: labels may arrive weeks later. Plan proxy metrics.
- Over-engineering early: automate the painful step first, not everything at once.
FAQ
What is the difference between MLOps and DevOps?
DevOps automates the delivery of code. MLOps adds data and model versioning, continuous training, and drift monitoring, because ML systems can fail without any code change.
What skills do you need to work in MLOps?
You need Python, Docker, Git, and CI/CD basics. Add cloud platforms (AWS, GCP, or Azure), Kubernetes, and an orchestrator such as Airflow or Prefect. Solid ML fundamentals help you debug model behavior.
How often should a model be retrained?
It depends on how fast your data changes. Fraud models may retrain daily. Stable models may retrain quarterly. Best practice is trigger-based retraining tied to drift thresholds or performance drops, not a fixed calendar alone.
What is the difference between MLOps and LLMOps?
MLOps manages the lifecycle of trained models. LLMOps manages prompts, retrieval systems, evaluation, guardrails, and token costs for applications built on large language models.




