Tutorials › Nonlinear Mixed-Effects Models (NLME) › Versioning Data, Code, and Model Specifications

Workflow and Reporting · Tutorial 787 of 797

Versioning Data, Code, and Model Specifications

Build a traceable link between each reported NLME result and the frozen data, code, and model specification used to obtain it.

Intermediate 11 min read

What You'll Learn

  • Distinguish a version label from an immutable snapshot of analysis inputs.
  • Use content hashes to check whether files or complete input sets have changed.
  • Separate data, code, and model specification versions in an NLME workflow.
  • Create a compact input manifest that identifies the source of a reported result.
  • Recognize the limits of version control, hashes, and data snapshots.

Why Versioning Matters for NLME Results

An NLME result is not determined by a model name alone. It depends on the analysis data, the code that implements the analysis, and the model specification encoded by that code and its configuration. If any of these inputs changes, a later fit may answer a different question or produce a different estimate, even when the analyst still calls it the same model.

Versioning makes those inputs identifiable. It provides a disciplined way to distinguish a changed data extract from a correction to the model code, or a deliberate change in the structural or variability assumptions. The aim is not merely to preserve files. It is to make it possible to say precisely which versions were used for a reported result, and to detect when a proposed replacement is not the same input.

Definition: A version is an identified state of an analysis input, such as a data snapshot, a code revision, or a model specification. An immutable snapshot is a preserved state that is not edited in place: a change creates a new version while the earlier state remains available. A content hash is a fixed-length value calculated from file contents; with a suitable cryptographic hash, matching values provide strong practical evidence that the contents match, but do not prove that the contents are correct or appropriate.

What Must Be Versioned

For a fitted NLME model, keep the identities of at least three input classes distinct. First, identify the exact analysis dataset, including the records and variable definitions supplied to the model. Second, identify the source code and configuration files that implement data processing and model fitting. Third, identify the model specification: the scientific and statistical choices the analysis is intended to represent.

A model specification should be explicit enough to distinguish meaningful alternatives. Depending on the analysis, it can state the structural model, the random effects and their covariance assumptions, the residual error model, covariate relationships, parameter constraints, and the observation model. An implementation may put some of these choices in source code and others in a configuration file. Versioning both the code and a readable specification helps make the intended model clear and helps detect disagreement between the two.

A version label such as “final” or “model 3” is not sufficient by itself. It has meaning only if it resolves unambiguously to preserved input contents. A Git commit can identify a state of tracked files, but it does not automatically preserve every external file used by an analysis. For example, a data file stored outside the repository, a manually edited spreadsheet, or a configuration file excluded from version control must be identified separately or captured in a snapshot.

Input classWhat its version should identifyTypical failure if omitted
Analysis dataThe exact frozen records and variables supplied to the analysisA refreshed extract silently changes the analysis population or values
CodeThe source files and configuration used to prepare data and define the fitA later code edit is mistaken for the code that produced an earlier result
Model specificationThe intended structural, variability, residual, and covariate assumptionsA model label hides a substantive change in assumptions

Snapshots, Hashes, and Manifests

A practical versioning scheme combines human-readable labels with machine-checkable identifiers. Labels such as “analysis dataset 1.2” or “specification 2.0” are easy to discuss, while hashes help verify the contents associated with those labels. Record both when feasible. A label alone can be accidentally reused; a hash alone is difficult for people to interpret.

For a collection of files, a manifest can list each file’s path, size, and content hash, along with a version label and a short description of the snapshot. If the dataset is built from source files through transformations, identify both the frozen analysis dataset and the code or documented procedure that created it. The dataset hash identifies the delivered file contents; it does not, by itself, explain how those contents were produced.

The principle behind this check is straightforward. Let \(H(x)\) denote a cryptographic hash of the bytes in file \(x\). If a file changes, its hash will ordinarily change as well. Thus, comparing \(H(x)\) with a stored reference hash tests whether the present bytes match the recorded bytes. For multiple inputs \(x_1,\ldots,x_k\), a manifest records the separate hashes and their file identities; it should not rely on an ambiguous concatenation of file contents. Hashes are practical integrity checks, not mathematical proofs against every possible collision, and they say nothing about whether the data or model is scientifically valid.

