The Models Going Stale

A hard Pipeline Design interview practice problem on DataDriven. Design the pipeline on the canvas: sources, transforms, storage and checks, then get feedback on it.

Domain
Pipeline Design
Difficulty
hard
Seniority
L6

Problem

Account and transaction activity arrives at about 50,000 events per second and feeds roughly 800 features behind 12 loan risk models. Design the pipeline and stores that serve those features to live scoring of 200 applications per second, keep their history for retraining, and record what each score used.

The model is only as good as what you feed it.

Business requirements

Worked solution and explanation

The trap

This looks like a freshness complaint. Underneath, it is a training-serving consistency problem. Everyone draws a fast store for live scoring. What separates candidates is seeing that one store on one schedule cannot carry four jobs: features from minutes ago, nightly features, years of point-in-time history, and a record of every decision. Collapse them and the model trains on today's values for old loans. It looks brilliant offline, it underperforms in production, and compliance gets a reconstruction instead of a record.

Walk the requirements

Step 1: Split the feature computation by cadence

Recent account activity goes through a streaming path (streaming_features on Flink). The slow aggregates go through a batch path (batch_features on Spark). If everything is streamed you are over-engineering. If everything runs nightly you have the exact staleness the prompt complains about.

Step 2: Give each consumer a store shaped for it

online_store holds only the latest value per entity and serves key lookups inside the request. offline_store keeps every historical value with its effective timestamp. Both are written by both paths, so neither tier has its own private definition that can drift.

Step 3: Train with as-of joins

Retraining joins on (entity_id, event_time) and takes the value that was valid at the decision, never the current one. That is the only way the offline metric predicts the production metric.

Step 4: Log the decision at scoring time

scoring_service writes the feature values, the feature version and the model version to decision_log in the same moment it returns the score. An audit is then a query against that log, with no replay needed.

The reference design

Streaming and nightly batch feature paths both write `online_store` for live scoring and `offline_store` for full-history training. `scoring_service` records every score in `decision_log` for audit.
  • event_streams (source, Kafka)
  • streaming_features (transform, Flink)
  • batch_features (transform, Spark)
  • online_store (storage, Redis)
  • offline_store (storage, warehouse)
  • scoring_service (transform)
  • decision_log (storage, object storage)
  • training_job (consumer, Jupyter)
  • inference_consumer (consumer, API)
  • audit_console (consumer, Metabase)
  • event_streams -> streaming_features
  • event_streams -> batch_features
  • streaming_features -> online_store
  • streaming_features -> offline_store
  • batch_features -> online_store
  • batch_features -> offline_store
  • online_store -> scoring_service
  • scoring_service -> decision_log
  • scoring_service -> inference_consumer
  • offline_store -> training_job
  • decision_log -> audit_console
  • Source + features: event_streams, streaming_features, batch_features
  • Stores: online_store, offline_store
  • Scoring + audit: scoring_service, decision_log
  • Consumers: training_job, inference_consumer, audit_console
One store, nightly
A nightly batch job overwrites a key-value store. Credit risk misses the last day of activity. Training joins current values onto old events, and the audit trail is rebuilt from application logs.
Two stores, two cadences
Streaming and batch paths share one definition. online_store serves the latest value, offline_store keeps the history for as-of joins, and decision_log stores what was actually scored.

Common follow-up questions