In the early stages of a data science project, tracking progress is often informal. A researcher might save a model file as final_v2_new.pkl or rely on a manually exported CSV file for training. This approach works in a silo, but it disintegrates the moment the project moves toward a production environment where reliability is non-negotiable. MLOps fundamentals dictate that every component of the pipeline must be versioned, immutable, and retrievable. Without these constraints, a model running in production becomes a black box that cannot be audited or improved with any scientific certainty.
The core difficulty in machine learning compared to traditional software engineering is the addition of data as a first-class citizen. In software, if you have the source code and the environment configuration, you can generally reproduce the binary. In machine learning, the resulting artifact is a function of the code, the hyper-parameters, and the specific state of the data at the time of execution. If the underlying data changes—which it inevitably does through late-arriving records or schema updates—the model behavior changes even if the code remains static. This article breaks down the technical layers required to version these three distinct pillars.
The three pillars of machine learning versioning
To understand MLOps fundamentals, we must categorize versioning into three distinct domains: Code, Data, and Models. Code versioning is the most mature, relying on Git to track changes in training scripts, preprocessing logic, and deployment manifests. However, Git is not designed for multi-gigabyte datasets or binary model weights. Attempting to force large files into a Git repository leads to bloated index files and significant performance degradation during simple operations like git status or git pull.
Data versioning requires a different mechanism entirely. It involves creating a content-addressable storage system where a unique hash represents a specific state of the dataset. Tools like DVC (Data Version Control) or LakeFS allow developers to link small metadata files in Git to large blobs in object storage like AWS S3 or Google Cloud Storage. This ensures that when a practitioner checks out a specific commit, they can pull the exact version of the data used during that specific training run, eliminating the 'it worked on my machine' syndrome.
Model versioning completes the loop by tracking the output of the training process. A model version is not just a weight file; it is a package that includes the model architecture, the weights, the environment dependencies (e.g., requirements.txt or a Docker image hash), and the performance metrics generated during validation. By treating models as immutable artifacts, teams can implement safe rollback strategies and A/B testing frameworks that reference specific, hashed versions rather than ephemeral file names.

Data versioning strategies and technical implementations
Effective data versioning is built on the principle of immutability. Once a dataset is used for a production model, it should never be modified in place. Instead, we use a 'snapshot' approach. In high-velocity environments, taking a full copy of a petabyte-scale data warehouse is impractical. Instead, practitioners utilize delta-lake architectures or logical snapshots. For example, in a SQL-based workflow, one might use a timestamp or a snapshot_id in the WHERE clause to ensure the training query always pulls the same rows.
For unstructured data like images or audio, the industry standard involves using a manifest file. This file contains a list of file paths and their corresponding MD5 or SHA-256 hashes. When the dataset 'v2' is created, a new manifest is generated. The MLOps pipeline reads this manifest to fetch the assets. This allows for 'virtual versioning' where files are stored once in a flat hierarchy, but the manifest defines the logical grouping. This prevents data duplication and keeps storage costs manageable while providing absolute certainty regarding the input features.
Another layer is the feature store. Feature stores like Feast or Hopsworks provide 'point-in-time' joins. This is a critical MLOps fundamental because it prevents data leakage. Data leakage occurs when information from the future is inadvertently used during training. By using a feature store, a developer can request: 'Give me the features for user X as they existed at 2026-05-12 14:00:00.' The system handles the complex time-travel logic, ensuring the training set perfectly reflects what the model would have seen in a real-world inference request at that moment.
Model registries and artifact management
A model registry is a centralized catalog for managing the lifecycle of an ML model. It acts as a bridge between the experimentation phase and the production phase. When a training job finishes, the MLOps pipeline should automatically register the resulting artifact. This registration includes the .onnx, .pt, or .pb file, along with a pointer to the code commit and data version used. MLflow and BentoML are common tools that facilitate this. They provide a clear API to query for the latest 'Staging' or 'Production' model version.
The registry also handles transitions. A model version should move through a lifecycle: None -> Staging -> Production -> Archived. This state management is essential for CI/CD pipelines. For instance, a deployment script can be configured to always pull the model tagged as Production. When a new model is ready, a human or an automated testing suite promotes the new version, and the deployment infrastructure automatically triggers a rolling update to the inference service. This decoupling of model promotion from code deployment allows for faster iteration.
Furthermore, model versioning must account for the inference environment. A model trained with scikit-learn 1.4 might fail or produce different results if loaded with scikit-learn 1.6 due to changes in underlying algorithms or default parameters. Therefore, the model version must be bundled with a specific environment definition. Containerization (Docker) is the gold standard here. The registry entry should ideally point to a specific container image tag that contains the exact OS libraries and Python dependencies needed for execution.

Comparative analysis of versioning tools
Choosing the right stack depends on the existing infrastructure and the scale of the data. The following table highlights the differences between common versioning approaches found in modern MLOps architectures.
| Tool Category | Primary Use Case | Key Advantage | Storage Constraint |
|---|---|---|---|
| Git LFS | Small-scale assets | Integrated with Git | Poor for >10GB |
| DVC | Data & pipeline tracking | Cloud agnostic | Metadata manual sync |
| MLflow | Model registry | Rich UI/Metrics | Requires central server |
| Delta Lake | Tabular data versioning | Time-travel queries | Locked to Spark/Databricks |
While these tools overlap, a mature MLOps foundation usually combines them. A typical setup involves Git for logic, DVC for raw data pointers, and MLflow for model artifact and metric tracking. The integration between these tools is usually handled by a CI/CD orchestrator like GitHub Actions or Kubeflow Pipelines, which ensures that a change in one triggers the appropriate updates in the others.
The experiment tracking layer
Experiment tracking is the 'lab notebook' of the machine learning engineer. In the discovery phase, hundreds of models might be trained with varying hyperparameters. Versioning these experiments is distinct from versioning production models. An experiment version includes the learning_rate, batch_size, and optimizer configuration along with the resulting loss curves and accuracy metrics. This allows teams to compare runs and determine if a new approach is genuinely better or just lucky.
One major pitfall in experiment versioning is the 'hidden state' problem. If an engineer modifies a local utility function but doesn't commit it to Git before running the experiment, the experiment is technically non-reproducible. MLOps best practices suggest that the training environment should automatically verify a 'clean' Git state (no uncommitted changes) before allowing a high-priority training job to start. This guarantees that the code version logged is the exact code that executed.
Visualization is also a key component. Versioning metrics over time allows for the detection of 'training drift,' where newer versions of a model perform worse on specific slices of data even if the global accuracy remains stable. By versioning the evaluation results at a granular level (e.g., performance by geographic region), developers can identify regression issues that would otherwise be missed by a simple global metric comparison.

Version control for environment and infrastructure
Beyond data and code, the underlying infrastructure must be versioned. This is known as Infrastructure as Code (IaC). If a model requires a specific GPU driver version or a specific amount of VRAM to function at a certain latency, that configuration must be tracked. Tools like Terraform or Pulumi allow MLOps teams to version the definition of their inference clusters. If the cluster configuration is not versioned, you might find that a model performs perfectly in the 'Dev' environment but fails in 'Production' due to a subtle difference in CPU architecture or memory limits.
Environment versioning also extends to the Python ecosystem. Using pip freeze > requirements.txt is a start, but it is often insufficient because it doesn't lock the versions of sub-dependencies. Using poetry.lock or conda environment files provides a more deterministic build. In a production MLOps pipeline, these lock files are used to build a Docker image, which is then tagged with the same version ID as the model. This creates a 'hermetic' seal around the model and its execution environment.
Consider the latency implications of environment versioning. A change in a base Docker image (e.g., switching from Ubuntu to Alpine) can significantly alter the cold-start time of a serverless inference function. By versioning these images and monitoring their performance, teams can maintain a consistent Service Level Agreement (SLA). When a performance regression occurs, the team can simply check the version history of the infrastructure to identify which change introduced the latency.
A machine learning model is a snapshot of code, data, and environment; if any one of these is unversioned, your entire system is non-deterministic.
Common mistakes in versioning
One of the most frequent errors is 'semantic versioning drift.' Teams often use tags like latest for their data or model artifacts. In a production environment, latest is a moving target. If a deployment fails and you need to rollback, latest now points to the broken version. Always use specific, immutable hashes or incrementing semantic versions (e.g., v1.2.4) for every component.
Another mistake is neglecting to version the preprocessing pipeline. Often, data scientists apply a transform—like scaling or one-hot encoding—manually and then version only the resulting model weights. However, the model weights are useless without the exact same scaling parameters (the mean and standard deviation of the training set). The preprocessing state must be serialized and versioned as part of the model artifact, often referred to as a 'pipeline object' in frameworks like Scikit-Learn or Spark ML.
Finally, teams often fail to version their evaluation datasets. As the business changes, the 'Gold Standard' test set might be updated to reflect new realities. If you compare a model version from last year against the current test set, the comparison is invalid. You must version the test sets alongside the models to ensure that performance metrics are calculated against a consistent baseline when doing historical benchmarking.
- Using 'latest' tags in production instead of immutable hashes.
- Failing to include preprocessing parameters in the model artifact.
- Inconsistent versioning between training and inference environments.
- Storing large binary data directly in Git repositories.
- Neglecting to version the test datasets used for validation.
Establishing a versioning culture
Implementing these MLOps fundamentals is as much about culture as it is about tooling. It requires a shift in mindset from 'exploratory research' to 'experimental engineering.' This means that every experiment, no matter how small, must be traceable. Initially, this feels like overhead, but it pays dividends when a model starts behaving unexpectedly in production. The ability to instantly pull up the exact data and code that produced a specific model version allows for rapid debugging.
Automation is the primary enabler of this culture. Manual versioning is prone to human error. By integrating versioning into the CI/CD pipeline, the 'paperwork' is handled by the system. For instance, when a pull request is merged, the system can automatically trigger a training run, version the output, and log the results to a registry. This ensures that the versioning remains consistent regardless of who is working on the project.
Education is also vital. Data scientists coming from academic backgrounds may not be familiar with software engineering best practices like branch management or containerization. MLOps engineers should provide templates and 'paved paths'—standardized ways of working that make the right way (versioning everything) the easiest way. This reduces friction and ensures high adoption across the organization.
What to practice this week
To transition from theory to practice, focus on these actionable steps to harden your MLOps foundation. Start small by auditing your current pipeline and identifying the least reproducible link.
- Select an existing ML script and convert all file-based data loading to a versioned approach using a DVC manifest or a timestamped SQL query.
- Implement a model registry using a tool like MLflow. Log a model along with its
conda.yamlorrequirements.txtand ensure you can load it in a fresh environment. - Create a 'Gold' test dataset and version it. Run three different model versions against this same dataset and generate a comparison report.
- Refactor your training code to automatically check if there are uncommitted changes in Git; prevent the script from running unless the state is clean.
- Containerize your inference logic. Build a Docker image that contains your model and its dependencies, and use a specific tag to deploy it to a local container runtime.
By mastering these fundamentals, you move beyond the 'black box' approach to machine learning and toward a robust, industrial-grade operation. Versioning is the bedrock upon which monitoring, retraining, and scaling are built. Without it, your machine learning efforts will remain fragile and difficult to maintain as your team and data grow.

