The Meal Kit That Knows You

A medium 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
medium
Seniority
L6

Problem

What turns a meal-kit service's orders and weekly menus into a list every customer, new or returning, can actually order and eat the moment the app opens, when recipes sell out mid-evening and opens peak near 500 per second on Sunday evenings? Design the pipeline that learns from past orders and serves that list to the app.

What they ordered says a lot about what they want next.

Business requirements

Worked solution and explanation

What this really is

This is a feature-store problem dressed up as recipe personalization. The skill being probed is splitting the work across two clocks: features computed ahead of time on a streaming path and a batch path, and a request path that only looks features up and scores them. Most candidates draw a service that joins order history to the menu when the app opens. That design hangs at the dinner-hour peak, shows first-time customers an empty screen, and recommends a sold-out kit, because nothing sits between the model and the customer.

Two feature clocks feed one online store; the request path is lookup, score, rules, log.
  • order_events (source, CDC)
  • menu_inventory (source, CDC)
  • streaming_features (transform, Flink); freshness: real-time
  • batch_features (transform, Spark); freshness: < 1h
  • online_feature_store (storage, Redis)
  • cold_start_lists (storage, Redis)
  • offline_lakehouse (storage, Delta Lake)
  • recommendation_service (transform)
  • sold_out_and_dietary_rules (quality gate, custom)
  • feedback_log (storage)
  • training_job (consumer, Jupyter)
  • app (consumer, API)
  • order_events -> streaming_features
  • menu_inventory -> streaming_features
  • order_events -> batch_features
  • menu_inventory -> batch_features
  • streaming_features -> online_feature_store
  • streaming_features -> offline_lakehouse
  • batch_features -> online_feature_store
  • batch_features -> offline_lakehouse
  • batch_features -> cold_start_lists
  • online_feature_store -> recommendation_service
  • cold_start_lists -> recommendation_service
  • recommendation_service -> sold_out_and_dietary_rules
  • menu_inventory -> sold_out_and_dietary_rules
  • sold_out_and_dietary_rules -> app
  • sold_out_and_dietary_rules -> feedback_log
  • app -> feedback_log
  • feedback_log -> offline_lakehouse
  • offline_lakehouse -> training_job
  • Sources + features: order_events, menu_inventory, streaming_features, batch_features
  • Stores: online_feature_store, cold_start_lists, offline_lakehouse
  • Serve + rules + feedback: recommendation_service, sold_out_and_dietary_rules, feedback_log
  • Consumers: training_job, app

Step 1: Move feature work off the request

streaming_features keeps recent-order signals fresh in online_feature_store; batch_features recomputes the heavy history hourly. At app open, recommendation_service does a key lookup plus a model call. Any join at request time is the part that melts at peak.

Step 2: Give the lookup miss somewhere to land

About 15% of daily opens have no user row. The service treats the miss as a branch, not an error, and reads cold_start_lists: popular-this-week lists per dietary segment, rebuilt by the batch job.

Step 3: Put sold_out_and_dietary_rules after scoring

The model ranks for relevance; the rules guarantee deliverability. sold_out_and_dietary_rules reads live availability from menu_inventory, drops sold-out items, enforces dietary filters, and applies the launch boost. Rules before scoring would force retraining every time inventory changes.

Step 4: Log what was shown, then what was clicked

sold_out_and_dietary_rules writes every impression to feedback_log with its final rank, and app writes the clicks back to the same log. Orders already arrive through order_events. All of it lands in offline_lakehouse and feeds training_job. Clicks without impressions give you no negatives to learn from.

Request-time join
Joins order history to the menu per open. Latency grows with history and traffic. New users join to nothing.
Pre-computed lookup
Reads one row from online_feature_store. Latency is flat. A miss routes to cold_start_lists.

Common follow-up questions