Model Evaluation · ML Systems Lab

The Walk-Forward Validation Rule: Why Every Other Backtest Is Dishonest

Your backtest shows 0.91 AUC. Your production model underperforms by 20 points. Nobody changed the pipeline. The problem is not the model — it is the backtest itself. A backtest that fits a model on all historical data and then scores it on historical windows is not forecasting. It is memorisation. Walk-forward validation is the only backtest that measures what you actually need to measure: whether a model trained up to time T can predict events after time T.

Most backtests are dishonest. Not because the engineer intended deception, but because the evaluation procedure answers a question different from the one that matters.

The question you care about: "If I train my model on data up to today and deploy it, how well will it perform next month?"

The question most backtests actually answer: "How well does a model trained on all available history describe patterns that are already in the history?"

These are different questions. The first is about prediction. The second is about description. A model that scores 0.91 AUC describing historical patterns may score 0.72 AUC predicting future events — and you will not know until you deploy.

What the standard backtest gets wrong

A typical backtest procedure: (1) gather all historical data, (2) fit the model, (3) generate predictions on held-out rows sampled from across the history, (4) measure AUC.

The problem: step 2 fits on the same time range that step 3 evaluates. Even with a held-out split, if the model is fit on data from 2022–2024 and evaluated on randomly sampled rows from 2022–2024, the model has already "seen" the periods it is evaluated on in the sense that it trained on surrounding events, the same distribution, and potentially the same users or items.

For time series and any temporal prediction task, this is a dishonest evaluation. It conflates in-distribution performance (which tells you about model fit) with out-of-distribution prediction (which tells you about deployment risk).

Walk-forward validation: the honest backtest

Walk-forward validation (also called expanding window or rolling origin cross-validation) works as follows:

1. Set an initial training end date: T0. 2. Train the model on all data with timestamp <= T0. 3. Generate predictions on events in [T0, T0 + H] where H is your forecast horizon. 4. Record performance. 5. Advance T0 by one period (one week, one month). Repeat from step 2.

At each step, the model has never seen the evaluation window. The predictions are genuinely out-of-sample. The backtest directly simulates what will happen in production: a model trained up to date T is asked to predict events after T.

Why this is harder to implement and why it matters

Walk-forward validation is computationally expensive. You retrain the model at each step. For complex models with large datasets, this means 10–20 full training runs instead of one. Most teams skip it.

The cost of skipping: you deploy with false confidence. You attribute production underperformance to drift when the true cause is that your backtest was measuring the wrong thing. You retrain on more data and get a similar result, because the new backtest is still dishonest.

The expanding vs rolling window choice

Expanding window: each training fold includes all data up to T0. The training set grows. This is appropriate when you believe the model benefits from more historical data and when older data is still representative.

Rolling window: each training fold uses only the most recent N periods. Older data is dropped. This is appropriate when you believe recent data is more predictive than old data, or when the data generating process changes over time (structural breaks, seasonal regime changes).

For most production ML use cases with stable data distributions: expanding window. For forecasting tasks with trend or seasonality changes: rolling window.

The performance gap diagnostic

Run both the standard backtest and a walk-forward validation on your current model. The gap between them tells you something specific:

  • Standard backtest AUC 0.91, walk-forward AUC 0.88: small gap, the model generalises well to new time periods. Deploy with reasonable confidence.
  • Standard backtest AUC 0.91, walk-forward AUC 0.74: large gap, the model is fitting to historical patterns that do not generalise. Your features may have temporal leakage, or the model is overfitting to historical signal that does not persist.
  • The rule: if you cannot describe your validation procedure as walk-forward, your backtest metrics are not production estimates. They are training diagnostics.

    Continue interactively
    Read this post inside ML Systems Lab — with Simplify toggle, interview Q&As, inline glossary, and the MLE Path forward pointer.
    Open in MSL →