# How to Manage Model Versioning and Registries in MLOps

> Master model versioning and registries for MLOps. Learn how to achieve immutable artifact storage, track metadata lineage, and ensure reproducible experiments for seamless deployment.

- Repository: [Harvard Edge Computing/cs249r_book](https://github.com/harvard-edge/cs249r_book)
- Tags: how-to-guide
- Published: 2026-02-19

---

**Model versioning and registries provide immutable artifact storage with full metadata lineage, enabling reproducible experiments and safe promotion from staging to production.**

The harvard-edge/cs249r_book repository defines model versioning and registries as core MLOps components that bridge the gap between experimental training and production deployment. By treating every trained model as an immutable artifact with captured hyperparameters, evaluation metrics, and environment specifications, teams can manage model versioning and registries to ensure reproducibility and auditability across the entire machine learning lifecycle.

## What Is Model Versioning?

Model versioning is the practice of registering each trained model as an immutable artifact with comprehensive metadata. According to the source code analysis in `book/quarto/contents/core/ops/ops.qmd` around line 576, this process captures hyperparameters, evaluation metrics, environment specifications, and a unique version identifier.

When you manage model versioning and registries correctly, you create an auditable trail that links a specific model artifact to the exact code, data, and configuration that produced it. This guarantees you can roll back to a known-good model and determine which dataset version and feature store snapshot were used during training.

### Immutable Artifacts and Metadata

Every model version must be immutable once registered. The registry stores the model binary alongside a metadata JSON file that includes:

- **Framework version** (e.g., `tinytorch` commit hash)
- **Hyperparameters** (learning rate, batch size, architecture)
- **Performance metrics** (training accuracy, validation loss)
- **Data lineage** (dataset version, feature store snapshot ID)
- **Environment specs** (CUDA version, Python version, dependencies)

## Understanding Model Registries

A model registry is a centralized store that lists all model versions and exposes operations for **promote**, **deploy**, and **deprecate**. As documented in `book/quarto/contents/core/ops/ops.qmd` at line 576, the registry provides a single source of truth for the serving layer and makes lineage visualization possible.

The registry decouples training from deployment. Data scientists can push new versions to the registry without affecting production systems, while DevOps engineers can promote specific versions through staging environments based on automated tests or manual approval gates.

### Registry Operations

The standard lifecycle operations in a model registry include:

- **Register**: Upload a new model artifact with metadata after training completes
- **List**: Query all available versions with filtering by stage (development, staging, production)
- **Promote**: Move a version from one stage to another (e.g., staging to production)
- **Deprecate**: Mark a version as outdated or unsafe for serving without deleting the artifact
- **Rollback**: Switch the production endpoint to point to a previous version

### TinyTorch Command Registry Pattern

The cs249r_book repository demonstrates a pluggable command registry pattern in [`tinytorch/tito/main.py`](https://github.com/harvard-edge/cs249r_book/blob/main/tinytorch/tito/main.py) (lines 84-95) that you can reuse for model registry CLI commands. The `BaseCommand` class allows automatic discovery of subcommands, making it easy to add registry operations like `tinytorch model-registry list` or `tinytorch model-registry promote`.

## Implementing Model Versioning in Practice

Below is a minimal implementation following the patterns described in the repository. This example creates a local model registry with immutable artifacts and JSON metadata.

```python

# utils/model_registry.py

import json
from pathlib import Path
from datetime import datetime
import uuid

REGISTRY_ROOT = Path(__file__).parent / "registry"
REGISTRY_ROOT.mkdir(exist_ok=True)

def _version_id() -> str:
    """Generate a short, timestamped version string."""
    return f"v{datetime.utcnow().strftime('%Y%m%d%H%M%S')}-{uuid.uuid4().hex[:6]}"

def register_model(model_path: Path, metadata: dict) -> str:
    """Persist a model file and its metadata in the registry."""
    version = _version_id()
    version_dir = REGISTRY_ROOT / version
    version_dir.mkdir()
    # copy model artifact

    target = version_dir / model_path.name
    target.write_bytes(model_path.read_bytes())
    # enrich metadata

    metadata.update({
        "version": version,
        "registered_at": datetime.utcnow().isoformat(),
        "model_file": target.name,
    })
    (version_dir / "metadata.json").write_text(json.dumps(metadata, indent=2))
    return version

def list_versions() -> list[str]:
    """Return all registered version identifiers."""
    return sorted([p.name for p in REGISTRY_ROOT.iterdir() if p.is_dir()], reverse=True)

def get_metadata(version: str) -> dict:
    """Load metadata for a given version."""
    meta_path = REGISTRY_ROOT / version / "metadata.json"
    return json.loads(meta_path.read_text())

```

### Registering a Model After Training

Integrate the registry into your training loop to automatically capture every experiment:

```python
from pathlib import Path
from utils.model_registry import register_model

# after training...

model_file = Path("outputs/my_model.pt")
meta = {
    "framework": "tinytorch",
    "hyperparameters": {"lr": 0.01, "epochs": 20},
    "train_accuracy": 0.93,
    "validation_accuracy": 0.88,
}
version = register_model(model_file, meta)
print(f"Model registered as {version}")

```

### Listing and Retrieving Versions

Query the registry to inspect available models and their performance metrics:

```python
from utils.model_registry import list_versions, get_metadata

for v in list_versions()[:5]:  # show latest 5

    meta = get_metadata(v)
    print(f"{v}: {meta['validation_accuracy']*100:.1f}% val acc")

```

## Integrating with CI/CD Pipelines

Model versioning and registries must connect to your broader MLOps pipeline. According to `book/quarto/contents/core/workflow/workflow.qmd` at line 529, versioning ties model artifacts to data versioning, feature stores, and CI/CD automation.

When a pipeline (Airflow, Prefect, or GitHub Actions) reads from the model registry, it pulls the exact **data version** (via DVC or external storage) and the **feature store** snapshot referenced in the model metadata. This guarantees that a model can be rebuilt exactly, satisfying reproducibility requirements for regulated industries.

Promotion gates in the registry trigger deployment workflows. When a model version moves from *staging* to *production*, the CI pipeline updates the serving endpoint configuration to point to the new artifact path, while the registry logs the promotion event for audit purposes.

## Summary

- **Model versioning** creates immutable artifacts with complete metadata (hyperparameters, metrics, environment specs) to ensure reproducibility and auditability.
- **Model registries** provide centralized storage with lifecycle operations (register, promote, deprecate) that decouple training from production deployment.
- The cs249r_book repository defines these concepts in `book/quarto/contents/core/ops/ops.qmd` and demonstrates implementation patterns using the TinyTorch command registry in [`tinytorch/tito/main.py`](https://github.com/harvard-edge/cs249r_book/blob/main/tinytorch/tito/main.py).
- Production implementations should integrate registries with data versioning (DVC), feature stores, and CI/CD pipelines to maintain end-to-end lineage from raw data to serving endpoint.

## Frequently Asked Questions

### What is the difference between model versioning and a model registry?

Model versioning is the practice of capturing a trained model as an immutable artifact with metadata, while a model registry is the centralized system that stores these versions and manages their lifecycle stages. Versioning ensures you can reproduce a specific model state; the registry provides the interface to promote, deploy, or deprecate those versions across environments.

### How does model versioning ensure reproducibility in MLOps?

By storing immutable artifacts alongside metadata that includes the exact hyperparameters, training dataset version, feature store snapshot, and environment specifications (CUDA, Python, library versions), model versioning creates a complete provenance record. As documented in `book/quarto/contents/core/workflow/workflow.qmd`, this allows pipelines to rebuild the model exactly or roll back to previous versions when production issues arise.

### Can I implement a model registry without using commercial tools like MLflow?

Yes. The cs249r_book repository demonstrates that you can build a functional registry using simple filesystem storage and JSON metadata, as shown in the [`utils/model_registry.py`](https://github.com/harvard-edge/cs249r_book/blob/main/utils/model_registry.py) example. For CLI integration, you can reuse the TinyTorch command registry pattern from [`tinytorch/tito/main.py`](https://github.com/harvard-edge/cs249r_book/blob/main/tinytorch/tito/main.py) to create subcommands for `register`, `list`, and `promote` operations without external dependencies.

### What role does the model registry play in CI/CD pipelines?

The model registry acts as the single source of truth that decouples model training from deployment. CI/CD pipelines query the registry to determine which model version to deploy, trigger promotion workflows when validation criteria are met, and maintain lineage links between the model artifact, data version, and serving endpoint configuration.