Case study — Shiftova

Forecasting sales better than the franchise's own formula

Shiftova is our own scheduling app for hourly teams, and one of its harder problems is predicting tomorrow's sales well enough to staff a shift properly. Instead of a trained model or an external AI service, we built a two-stage, outlier-resistant regression that learns entirely from a restaurant's own sales history — then backtested it, week by week, against the actual corporate forecasting formula it replaced.

~41%

lower error on holidays, vs. the franchise's own formula

1,211

real days scored in a walk-forward backtest, no future data leakage

0

external API calls — a hand-written, unit-tested pure function

The problem with a naive forecast

The obvious approach — “average of the last few same-weekdays” — breaks in three specific, real ways for a small restaurant's sales data:

Two stages, not one model

Two designs were tried and rejected first: one joint fit where every feature shared a single “recent counts more” weight (the recency bias leaked into the seasonal curve, pointing it the wrong way months out), and a shape/trend split with no trend term in the shape fit at all (the business's real long-run drift had nowhere to go but into the seasonal numbers instead, distorting them). What shipped is two independent fits, added together.

Daily sales historytrimmed to the most recent ~3 yearsSTAGE 1 — the shapeDay-of-week + holidays + annual seasonality + long-run trendfit over ALL of the trimmed historyAuto-detects real recurring patterns (a holiday spike, say) directly from thedata — a candidate needs 2+ years, every year well above normal, same directionoutput → Shape(day)STAGE 2 — the recent correctionHow the last 90 days trended vs. Stage 1's baseline — a hard cutoff,not a slow decay, so a real reversal actually shows up as oneoutput → level, slopeprediction = shape + level + slope × days, floored at $0a known one-off (a big catering order) can be added on top, additivelyFinal forecast for that datetagged low / medium / high confidence — by total history length onlya data-sufficiency label, not a statistical margin of error —it doesn't widen or shrink around the number itselfmore on that below

What it actually found

A candidate pattern only earns its own permanent coefficient if every year it's been seen clears a high bar — at least 2 years, every single one well above what the rest of the model already explains, all in the same direction. One weak or contradicting year withholds it entirely, on purpose: a detected pattern is permanent, so it needs far more evidence than a single fitting pass does.

Against a real restaurant's multi-year sales history, that check did detect a recurring notable day and a standout legislated holiday — and it correctly refused to split out most other holidays, and found no reliable monthly pattern at all, once day-of-week composition was accounted for.

Before the fixAfterTrue value
$4,600 (a shared “holiday” flag)$2,026 (split into its own coefficient)~$0 (the store is closed)

Roughly a 56% cut in error for that one date; every other holiday's forecast moved by under 2% — correctly unaffected, since none of them cleared the bar for their own split.

Worth stating plainly: the split coefficient is one fixed dollar amount, added on top of the day-of-week baseline. This particular holiday has only ever landed on the restaurant's quieter weekdays in the recorded history, so the same flat offset under-corrects if it ever lands on the busiest day of the week — a real, measured improvement, not a complete fix, and worth saying so rather than overselling it.

The confidence label, and a bug we caught writing this up

The forecast returns a low / medium / high confidence tag alongside every prediction — and it's tempting to read that as a real statistical confidence interval. It isn't. It's a plain data-sufficiency signal: purely how many days of sales history exist, full stop.

confidence = low     if history < 42 days
           = medium  if 42 <= history < 84 days
           = high    if history >= 84 days

It does not vary by how far out the target date is, whether it's a holiday, or how noisy either stage's fit actually was. Two forecasts tagged “high confidence” can have very different real accuracy.

While writing this case study, we checked the source comment attached to that field against what the code actually does — and it didn't hold up. It cited a threshold from a different file that no longer exists under that name, with a number (56 days) that didn't even match the real boundary (42 days) it was supposedly describing. The code itself was always correct; the comment had just drifted. We fixed it on the spot. It's a small thing, but it's exactly the kind of drift that's worth catching rather than repeating uncritically — and a habit we try to apply everywhere, not just when someone's writing a case study about it.

Backtesting it for real

The restaurant's franchisor hands every location its own corporate scheduling spreadsheet, with a built-in sales projection. We reverse-engineered its actual formula from real weekly files and cross-checked it against the restaurant's own sales: underneath the spreadsheet, it turned out to be a plain, unweighted 4-week trailing average, with an optional manual override that — across every sampled week — was never actually used.

To find out if the new forecast was actually better, rather than lean on a couple of cherry-picked dates, we ran a real walk-forward backtest: for every week with enough prior data, both formulas were computed using only data that would've been available before that week — no future leakage — then scored against the real sales that followed. 173 weeks, 1,211 scored days.

Mean forecast error, mature period (2+ years of real history behind every prediction)

All days
$481
$453
Ordinary days
$467
$451
Holiday days
$895
$530
Franchise's formula Shiftova

Shiftova wins by roughly 5.7% overall, and by roughly 41% on holidays specifically. The honest shape of that result: on an ordinary day, a trailing 4-week average is already a reasonable estimator, so the two methods land close together. The real edge is concentrated exactly where naive averaging is structurally incapable, not just less accurate — the franchise's formula has no mechanism at all to represent a holiday or a recurring notable day. An average can't know a date is different. This can.

What it still gets wrong

This is the kind of problem we like taking on — not just shipping a feature, but actually proving it's better than what it replaced.