Worked Example: Freezing an Analysis Dataset

Suppose a hypothetical analysis uses a cleaned dataset with 240 participants and 6,840 concentration records. The source extract is named extract_2026-04-12.csv, and the analysis-ready file is pk_analysis_v1.0.csv. The analyst calculates a content hash for the analysis-ready file and records the following illustrative, shortened identifier: SHA-256 81ac…2f70.

The analyst saves the file without further edits, records that it contains 240 participants and 6,840 records, and adds the source extract identifier and data-preparation code revision to a manifest. This identifies both the exact input file and the stated origin of the snapshot. The count is a useful consistency check, but it is not a substitute for the hash: two different files can have the same number of records.

A later correction changes one concentration from 12.4 to 12.1 ng/mL. The corrected file is not saved over version 1.0. It receives a new label, pk_analysis_v1.1.csv, and a new hash. The two versions now remain distinguishable. Any result based on version 1.0 continues to point to the original file; analyses using the correction must identify version 1.1. The units and record counts alone would not reveal this one-value change, whereas the content hash is expected to do so.

Connecting Code and Specification to a Result

A robust identification of analysis inputs is a tuple: a data snapshot identifier, a code revision identifier, and a model specification identifier. This tuple is more informative than a single model nickname because each part can change independently. For example, a dataset may be corrected while the model is unchanged, or the specification may be revised while the data and fitting code remain fixed.

A specification should not be treated as interchangeable with its implementation. The specification describes the model that is intended; the code is the executable representation. A code change might be a bug fix that brings the implementation into agreement with the same specification, or it might introduce a new model choice. Record enough detail to tell which kind of change occurred. A brief change description helps, but it does not replace preserving the actual versions.

Worked Example: Identifying the Inputs Behind a Reported Estimate

Consider a hypothetical report stating that the estimated typical clearance is 4.82 L/h. Its input manifest identifies dataset snapshot PKDATA-1.0 (hash 81ac…2f70), code commit c7e91b4, and model specification CL-2.1. The specification states a one-compartment disposition model, exponential between-subject variability on clearance, and a proportional residual error model.

To check what the result refers to, match each reported identifier to the preserved input: confirm the dataset hash against the dataset file; resolve the code commit to the source and configuration files; and retrieve specification CL-2.1. The report is then tied to a particular data file, code state, and set of model assumptions. The estimate’s units, L/h, remain attached to the result, while the identifiers establish which inputs it summarizes.

Suppose a later code revision changes the residual-error implementation, but the analyst keeps the label “CL-2.1” and the data are unchanged. The tuple is no longer the same because the code commit has changed. The change should be assessed and described, and the result should be identified with the new code revision. If the revised implementation also changes the intended residual model, the specification needs a new version as well. A result should not be attributed to old identifiers merely because the model nickname or output filename is unchanged.

A Short Justification for Immutable Input Records

The reason to preserve each input state rather than overwrite it follows from the dependency of the reported result on all of the inputs. Write \(R(D,C,M)\) for a reported result obtained using dataset \(D\), code \(C\), and model specification \(M\). If any input changes, the new result is \(R(D',C,M)\), \(R(D,C',M)\), or \(R(D,C,M')\), not automatically the same result from the same analysis.

$$ (D,C,M) \ne (D',C,M) \quad\Longrightarrow\quad \text{the input identity is different, even if } R(D,C,M)=R(D',C,M). $$

The equality of two numerical outputs would not make their inputs identical: rounding, low sensitivity, or compensating changes can produce the same displayed number. Conversely, a small input change may alter estimates or uncertainty. Preserving immutable versions therefore protects the meaning of the report independently of whether the output happens to change.

1
Freeze the analysis data.
Save the exact data file supplied to the model, give it a unique version label, and calculate its content hash. Do not silently replace that file after analysis begins.
2
Identify the code state.
Record the revision of the source code and configuration used. Confirm that external files required by the analysis are also identified and preserved.
3
Identify the model specification.
Record the intended model assumptions and assign a version that changes when a substantive specification choice changes.
4
Attach the identifiers to reported results.
For each result being reported, state the data snapshot, code revision, and specification version together. Do not infer these later from a filename or model nickname.

Worked Example: A Specification Change Without a Data Change

A hypothetical team has frozen dataset PKDATA-1.0 and code commit c7e91b4. The current specification, CL-2.1, uses an exponential random effect on clearance. The team proposes a revised specification, CL-2.2, that adds an exponential random effect on volume. No data file or code file has yet changed.

The specification has changed because the random-effects structure is different. The team records CL-2.2 as a new specification version rather than overwriting CL-2.1. If the model code is subsequently updated to implement the added random effect, that code change receives a new code revision too. The identifiers for the revised analysis are therefore PKDATA-1.0, the new code revision, and CL-2.2.

Keeping the dataset identifier fixed is informative: it shows that the change was not caused by a new data snapshot. Keeping CL-2.1 available preserves the definition of the earlier model. The team can then describe the intended difference precisely without implying that the two fits used different participants or observations.

Diagnostics, Limitations, and Common Mistakes

Versioning can fail in ways that are not visible from the final estimate. Check that each recorded identifier resolves to an available file or revision, that hashes match the preserved contents, and that the manifest names every relevant input rather than only the main model script. Confirm that the written specification and implemented code agree on the model choices being claimed. An untracked configuration file or a data file replaced in place can break the chain even when the main code repository appears unchanged.

Version control does not ensure that an analysis is correct, that a model is appropriate, or that an estimate is reproducible on every computing system. A hash shows a match to bytes, not sound data handling. A commit identifies tracked code, not necessarily external data or every local setting. Immutable snapshots also require controlled storage, access procedures, and backups. In settings involving confidential data, do not place identifiable records in a repository merely to make versioning convenient; use an approved secure storage process and record a safe, permitted identifier for the frozen snapshot.

  • Mistake: “The file is called final, so it is the version that produced the report.” A filename can be reused or overwritten. Record a unique version and verify the contents with a hash.
  • Mistake: “The code commit is enough to identify the analysis.” The commit does not identify untracked data or an external configuration. Identify those inputs separately.
  • Mistake: “The result did not change, so the model version did not matter.” Equal displayed results do not imply equal inputs. Preserve and report the changed input identity.
  • Mistake: “A hash proves the data are correct.” It only helps check whether contents match a reference. Scientific review and data-quality checks remain necessary.

The introductory material on modularizing NLME model code and configuration provides a useful basis for keeping implementation choices organized. Versioning adds a separate discipline: preserve each state and identify the exact data, code, and specification associated with a reported result. This is narrower than cataloguing all analysis runs or documenting every computational detail; its central task is to make the input identities stable and explicit.

Key takeaway: A reported NLME result should point to a preserved analysis-data snapshot, a specific code revision, and a specific model specification. Unique labels aid communication; hashes and immutable storage help verify that those labels still refer to the intended contents.

Check Your Understanding

Answer each question, then compare your reasoning with the solutions.

  1. A corrected data extract has one changed concentration but the same participant and record counts. Why should it receive a new snapshot identity?
  2. What three input identities should accompany a reported NLME result?
  3. A repository commit is unchanged, but the analysis uses a new external configuration file. Is the analysis input identity unchanged? Explain.
  4. Two versions produce the same estimate to two decimal places. Does that establish that the inputs were identical?
  5. What does a matching content hash establish, and what does it not establish?

Solutions. (1) The file contents differ, so it is a different data snapshot even though its counts match. (2) The analysis-data snapshot, code revision, and model specification version. (3) No. The external configuration is part of the analysis inputs and must be identified and preserved separately. (4) No. Different inputs can produce the same rounded output. (5) It provides strong practical evidence that the bytes match the recorded file; it does not establish that the file is scientifically correct, complete, or appropriate.