BXUP Track progress & test yourself — free
Data Science & ML/AI · Playbook · Core · ~5 min read
DATA SCIENCE & ML/AI · PLAYBOOK · CORE

Notebook to Production Package: Agentic Refactor with Review Gates

The gap between "works in my notebook" and "runs in the pipeline" is where DS projects go to die.

Tier: Core · Workflow area: Productionization

Agentic coding tools now execute the mechanical 80% of that refactor — module extraction, typing, tests, CLI, CI — in an afternoon, but only safely under review gates: an equivalence harness proving the package reproduces the notebook's numbers, and human review at each stage boundary. Heavy generation, mandatory review.

When to use

  • A notebook's model or analysis is being promoted to scheduled/production use.
  • You inherit a 2,000-line notebook that is now load-bearing.
  • Handing work to engineers who (reasonably) refuse to deploy .ipynb.

Skip full packaging for one-off analyses — over-engineering exploratory code is its own pathology.

Prerequisites

  • An agentic coding tool: one that can read, edit, and run code across files (Claude Code, Cursor agent mode).
  • A reproducing notebook: it runs cleanly top-to-bottom with pinned inputs (fix this first — you cannot refactor what does not reproduce).
  • Test and CI tooling: pytest, a formatter/linter (ruff), and a CI runner available.
  • Captured ground truth: run the notebook once and save key outputs — metric values, prediction sample (preds.head(50)), feature matrix hash — to files. This is your equivalence oracle.

The workflow

  1. Freeze the oracle. Add a final notebook cell writing oracle/metrics.json, oracle/predictions_sample.parquet, and oracle/feature_hash.txt (e.g. hashlib.sha256(pd.util.hash_pandas_object(X).values).hexdigest()). Commit these. Every refactor stage must reproduce them.

  2. Plan with the agent before any edits:

    Read notebook churn_v3.ipynb. Propose a package structure for production
    (target: daily batch scoring via CLI). Constraints:
    - src/churn/{data.py,features.py,train.py,predict.py,config.py}, tests/, cli
      via argparse or typer, pyproject.toml, no notebook imports.
    - Config (paths, dates, hyperparams) out of code into a yaml.
    - Identify: hidden state from out-of-order cells, hardcoded paths, dead
      cells, and anything nondeterministic (unseeded ops, set iteration).
    Output the plan and the risk list. Do not write code yet.
    

    Review the plan — especially the nondeterminism list, which is what usually breaks equivalence.

  3. Refactor in gated stages, equivalence-checked at each. Stage order that works: (a) extract data loading + feature building; (b) extract training; (c) extract prediction/scoring; (d) add CLI + config; (e) tests + CI. After each stage, the agent must run the equivalence harness:

    Stage (a) done? Run: python -m churn.features --config config.yaml, then
    compare the feature matrix hash to oracle/feature_hash.txt and show me the
    diff of predictions_sample against the oracle. If anything differs, stop
    and report the cause — do not "fix" the oracle.
    

    You review the diff and the code at each gate. Never let the agent proceed through two stages on green-it-said-so.

  4. Make hidden state explicit. Notebooks accumulate order-dependent state (a cell run twice, a variable overwritten). Where equivalence breaks, the usual suspects: cell-execution order, implicit dtype changes, global random state consumed differently. The agent finds these fast when told to bisect: Binary-search the divergence: hash intermediates after each transform step in both code paths and find the first mismatch.

  5. Generate the test suite with teeth (see pb-10 for depth): unit tests for each transform with tiny fixture frames, the as-of/leakage synthetic-row test if features are temporal, a smoke test running the full CLI on 1,000 sampled rows, and the equivalence test itself as a permanent regression test against committed oracle files.

  6. CI and review. Agent writes the GitHub Actions workflow (lint, mypy on public interfaces, pytest, the smoke test). Then a real pull request with human review — review the diff, not the agent's summary of it. Anything you do not understand, have the agent explain, then verify the explanation against the code.

  7. Deprecate the notebook. Replace its body with a header pointing at the package and a thin demo importing from src/. Two sources of truth immediately diverge.

Warning

An agent that cannot make the numbers match will eventually propose updating the oracle "since the new values look reasonable." That proposal is always a stop-and-investigate, never an accept.

Tool variants

  • Claude Code (CLI agent): strongest fit — multi-file edits, runs the harness itself, works in your real repo; gate it with explicit "stop after stage N" instructions.
  • Cursor / IDE agent mode: same workflow with more granular visual diff review per file; good when you want to watch every edit.
  • Chat-only fallback: paste the notebook module-by-module and assemble manually; slower, but the oracle + staged-equivalence method is identical.

Verification

  • Equivalence tests pass: metrics match oracle to float tolerance, prediction sample identical, feature hash identical.
  • Fresh-clone test: pip install -e . && pytest && churn predict --config ... works on a clean machine/container.
  • CI green, including the smoke test on sampled data.
  • No notebook imports anywhere in src/; config fully externalized (grep for hardcoded paths and dates).
  • A colleague can run scoring end-to-end from the README in under 15 minutes.

Pitfalls

  • Refactoring and "improving" simultaneously. The agent will want to fix the imputation strategy it dislikes mid-refactor. Refuse: equivalence first, improvements as separate PRs after. Mixed changes make the oracle useless.
  • Float-tolerance theater.assert abs(a-b) < 1 passes everything. Tolerances should be tight (1e-9 for deterministic paths, documented looser bounds only where parallelism genuinely reorders reductions).
  • Seed illusions. Same seed, different call order → different draws. If equivalence breaks only in stochastic steps, bisect the random-state consumption order.
  • Test suites that mock everything. Generated tests love mocking pandas ops into tautologies; fixture-based tests on tiny real frames are the standard to demand.
  • Skipping the plan gate. Letting the agent restructure 2,000 lines in one shot produces a diff nobody can review — staged gates exist to keep diffs reviewable.

Restricted-environment variant

  • The agent needs code, not data: run it against the repo with data paths stubbed and a 1,000-row synthetic or sampled fixture checked in for the harness. Real-data equivalence runs happen locally by you.
  • Air-gapped: local code models via Ollama inside the IDE can do stage-by-stage extraction (smaller diffs per prompt); the oracle harness carries more of the safety load, so build it first and make it strict.
  • If the notebook itself contains embedded outputs with sensitive data, jupyter nbconvert --clear-output before any assistant sees it.

Success criteria

  • Notebook → installed, tested, CI-gated package with byte-equivalent (tolerance-documented) outputs.
  • Total elapsed time under a day for a notebook that would have taken a week manually.
  • Every stage boundary has a human-reviewed diff on record.
  • The package survives the fresh-clone test and its first scheduled production run without intervention.
This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in