You have a model. Now ship it.
Sessions b1-b8 took Lumen's daily sessions from a raw series to a backtested model with honest prediction intervals. But the model still lives in your notebook. Production forecasting is almost never a live model behind every page request - it is a batch job: refit on a schedule, write the forecast and its interval to a store, and let the business read that store. This session builds exactly that pipeline and the contract that hands it to engineering.
Forecasting is a batch job, not a live model 7 min live
A classifier scores each request as it arrives. A forecast does not work that way: the answer for the next 14 days barely changes minute to minute, and recomputing it on every dashboard load is pure waste. The production pattern is retrain-then-serve - a scheduled job refits the model as new data lands, writes the forecast and its prediction interval to a store, and serving becomes a cheap lookup. Offline you refit; online you serve.
LiveThe data pipeline that lands each new day cleanly3 min▶
The refit is only as good as the day you feed it. The b1 resample discipline is not a notebook nicety - in production it is the ingest contract:
- Land, then resample. Raw events arrive irregular. The pipeline resamples to the same fixed daily grid the model was trained on - the same frequency, every run.
- No silent gaps. A missing day is not a zero. Fill, interpolate, or flag it explicitly, exactly as in b1, before it reaches the model.
- Idempotent. Re-running yesterday's job must produce the same series, not a double count. Key on the date.
The stale-data failure is silent. A model can keep producing plausible forecasts long after the upstream feed broke - it just extrapolates the last good day. The pipeline must check freshness (did today's row actually arrive?) and refuse to serve a forecast built on stale input rather than quietly mislead the business.
Self-studyPersisting the model vs persisting the forecast2 min read▶
Two things can be saved, and they answer different questions:
- The forecast (values + interval, per date) - this is what the business reads. Persist it every run; it is small and cheap to serve.
- The fitted model (its parameters) - persist it if refitting is slow or if you need to reproduce a past forecast for audit. For light models like ETS, refit-from-scratch each run is often simpler and safer than un-pickling a stale object across library versions.
Rule of thumb: always persist the forecast; persist the model only when you have a concrete reason (cost, audit, reproducibility).
Retrain cadence: how often to refit 5 min live
"Retrain" here means refit on the newest window of data so the forecast reflects the latest days. Cadence is a trade-off. Refit too rarely and the forecast goes stale - it misses a new trend break or a shifted season. Refit too often and you pay in compute cost and churn - the numbers jitter run to run, and the business loses trust in a forecast that changes every hour. The right cadence tracks how fast the series actually moves and how often the decision is made.
LiveThe DS-to-engineering handoff is a contract3 min▶
The most expensive production bugs are not model bugs - they are handoff bugs. The forecast crosses from data science to engineering, and if the meaning of the numbers is ambiguous, the dashboard will quietly show the wrong thing. Write the contract down:
| Contract term | What it pins down |
|---|---|
| Horizon | How many periods ahead (H = 14 days). The dashboard must not plot past it. |
| Granularity | Daily. Not "days" some places and "weeks" others. |
| Interval | Every point ships with a lower and upper bound and its coverage level (e.g. 90%). |
| As-of / freshness | The last actual date the forecast was built from, so the dashboard can show "as of". |
| Units + timezone | Sessions, in the store's timezone. Ambiguity here silently doubles or shifts a day. |
yhat, lower, upper. A dashboard that plots the point without the band is showing false confidence.
A scheduled refit-and-store routine ★ 14 min · everyone builds
Build the offline half of the pattern: a function that ingests the latest daily data, refits Holt-Winters, and writes the forecast plus its prediction interval to a store. The store here is a dict / JSON standing in for a warehouse table - swap it for your real sink later.
Rebuild the Lumen daily series from b1 (trend + weekly seasonality + noise), on a clean daily datetime index.
Write one function: ingest the newest data, refit ETS/Holt-Winters, forecast H days with an interval, and write rows to the store.
Run it. The store now holds one row per future date with yhat, lower, upper, and an as_of stamp.
Persist the store to JSON - this is what the API will read. Serving is now a lookup.
as_of. The interval here is a simple residual-based band; b8's backtested coverage is the honest way to size it.
A minimal forecast API the dashboard reads ★ 8 min · build your own
Build the online half: a tiny FastAPI service that reads the store and returns the forecast + interval as JSON. This is the contract the dashboard consumes - no model runs here, it is a lookup. The dashboard / UI part is called out separately: that layer just draws the JSON.
Load the JSON store the refit job wrote. The API never fits a model - it only reads.
Expose GET /forecast/{metric} returning as_of, horizon, granularity, coverage, and the rows (date, yhat, lower, upper).
Return a clean 404 when the metric is unknown, so the dashboard fails loud, not silent.
The dashboard (a separate UI layer) calls this endpoint and plots the band around the line.
The dashboard is where value is won or lost. A perfectly backtested model behind a dashboard that hides the interval, omits the "as of" date, or plots past the horizon will mislead the business - and they will (rightly) stop trusting the forecast. Treat the API contract and the dashboard as part of the model's accuracy, not an afterthought. Buying a vendor forecast product still leaves this last mile to you.
Try it yourself - this week ◐ 30-45 min total
- Take your best model from b3-b8 and wrap it in a
refit_and_storefunction that writes forecast + interval to a JSON (or parquet) store, stamped withas_of. - Add a freshness guard: if the newest expected day is missing, refuse to overwrite the store and raise instead of serving stale numbers.
- Stand up the FastAPI endpoint locally and hit
GET /forecast/sessions. Confirm it returns the interval, not just the point. - Write your one-page DS-to-engineering contract: horizon, granularity, interval + coverage, as-of, units, timezone.
- Decide your retrain cadence in one sentence and justify it against the decision it serves.
Sources covered (cited)
Productionizing a forecast is industry practice rather than a single paper; it builds on the modelling canon this track has taught. This session covers:
Three questions before you go 🎯 ◐ 90 seconds
1 · Why is production forecasting usually a batch job rather than a live model call?
Retrain-then-serve: a scheduled job refits and writes the forecast + interval to a store; serving is a lookup. Recomputing per request is pure waste.
2 · What is the risk of retraining too often?
Too rare = stale; too often = cost and churn. Match cadence to how fast the series moves and how often the decision is made.
3 · What must the DS-to-engineering contract pin down?
Ambiguous handoffs are the most expensive production bugs. The dashboard must know the horizon, granularity, interval + coverage, and freshness - and never plot a bare point.