Reproducible notebooks and version control

Jupyter · Advanced · 14 min

A notebook that only works on one person's machine, in one kernel session, is not a result anyone can trust.

Reproducibility problems come from three sources: state, environment and files.

State. Out-of-order execution and deleted cells leave variables that the notebook no longer defines. Restart and Run All before sharing, and make CI run jupyter nbconvert --execute (or papermill) so broken notebooks fail fast. Set random seeds explicitly.

Environment. Record dependencies with pinned versions (requirements.txt, uv.lock, environment.yml) and use a dedicated kernel per project. Print key versions in the first cell when results matter, and avoid absolute paths to your home directory; use paths relative to the project.

Files and Git. .ipynb files are JSON that store outputs, execution counts and metadata, so diffs are noisy and images bloat history. Options:

  • nbstripout installs a Git filter that removes outputs on commit (nbstripout --install).
  • Jupytext pairs each notebook with a plain-text script, for example py:percent format with # %% cell markers. Review and diff the .py file; Jupytext keeps both in sync when you save. Many teams commit only the text version.
  • nbdime provides notebook-aware nbdiff and nbmerge, and Git integration via nbdime config-git --enable.

Keep heavy logic in importable modules with tests, and let notebooks orchestrate and explain. A notebook that is mostly calls to well-named functions is easy to rerun, review and trust.

Example

# Pair a notebook with a percent-format script and keep them in sync
jupytext --set-formats ipynb,py:percent analysis.ipynb
jupytext --sync analysis.ipynb         # updates whichever is older
head -3 analysis.py
#   # ---
#   # jupyter:
#   #   jupytext:     (YAML header, then cells marked with # %%)

# Strip outputs automatically on commit
pip install nbstripout && nbstripout --install

# Notebook-aware diffs and merges in git
nbdime config-git --enable
git diff analysis.ipynb                # now shows cell-level changes

# Prove it runs from scratch (e.g. in CI)
jupyter nbconvert --to notebook --execute analysis.ipynb --output /tmp/ci.ipynb

Remember

  • Restart and Run All, and execute notebooks in CI to catch hidden state
  • Pin the environment and use relative paths and explicit seeds
  • Use nbstripout, Jupytext and nbdime to make notebooks Git-friendly

Watch out for

  • Committing notebooks with large outputs, so every rerun produces a huge, unreadable diff.

Check yourself

  1. What does `jupytext --set-formats ipynb,py:percent` give you?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: jupytext.readthedocs.io/en/latest/