From Notebook to Platform Pipeline
Taking Exploratory Code to Production — Without Rewriting the Logic
A notebook proves an idea. A platform runs it every night, on schedule, for the business. The gap between "works on my laptop" and "runs on Microsoft Fabric / Snowflake / Databricks / BigQuery" is where a lot of good analytics work quietly dies. The encouraging part: closing that gap is a pattern, not a product. The same five moves take an exploratory notebook — or a Python library like the Data Analytics Library — into a production pipeline, and only the final runner changes from platform to platform. This is the piece that ties the Learn the Pattern series to real, shippable code.
Five moves carry the same logic from a notebook to a scheduled platform pipeline; the runner on the right is the only platform-specific part.
1. Modularize — Cells Become Functions
A notebook is a script with hidden state: cell 12 depends on cell 4 having been run, and re-running out of
order breaks it. The first move is to lift the logic into functions and modules — each one
taking inputs and returning outputs, with no reliance on global notebook state. This is exactly the shape the
Data Analytics Library is built around: every stage is DataFrame-in, DataFrame-out,
so stages chain into a pipeline and each is independently testable.
Python · before (notebook cell) → after (module function)
# BEFORE — a notebook cell that mutates a global and hard-codes a path
df = pd.read_csv("/Users/mario/Downloads/orders_2026.csv")
df = df.dropna()
df["sales"] = df["quantity"] * df["unit_price"]
# AFTER — a pure function in a module (pipeline/transform.py)
def add_sales(orders: pd.DataFrame) -> pd.DataFrame:
"""Derive the sales column. Non-mutating: returns a new frame."""
out = orders.copy()
out["sales"] = out["quantity"] * out["unit_price"]
return out
2. Parameterize — Nothing Hard-Coded
Production code can't carry /Users/mario/Downloads, a fixed date, or a password in plain text.
Lift every environment-specific value into parameters — read paths and dates from arguments
or config, and pull secrets from the platform's secret store, never the source. The same function then runs
against a laptop CSV or a cloud table by changing inputs, not code.
Python · parameterized entry point
import os, argparse
def run(source_uri: str, run_date: str) -> None:
secret = os.environ["WAREHOUSE_TOKEN"] # from the platform's secret store
orders = load(source_uri) # path/URI is an argument, not a constant
result = build_pipeline(orders, as_of=run_date)
write(result, destination=os.environ["OUTPUT_TABLE"])
if __name__ == "__main__":
p = argparse.ArgumentParser()
p.add_argument("--source", required=True)
p.add_argument("--run-date", required=True)
args = p.parse_args()
run(args.source, args.run_date)
3. Package — Pin Dependencies, Make It Importable
"It worked yesterday" usually means a dependency moved. Turn the modules into an installable package with
pinned versions so the same code resolves to the same libraries everywhere. A
pyproject.toml with version constraints, an importable package, and a test suite (the library
ships 81 pytest tests on CI) is what lets a platform install and run your code unattended.
Python · pinned, importable
# pyproject.toml (excerpt) — pin the ranges you tested against
# dependencies = ["pandas>=2.0,<3.0", "scikit-learn>=1.3,<2.0"]
# On the platform, install and import — no copy-paste of cells
# pip install data-analytics-library==1.0.0
from analytics import predictiveanalysis as pa
model = pa.train(features, target)
4. Orchestrate — Wrap It in the Platform's Scheduler
The last move hands the packaged code to a scheduler that runs the steps in order, on a cadence, with retries and alerting. Model the work as a DAG (a flow of dependent tasks), point the scheduler at your parameterized entry point, and the pipeline becomes hands-off. This is the only step that is genuinely platform-specific — and even here the concept (a scheduled DAG of tasks) is identical everywhere.
5. Same Logic, Every Platform
The Python you wrote in steps 1–3 does not change between platforms. Only the storage it reads, the engine it runs on, and the scheduler that triggers it differ — and each maps cleanly:
| The Piece | Snowflake | Databricks | BigQuery | Microsoft Fabric |
|---|---|---|---|---|
| Storage | Tables · stages | Delta on cloud storage | BigQuery tables · GCS | OneLake |
| Run the Python | Snowpark · Python worksheets | Notebooks / jobs on clusters | BigQuery DataFrames · Dataproc / Cloud Run | Spark / Python notebooks |
| Orchestrate | Tasks & Streams | Jobs / Workflows | Cloud Composer (Airflow) · scheduled queries | Data Factory pipelines |
| Secrets & params | Secret objects · parameters | Secret scopes · widgets | Secret Manager | Key Vault · pipeline parameters |
Feature names evolve — treat this as a capability map and confirm specifics against current vendor docs before relying on them.
Tools Commonly Used
- Packaging:
pyproject.toml, pip/uv, pinned dependency ranges, pytest + CI. - Orchestration: Airflow, Dagster, Prefect, and each platform's native scheduler.
- Config & secrets: environment variables, argparse/CLI, and the platform's secret store.
- Reference code: the Data Analytics Library is already structured this way — modular, non-mutating, tested — so it drops into any of the runners above.
Best Practices
- Refactor cells into pure functions with no hidden notebook state before anything else.
- Parameterize every path, date, and credential; keep secrets in the platform's store, not the code.
- Pin dependencies and ship tests so a fresh environment reproduces your results.
- Keep the business logic platform-agnostic; isolate the platform-specific read/write/schedule at the edges.
- Make runs idempotent and parameterized by date so backfills and retries are safe.
Example: The Descriptive Pipeline, Productionized
The companion notebook from the Descriptive Analytics series runs ten steps end to end. Productionizing it is exactly this pattern: lift each step into a library function (collect → clean → transform → … → share), parameterize the source and run-date, pin the dependencies, and schedule the entry point on whichever platform owns the data. The notebook that proved the idea and the pipeline that runs it nightly share the same logic — which is the whole point of learning the pattern, not the product.