Version Python notebooks with Git
marimo eliminates large Git merge conflicts and CI/CD challenges in Python notebooks by storing everything as clean Python files instead of JSON.

Version control is one of the biggest frustrations in the notebook ecosystem. Anyone who has worked with Jupyter notebooks in a team setting knows the pain: cryptic merge conflicts in JSON files, noisy commit histories filled with execution count changes, and the impossibility of running notebooks in CI/CD pipelines without jumping through hoops.
Instead of complex JSON files that mix code, metadata, and outputs, marimo stores everything as clean Python files. This simple change transforms notebook development from a Git nightmare into a smooth, collaborative experience.
Why Git is better with marimo
Jupyter notebooks are typically stored as JSON files that not only track code, but also metadata and cell-outputs. This format is language agnostic, which allows Jupyter to also host Julia/R code, but it’s design also turns simple changes into big merge conflicts. In Jupyter a simple cell re-run needs to update the metadata, which in turn means a Git update even though there are zero changes to the actual code.
With marimo, which stores the entire notebook in a Python file, merge conflicts look like normal Python conflicts - you can actually understand and resolve them without external tools. marimo also does not store any base64-encoded images and gives the user to export the notebook to other formats if they intend to precompute charts You can resolve conflicts using standard Git tools, your favorite merge tool, or even by hand-editing the file since it’s just Python code.
This approach also makes code reviews meaningful. Instead of reviewers struggling to parse JSON diffs that show structural changes rather than logical ones, they can focus on the actual code changes that matter. Pull requests become readable, and the review process becomes about the science and logic rather than fighting with file formats.
Python notebooks for CI/CD
While the Python file format is great for Git, it also offers pytest integration. When the optional pytest dependency is present, marimo runs pytest
on cells that consist exclusively of test code. That means that your tests can ship with your notebook and which you can access easily from CI as well.
This integration enables powerful workflows that were difficult or impossible with traditional notebooks:
- Automated testing: Your CI pipeline can run
pytest
directly on marimo notebooks, ensuring your analysis remains correct as data and dependencies change - Regression testing: Test that your analysis produces expected outputs when run against known datasets
- Data validation: Include tests that verify data quality and assumptions directly in your notebooks
- Reproducibility checks: Automatically verify that notebooks can be executed from scratch in clean environments
The pytest integration also supports standard testing patterns like fixtures, parameterized tests, and custom assertions. You can write tests that validate your data transformations, check statistical assumptions, or ensure that visualizations contain expected elements.