DEV Community

Cover image for That messy 4000-line notebook taught me to stop hating Jupyter
Juan Torchia
Juan Torchia Subscriber

Posted on Originally published at juanchi.dev

That messy 4000-line notebook taught me to stop hating Jupyter

A couple of years ago, already working as Head of Development, I got pulled into a mess that wasn't mine but ended up being mine anyway: a data science team needed their scoring model to go to production, and everything — everything — lived inside a 4000-line notebook with cells executed in random order, global variables reused six times, and a git log that showed unreadable binary diffs every time someone touched a chart.

My first instinct, dragging along all my infrastructure and Linux baggage, was to curse at the .ipynb format. "How do you even code review this? How do you merge this without crying?" But after a couple of days working with those folks, I understood something important: the notebook isn't there because the data scientist is lazy or can't code properly. It's there because it's the right tool for the job they were doing — explore, iterate, visualize, repeat. That day I learned to separate the "how do we version this" problem from the "this tool is bad" problem. It isn't. It's brilliant for what it was designed for, and that's exactly why it shows up in three independent awesome lists without anyone batting an eye.

My take after that experience: the notebook isn't the villain, the missing discipline around it is. If your team ships a raw .ipynb straight to production without a plan for versioning and cleanup, that's not Jupyter's fault — that's a process nobody owned.

What it does

Jupyter Notebook is an interactive web environment that lets you mix executable code, Markdown text, visualizations, and outputs in a single living document. The architecture underneath is more interesting than it looks: it's not a text editor with an interpreter bolted on. It's a client-server setup where the browser (the frontend) talks to a kernel — a separate process that runs your code — through a protocol based on ZeroMQ. That means you can have the kernel running on a remote GPU machine while you write from your notebook on your laptop. The Python kernel (IPython) is the most common one, but the protocol is agnostic: there are kernels for R, Julia, Scala, and a long list of others.

Every notebook is saved as an .ipynb file, which under the hood is pure JSON: code, metadata, and outputs (text, base64 images, HTML tables) all mixed into the same structure. That's where half the good stuff and half the headaches we'll talk about later come from.

# cell 1: load data, you can re-run this a thousand times
# without losing the state of previous variables
import pandas as pd

df = pd.read_csv("ventas.csv")
df.head()  # the output stays embedded in the notebook, no print() needed
Enter fullscreen mode Exit fullscreen mode
# cell 2: use %time to measure performance without instrumenting code
# this is what makes iterating so much faster compared to a .py script
%time resultado = df.groupby("categoria")["monto"].sum()
resultado.plot(kind="bar")  # the chart renders inline, right below the cell
Enter fullscreen mode Exit fullscreen mode

The integration with numpy, pandas, matplotlib is native in the sense that the entire Python scientific ecosystem was designed (or adapted) assuming it was going to live inside a notebook. It's free software, BSD licensed, maintained by Project Jupyter.

Why it's on the list

Three independent awesome lists agreeing on Jupyter isn't a coincidence — it's de facto consensus. When something shows up repeated across lists curated by different communities (general Python, data science, ML), it means it's not some six-month fad, it's infrastructure the entire community decided to adopt as a standard.

What makes it better than the obvious alternative — a .py script with print() everywhere — is that Jupyter solves the living documentation problem. A well-built notebook is simultaneously the code, the result, and the explanation of why you did what you did. You share a link or a file and the other person sees exactly what you saw, without having to run anything. In practice, that's the kind of setup that tends to help reproducibility in research contexts, and it lines up with a pattern I've been circling in earlier posts of this series about TensorFlow and PyTorch: both frameworks are built around the assumption that you'll be iterating in a notebook before you even think about a production pipeline.

When NOT to use it

Here comes the part where I have to be honest instead of selling smoke. Jupyter is an exploration machine, not a production machine. The .ipynb format stores binary outputs (base64 charts, rendered tables) mixed with the code in the same JSON, and that makes versioning with git a real pain. A notebook diff doesn't show you "I changed this line," it shows you an unreadable JSON blob because a pixel changed in a chart. Tools like nbdime exist specifically to mitigate this, and if you're working on a team with shared notebooks, installing it isn't optional, it's survival.

Also don't use it if your notebook is growing without limits — performance drops hard with files above 100MB, because the browser has to render that entire pile of accumulated outputs. The fix there isn't "power through it," it's cleaning up old outputs or straight-up migrating the stable logic into a regular .py module and importing it. And if what you need is running a data pipeline automatically and reproducibly (not exploratory), don't run a raw notebook: check out Papermill to parameterize and execute notebooks as jobs, or just convert the analysis to a plain script with Jupytext, which lets you have the same content in a versionable .py format and an interactive notebook at the same time.

This is the line I'd hold: exploration lives in the notebook, anything that runs unattended lives outside it. The moment a notebook becomes a dependency for a cron job or a production trigger, it stopped being a notebook and became untracked technical debt with a nice UI.

Wrap-up

Jupyter passed this series' filter for the same reason XGBoost and Netron did before: it's not the newest or sexiest tool, it's the one the community chose over and over because it solves a real problem without fuss. Following the ML arc of "Awesome Curated: The Tools," Jupyter fills a specific gap in that story: it's the place where the rough first draft of a model gets written, long before anyone talks about deployment.

If your team still ships raw .ipynb files as the "final" artifact with no nbdime, no Jupytext, no plan — that's the actual risk, not the tool itself.

If you missed a chapter, the full series is at /blog/series/awesome-curated-tools. See you at the next tool that survived the filter.


This article was originally published on juanchi.dev

Top comments (0)