Tinker AI
Read reviews
intermediate 5 min read

Aider with Jupyter notebooks: working with .ipynb files without breaking the metadata

Published 2026-04-01 by Owner

Jupyter notebooks are JSON files with a specific structure: cells, outputs, execution counts, metadata. Aider’s default search-replace approach handles this poorly because diffs against JSON are awkward — a small code change inside a cell becomes a noisy multi-line diff that includes JSON escaping.

For serious notebook work, you need a different setup. Here’s what’s worked for me on data science projects.

The default experience

Out of the box, aider opens a .ipynb file and treats it as text. You ask for a change to a cell. Aider produces a search-replace diff against the JSON. The diff has:

  • Escaped newlines (\n instead of literal newlines)
  • Escaped quotes
  • Cell IDs and execution counts in the surrounding context
  • Potential breaking of the JSON if the search/replace is slightly wrong

About 30% of these edits go wrong. The most common failure: aider produces a diff that breaks the cell’s metadata structure. The notebook then won’t open in Jupyter because the JSON is invalid.

Option 1: jupytext to pair with .py

The cleanest approach: use jupytext to maintain a paired .py file alongside the .ipynb. Aider edits the .py, jupytext syncs to .ipynb.

Install:

pip install jupytext

In the notebook (or via Jupyter Lab), pair with a percent-format .py file:

jupytext --set-formats ipynb,py:percent notebook.ipynb

Now notebook.ipynb and notebook.py are kept in sync. The .py file has cell markers (# %%) that jupytext understands.

Add to aider:

> /add notebook.py

Edit the .py. After saving, jupytext auto-syncs to .ipynb (if you have the watcher running) or syncs on next notebook open. Outputs in the notebook are preserved across the sync.

This works extremely well for code-heavy notebooks. Aider edits clean Python; the notebook structure stays intact.

Option 2: jupyter nbconvert + manual sync

If you don’t want a permanent paired file:

jupyter nbconvert --to script notebook.ipynb

Edit notebook.py with aider. Then convert back:

jupyter nbconvert --to notebook --execute notebook.py --output notebook.ipynb

The downside: outputs aren’t preserved. The --execute re-runs all cells. For notebooks with expensive computations, this is painful.

I use this for notebooks I’d want to re-execute anyway (regenerating outputs is part of the workflow). I avoid it for notebooks with one-off expensive operations.

Option 3: aider with —map-tokens 0 and direct .ipynb edits

For notebooks where you absolutely need to edit the .ipynb directly (e.g., embedded outputs that take hours to generate), there’s a way to make aider work better:

# .aider.conf.yml
edit-format: whole
map-tokens: 0
read:
  - notebook.ipynb

The whole edit format makes aider produce the entire updated file rather than diffs. For notebooks, this is more reliable than diff-based edits.

The map-tokens: 0 skips the repo map for these sessions. Notebook content takes a lot of tokens; the repo map adds overhead without value for notebook-only work.

This costs more per turn (the model produces the whole file) but is more reliable than the default diff approach. For notebooks under 1000 lines, it’s tolerable.

What aider does well with notebooks

Generating new analysis cells. “Add a cell that plots the distribution of column X” works well. The cell appears in a clean form.

Refactoring across cells. “Extract this repeated cleaning logic into a function in an early cell, then use it in the analysis cells” is the kind of task aider handles well, especially with the jupytext approach.

Documentation and markdown cells. Aider produces good markdown for explaining what each cell does. For notebooks that double as reports, this matters.

What aider does poorly

Inline manipulation of cell outputs. Aider can suggest “the output of this cell shows X.” It can’t actually modify the output without running the cell. If you want updated outputs, you re-execute the notebook.

Complex matplotlib customizations. Asking aider to tweak a plot from “this color, smaller text, different legend position” is hit-or-miss. Matplotlib’s API is large and the model’s training has uneven coverage.

Working with kernels other than Python 3. Aider’s defaults assume Python 3. For R, Julia, or non-default Python kernels, you may need to override defaults.

A workflow for data science projects

The pattern that’s emerged:

  1. Use jupytext to pair .ipynb with .py for any notebook I’d edit with aider
  2. Aider works on the .py files
  3. When I want to inspect outputs or run cells, switch to Jupyter Lab
  4. Periodically re-run the notebook to regenerate outputs (especially before sharing)

The workflow keeps aider in the textual editing mode where it’s strong, and Jupyter in the execution mode where it’s strong. They communicate through the synced files.

Cost

Notebook work in aider tends to be more expensive than regular code work because:

  • Notebooks often have multiple unrelated tasks in one file (more context loaded)
  • Cell outputs (markdown, error messages) are tokens too
  • jupytext’s .py files include cell metadata that adds tokens

For a typical 30-minute session on a 500-line notebook: $0.80-1.50 with Claude 3.5 Sonnet. Higher than a similar-length pure-code session by maybe 30%.

When to skip aider entirely for notebooks

For exploratory data analysis where you’re iterating rapidly and the value is in the interactive feedback (run cell, see output, adjust), aider doesn’t add much. The Jupyter experience is already optimized for that loop.

I use aider for notebooks when:

  • The task is well-specified (“create a notebook that does X”)
  • The notebook is long enough that scrolling around is painful
  • Multiple cells need consistent changes (refactoring a pattern)

I skip aider when:

  • I’m exploring a new dataset
  • The task is “tweak this plot until it looks right”
  • The notebook is small and I’d type the change faster than I’d describe it

The shape of the work tells you which is which. Aider is for the structural and refactoring work. Jupyter is for the exploratory work. Both have a place; neither is the right tool for everything.