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:
- One bad day drags the average. A single mistyped entry among ordinary days can pull a naive weekday average up by nearly 10x.
- A real trend reversal gets averaged away. Years of decline followed by a genuine recent turnaround reads as “no trend” once every day counts equally.
- A real recurring pattern gets missed, or bleeds into the wrong weekday. A holiday, or a date like Halloween, either goes unrepresented, or its spike gets blended into whatever day of the week it happened to land on that year.
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.
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 fix | After | True 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 daysIt 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)
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
- A detected holiday effect is one fixed dollar amount — it under- or over-corrects when a future date lands on a different weekday than most of what was observed.
- Only fixed calendar dates are recognized. A pattern tied to a moving date, or to something other than the calendar, isn't representable — no matter how consistent it is in the real data.
- With only a year or two of history, the seasonal shape itself is still imprecise — too few real cycles to fully separate a genuine effect from a single-year fluke.
- A one-off manual adjustment (a known large order, say) never touches the training data — it's applied strictly after the forecast runs, so it can't leak into next year's numbers by construction.
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.