marimo: Dependency-Graph Notebooks That Fix Jupyter's Hidden State

The article analyzes marimo, a Python notebook tool that replaces Jupyter's manual execution order with a reactive dependency graph, saves notebooks as plain .py files for Git-friendly diffs and tooling, supports interactive widgets and SQL within the same model, and enables dependency management via uv and PEP 723, while noting trade-offs like lazy mode for long-running cells and mutable object caveats.

IT Services Circle
IT Services Circle
IT Services Circle
marimo: Dependency-Graph Notebooks That Fix Jupyter's Hidden State

Jupyter notebooks suffer from hidden state and execution-order problems: cells run in a long-lived kernel, variables persist after deletion, and the JSON-based .ipynb format makes Git diffs noisy and tooling difficult. marimo addresses these by changing the execution model and file format.

01 Execution Model: Dependency Graph Instead of Manual Order

marimo builds a directed acyclic graph (DAG) of cell dependencies by analyzing variable definitions and references. When an upstream cell changes, downstream cells automatically re-run. A minimal example:

pip install marimo
marimo edit hello.py

Cells:

x = 10
y = x * 2
print(y)  # 20
z = x + y
print(z)  # 30

Changing x = 10 to x = 50 triggers automatic recomputation of y and z. marimo enforces constraints: a variable can be defined in only one cell, dependencies must be acyclic, and deleting a definition cell removes the variable from the kernel — eliminating "ghost variables."

02 Plain .py Files for Engineering Integration

Notebooks are saved as regular Python files, not JSON. This enables: git diff shows readable code changes.

Linters (Ruff) and type checkers work directly. pytest can test functions defined in the notebook.

No manual copy-paste step to turn exploration into a script or module.

The article notes this doesn't automatically impose good software engineering practices (function boundaries, testing, data paths), but it removes the serialization barrier.

03 Interactive Widgets and SQL Within the Reactive Model

Widgets become Python variables; downstream cells react to their values without callbacks:

import marimo as mo
slider = mo.ui.slider(start=1, stop=100, value=10, label="Sample size")
slider
df.sample(slider.value)

SQL cells can query in-memory DataFrames ( df) and feed results back into Python:

SELECT region, SUM(revenue) as total
FROM df
GROUP BY region
ORDER BY total DESC

Database connectivity still depends on environment-specific configuration.

04 Dependency Declaration, App Mode, and Static Export

Dependencies are declared via PEP 723 inline metadata, consumed by uv:

# /// script
# requires-python = ">=3.11"
# dependencies = [
#     "pandas==2.2.0",
#     "scikit-learn==1.4.0",
#     "marimo",
# ]
# ///

Run with uv run marimo edit notebook.py. The same file can run as an interactive app ( marimo run notebook.py) hiding the editor, or export to a static WebAssembly bundle ( marimo export html-wasm notebook.py -o dist) for hosting on GitHub Pages. The article cautions that not all Python packages work in WASM and that binary dependencies, data paths, and OS differences still require verification.

05 Migration from Jupyter: Structural Changes Required

A conversion tool exists: marimo convert old.ipynb > new.py. However, notebooks with repeated variable definitions, circular references, or reliance on kernel execution order must be restructured manually. The article frames this as a benefit: hidden dependencies become explicit.

Trade-offs include:

Long-running cells can use lazy mode to mark themselves stale instead of auto-re-running.

In-place mutation of shared objects (e.g., df["new_col"] = transform(df)) is invisible to the dependency graph; returning new objects is safer.

06 When to Adopt marimo

The author suggests marimo fits workflows that need:

Notebooks handed off to others, not just personal kernels.

Git diffs, linting, testing, and script execution.

Automatic downstream updates when upstream data changes.

Lightweight app delivery without rewriting in Streamlit.

Willingness to make data flow explicit and avoid implicit state.

Teams heavily invested in Jupyter infrastructure or reliant on long-lived shared kernels may not benefit from immediate migration. A trial on a new project is recommended.

marimo's core contribution is not a prettier UI but a rethinking of notebook fundamentals: explicit execution dependencies, plain Python files, and a smoother path from exploration to reproducible, reviewable, deployable code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

reactive programmingJupyterdependency graphuvdata science toolingmarimoPEP 723Python notebooksWASM export
IT Services Circle
Written by

IT Services Circle

Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.