All topics
Data · Learning hub

Jupyter notes for developers

Master Jupyter with a curated set of 1 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallTest yourself — Jupyter quizMore Data notes
Jupyter

Jupyter Essentials

Jupyter Essentials Project Jupyter is the ecosystem behind interactive, cell-based computing: the .ipynb notebook file format, the kernel protocol that lets a n

Jupyter Essentials

Project Jupyter is the ecosystem behind interactive, cell-based computing: the .ipynb notebook file format, the kernel protocol that lets a notebook talk to a running interpreter in any language, and the tools built on top (JupyterLab, classic Notebook, JupyterHub, Voila, nbconvert). "Jupyter" itself is language-agnostic — the name comes from Julia, Python, and R, the three languages it originally targeted — everything else (which editor UI you use, which language kernel you run) is a separate, swappable layer on top of that core protocol.

The Notebook File Format (.ipynb)

A notebook file is plain JSON (the nbformat schema), not a binary format. It stores an ordered list of cells (code, markdown, or raw), and for code cells, the outputs produced the last time they ran are saved inline — including images, so a notebook can render its last results in GitHub or nbviewer without a live kernel. This also explains why notebooks are painful in git: outputs and execution_count change on every re-run, which is why diffs get noisy unless you strip outputs before committing.

{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {},
      "outputs": [
        {
          "output_type": "execute_result",
          "data": { "text/plain": ["6"] },
          "execution_count": 3
        }
      ],
      "source": ["2 + 4"]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": ["## Results\n", "The value above is our baseline."]
    }
  ],
  "metadata": {
    "kernelspec": { "name": "python3", "display_name": "Python 3" },
    "language_info": { "name": "python", "version": "3.12.0" }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}

Kernels & the Messaging Protocol

A kernel is a separate process that actually executes your code and maintains its state; the notebook UI is just a client. They communicate over ZeroMQ sockets using the Jupyter messaging protocol: a shell channel for execute requests and completion/introspection, an iopub channel that broadcasts outputs and status back (so multiple clients can watch the same kernel), stdin for input() prompts, and a heartbeat channel to detect a dead kernel. Because the protocol is language-agnostic JSON messages, any language can get a Jupyter kernel — IPython (Python), IRkernel (R), IJulia (Julia), and dozens more all implement the same wire protocol.

# List installed kernels and where their kernelspec (kernel.json) lives
jupyter kernelspec list

# Register a virtualenv/conda env as a selectable kernel
pip install ipykernel
python -m ipykernel install --user --name myenv --display-name "Python (myenv)"

# Talk to a running kernel directly (useful for debugging kernel connectivity issues)
jupyter kernel --kernel=python3
# prints a connection file path — jupyter console --existing <file> attaches a second client

# Remove a kernel you no longer need
jupyter kernelspec remove myenv

Execution Model: Cells Are Not the Program

The single biggest source of "works on my notebook, breaks for everyone else" bugs: a notebook's saved cell order is not necessarily its execution order. The kernel only knows about statements it has actually run, in whatever order you ran them, and every code cell's execution_count records that order. It's completely possible to run cell 5 before cell 2, delete cell 2, and end up with a notebook that only works because of state left over from a cell that no longer exists on disk.

# Cell [1]
x = 10

# Cell [2] — you run this, get 100, move on
print(x ** 2)   # 100

# Later you go back and edit Cell [1] to x = 20, but never re-run Cell [2].
# The notebook now DISPLAYS "100" next to x = 20 — stale output, not a lie
# about x's current value, but easy to misread as one.

# The only way to be sure a notebook is reproducible top-to-bottom:
# Kernel -> Restart Kernel and Run All Cells
# If that fails or produces different output, the notebook was relying
# on out-of-order execution state that no longer exists.

Interactive Widgets

ipywidgets adds live, two-way interactive controls (sliders, dropdowns, text boxes) that stay connected to Python state in the running kernel — turning a notebook into a lightweight interactive tool without a separate web app. Because the widget's state lives in a Python object, changing the slider re-runs your callback in the kernel and updates the output in place, no manual re-execution needed.

import ipywidgets as widgets
from IPython.display import display

def plot_for(n):
    print(f"Plotting with n={n}")  # replace with actual plotting code

slider = widgets.IntSlider(value=10, min=1, max=100, step=1, description="n:")
output = widgets.interactive_output(plot_for, {"n": slider})
display(slider, output)

# @widgets.interact is a shorthand that builds the control from the
# function's default argument type automatically
@widgets.interact(n=(1, 100))
def plot_interactive(n=10):
    plot_for(n)

Sharing & Running Notebooks Outside the Editor

A notebook you can only view in an editor isn't very shareable. nbconvert turns a notebook into a static HTML/PDF/slide deck; Voila serves a notebook as a live, kernel-backed web app with the code cells hidden, showing only widgets and outputs; papermill executes a notebook end-to-end with injected parameters, which is how notebooks get used as parameterized reports in a pipeline or cron job rather than just exploratory scratch space; and JupyterHub/Binder provision a hosted, multi-user (or ephemeral, link-shareable) Jupyter environment so recipients don't need any local setup at all.

# Static export — no kernel needed to view the result
jupyter nbconvert report.ipynb --to html
jupyter nbconvert report.ipynb --to pdf

# Re-execute top to bottom, then export — the reproducibility check as a CLI step
jupyter nbconvert report.ipynb --to html --execute

# Parameterized execution — swap in a different `region` each run,
# producing a distinct output notebook per invocation
pip install papermill
papermill report.ipynb out/report_eu.ipynb -p region "EU"
papermill report.ipynb out/report_us.ipynb -p region "US"

# Serve as a live dashboard — code hidden, widgets and outputs interactive
pip install voila
voila report.ipynb

Gotchas & Tips

  • Before trusting a notebook (or sharing one), do Restart Kernel and Run All — a notebook that only works out of execution order isn't actually reproducible, even though it looks fine on screen.

  • Use nbstripout (a pre-commit hook) to strip outputs and execution counts before committing .ipynb files — otherwise every re-run creates a noisy diff even when no logic changed.

  • A kernel and its notebook are decoupled — closing a browser tab does not stop the kernel, and it keeps consuming memory. Explicitly shut down unused kernels from the Running panel or `jupyter kernel list`.

  • Large DataFrame/array outputs get embedded directly in the .ipynb JSON, which can balloon file size and make git diffs unreadable — clear outputs on cells that dump large previews, or route them to files instead.

  • For anything meant to run unattended (CI, scheduled reports), prefer papermill or `nbconvert --execute` over manually clicking through cells — both fail loudly with a stack trace if a cell errors, instead of silently leaving stale output.

  • Once notebook logic stabilizes, move it into importable .py modules and call it from a thin notebook cell — notebooks are poor at code reuse (no real import story between them) and testing.

Keep your Jupyter knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever