The Big Trade-offs
Six forks that appear in every ML data design conversation. Each as a decision matrix with the condition that flips it — and the one-line answer that defends the fork instead of saying "it depends".
How to use a matrix in the room
A trade-off matrix is not something you recite; it's the shape of your reasoning when the interviewer says "why not the other one?" The pattern that works: name the requirement that dominates, pick, state the cost honestly, and name the flip condition. The rows below are the dimensions interviewers actually pull on. Learn the flip conditions cold — they are the difference between a defended decision and a preference.
Each fork points to the deep-dive chapter that carries the full argument. In the interview you need the matrix and the sentence; the chapter is for the follow-up where they want you to go three levels down.
Batch vs streaming features
The most common fork, and the one where candidates over-reach. The default is batch; streaming earns its place when a specific feature's value decays in minutes. Deep dive: 06 · Streaming Features.
| Dimension | Batch (hourly/daily materialisation) | Streaming (event-time aggregation) |
|---|---|---|
| Freshness | Minutes to a day | Sub-second to seconds |
| Correctness model | Reproducible from tables; point-in-time joins are easy | Watermarks, late events, exactly-once are yours to get right |
| Training/serving parity | Same SQL offline and online (materialised) | Needs a stream-batch unified definition or you will skew |
| Cost profile | Cheap compute, storage-heavy | Always-on cluster, ops-heavy |
| Failure mode | Stale features | Wrong features (silently) |
| Typical use | Aggregates over days/weeks, profile features, embeddings | Velocity counts ("txns in last 5 min"), session state, real-time context |
"I'd keep 90% of features batch because they aggregate over days and batch gives me reproducibility for free; I'd stream only the handful whose value decays in minutes — the 5-minute velocity counts for fraud — and I'd flip more features to streaming only when a measured lift from freshness justifies the operational cost."
Warehouse vs lakehouse for ML
A warehouse is a superb place to compute tabular features; it is a poor place to store images, tensors, or ten thousand versioned training sets. The fork is really "where does the ML data live", not "do we keep Snowflake". Deep dive: 05 · Storage, Formats & Versioning and 13 · Reference Architectures.
| Dimension | Warehouse-centric (Snowflake / BigQuery) | Lakehouse-centric (Iceberg / Delta on object storage) |
|---|---|---|
| Tabular features | Excellent — SQL, governance, semantic layer | Good — Spark/Trino/DuckDB; more assembly |
| Unstructured data | Awkward; external stages at best | Native — files, Lance/Parquet, tensors alongside tables |
| Training-data access | Export to files; egress and copies | Direct reads from training jobs; no export step |
| Versioning / time travel | Time travel with retention limits; cloning | Snapshots as first-class; branch/tag semantics |
| Cost at scale | Compute credits climb with feature backfills | Object storage cheap; compute you run yourself |
| Team fit | Analytics-heavy org, small ML team | ML-heavy org, platform team exists |
"Compute tabular features where the governed data already is — the warehouse — and land training sets and unstructured data in the lakehouse where training jobs read them directly. I'd flip to lakehouse-only when backfill credits or export volume start to dominate the bill, and to warehouse-only if the ML footprint stays small and tabular."
Build vs buy a feature store
The honest starting point: most teams need the discipline of a feature store (registry, point-in-time correctness, one definition offline and online) before they need the product. Deep dive: 04 · The Feature Store.
| Dimension | Build (tables + conventions + a thin online cache) | Open source (Feast, Hopsworks) | Managed (Tecton, Databricks, SageMaker, Vertex) |
|---|---|---|---|
| Time to first model | Fast for one team | Medium — you operate it | Fast — but onboarding and lock-in |
| Point-in-time joins | Hand-written; easy to get wrong | Built in | Built in, with materialisation managed |
| Streaming features | Separate build | Partial; you wire the stream job | First-class in some (Tecton) |
| Online serving | You own the cache and its SLOs | Pluggable store; you run it | Included, with SLOs |
| Cost | Engineer time | Engineer time + infra | Licence + infra; predictable |
| Right when | < ~5 models, one team, batch-heavy | Platform team exists, wants control, avoids lock-in | Many teams, real-time needs, small platform team |
"Below five models I'd build: versioned feature tables, a registry in code, and a Redis cache — and be strict about point-in-time joins. I'd buy the moment a second team needs the same features online, or a real-time feature's freshness SLO becomes a contract, because that's when the operational surface stops being a side project."
Online store choices
The online store serves a feature vector for one entity in single-digit milliseconds. The choice is dominated by access pattern (point reads by key), write pattern (batch materialisation vs streaming updates), and how much operational burden you can carry. Deep dive: 08 · Serving Architectures.
| Dimension | Redis / Valkey | DynamoDB | Cassandra / ScyllaDB | Bigtable |
|---|---|---|---|---|
| p99 read latency | < 1 ms (in-memory) | ~5–10 ms | ~2–10 ms | ~5–10 ms |
| Dataset size fit | Memory-bound — tens of GB per node | Unbounded, pay per request | Multi-TB, disk-backed | Multi-TB to PB |
| Bulk load from batch | Fast; pipeline it | Throttled by write capacity; costly at scale | Good with SSTable loading | Excellent with Dataflow/bulk import |
| Streaming updates | Trivial | Good | Good | Good |
| TTL / expiry | Native | Native | Native | Per column family (GC rules) |
| Ops burden | Low managed / medium self-run; memory planning | None (AWS) | High self-run; medium managed | Low (GCP), Google-only |
| Cost driver | Memory | Request units, write-heavy backfills | Nodes | Nodes + storage |
"Redis for a feature set that fits in memory with a sub-millisecond budget; DynamoDB or Bigtable when the entity space is hundreds of millions and I want zero ops; Cassandra only if I already run it. The flip is size × write pattern — the day a nightly backfill of 200M rows makes DynamoDB's write bill the biggest line, I move to a bulk-load-friendly store."
Dataset versioning approaches
"Which data trained model v14?" must be answerable in one query, for reproducibility, audit and debugging. The mechanisms differ in granularity and in what they version — table snapshots, files, or a whole repository of data. Deep dive: 09 · Lineage & Reproducibility.
| Dimension | Iceberg snapshots / tags | Delta time travel | LakeFS (git for object storage) | DVC (git-linked file versioning) |
|---|---|---|---|---|
| Unit versioned | A table | A table | A whole bucket/repo of tables and files | Files/directories tracked from a git repo |
| Branching | Tags; branches (newer specs) | No branches; clones | Full branch/merge semantics | Via git branches |
| Cross-table consistency | Per table; coordinate manually | Per table | Atomic commit across many tables | Per repo snapshot |
| Retention | Snapshot expiry you control | Retention window (default days) | Until you delete the branch | Until you prune |
| Unstructured data | Not the point | Not the point | Yes — files are first-class | Yes — its original purpose |
| Best fit | Tabular training sets in a lakehouse | Databricks shops | Multi-table datasets, images + tables, experiments on branches | Small teams, research, files alongside code |
"For tabular training data I'd tag the Iceberg snapshot and record the tag in the model registry — zero extra infrastructure. I'd add LakeFS when a training set spans several tables and files that must be consistent as a unit, or when teams need branches to experiment without copying data."
Vector store choices
Size the problem before naming a product: vectors × dimensions × bytes. Under a few million vectors an extension in the database you already run is usually right. Deep dive: 07 · Embeddings & Vector Data.
| Dimension | pgvector (Postgres) | Dedicated engine (Qdrant, Weaviate, Milvus) | Managed service (Pinecone, cloud-native) | Lakehouse-native (Lance / LanceDB) |
|---|---|---|---|---|
| Scale sweet spot | ≤ ~5M vectors | 10M–1B | Any, priced per unit | Batch/offline first; growing online story |
| Filtered search | SQL WHERE + index; can degrade | Filter during traversal; strong | Metadata filters; strong | Scalar indexes + prefilter |
| Ops | None new — it's Postgres | A new cluster to run | None; lock-in and egress | Files on object storage; minimal |
| Multi-tenancy | Row-level; partition tables | Collections/partitions; good | Namespaces; good | Per-dataset |
| Cost | Existing DB capacity | Nodes (memory for HNSW) | Highest per vector at scale | Storage-priced |
| Best fit | Small corpora, transactional joins matter | Large corpora, high QPS, filters | Small team, fast start, no ops | Training/eval data, offline retrieval, ML-native workflows |
"Six million vectors with tenant filtering at 200 QPS — pgvector would strain on filtered recall, so I'd run a dedicated engine with filtered HNSW; below a couple of million I'd have stayed in Postgres. I'd move to managed if the team can't carry another cluster, and I'd keep the chunk table in the lakehouse regardless so the index is always rebuildable."
The decision sentence
Every matrix above collapses to one sentence in the room. Practise it until it's reflex:
"Given [the requirement that dominates], I'd choose [option] over [alternative] because [how it meets the requirement]. The cost is [honest downside], acceptable because [context] — and I'd revisit if [flip condition]."
The flip condition is the senior tell. It shows you know the boundary of the decision, not just the decision — and it hands the interviewer a natural follow-up you're already prepared for.
Now put the matrices under pressure: 09 · Practice Questions, Graded.
✓ Check yourself
Answer from memory before opening each one.
-
A product manager wants a 28-day spend feature "real-time". How do you decide, and what does the answer say about batch versus streaming failure modes?
Show answer
Default to batch. A 28-day aggregate barely moves in minutes, so streaming costs an always-on cluster for no lift, and you stream only when measured lift from freshness pays for the operations. Batch fails by going stale, which is visible. Streaming fails by being quietly wrong, because watermarks, late events and exactly-once are yours to get right. See batch versus streaming.
-
Warehouse backfill credits dominate the bill, training jobs need exports, and the corpus now includes 30 TB of images. Where does each kind of data live?
Show answer
Compute tabular features where the governed data already is, and land training sets and unstructured data in the lakehouse where training jobs read directly. If backfill credits or export volume keep dominating, move to lakehouse-only. See warehouse versus lakehouse.
-
Three models, one team, nightly batch. A vendor offers a managed feature store. What do you do, and what event changes your answer?
Show answer
Build the discipline, not the product: versioned feature tables, a registry in code, a Redis cache if anything is online, and strict point-in-time joins. Buy when a second team needs the same features online, or when a real-time feature's freshness SLO becomes a contract. See build versus buy.
-
The entity space is 300M, a nightly backfill rewrites 200M rows, and p99 reads must be under 10 ms. What does each online store cost you, and what decides it?
Show answer
Redis is memory-bound at tens of GB per node, so a hot set that large gets expensive. DynamoDB handles the size but its write bill for a 200M-row backfill can become the largest line. Bigtable or Cassandra with bulk loading suit that write pattern. The decision is size times write pattern. See online store choices.
-
A training set spans three tables and a folder of images and must be reproducible as one unit. Why is an Iceberg snapshot tag not enough?
Show answer
Iceberg versions one table at a time, with no atomic commit across several tables and loose files. LakeFS gives an atomic commit and branches across a whole repository, so the tables and images are consistent as a unit. See dataset versioning approaches.
-
You have 2M vectors, already run Postgres, need joins to orders, and expect about 20 QPS. Which store, and what would make you move?
Show answer
pgvector, because at that size an extension in the database you already run is usually right and joins are easy. Move to a dedicated engine if vectors grow past a few million or filtered search at higher QPS starts to hurt recall. Size the problem before naming a product. See vector store choices.
Takeaway
- Default features to batch. Stream only those whose value decays in minutes, and only when measured lift justifies the operating cost.
- Batch fails stale and streaming fails silently wrong, and that difference sets how you monitor each.
- Compute tabular features where governed data already lives, and put training sets and unstructured data in the lakehouse.
- Below about five models, build the feature-store discipline. Buy when a second team needs the features online or freshness becomes a contract.
- An online store is chosen by entity count times write pattern: Redis in memory, DynamoDB or Bigtable at very large size, and bulk-load cost flips the choice.
- Tag an Iceberg snapshot for single tables, and add LakeFS for multi-table or file-backed datasets or for branches. Size vectors before you name a vector store.
- Every fork is a decision sentence with a cost and a flip condition.
Next, put those matrices under pressure with questions graded from L1 to L4. → Practice Questions, Graded
Further reading
- Feast documentation — Feast. The open-source feature store whose registry and stores anchor the build-versus-buy row.
- Tecton documentation — Tecton. The managed side of the same fork, with streaming features and online serving included.
- Branching and Tagging — Apache Iceberg documentation. Tags and branches on table snapshots for dataset versioning.
- Table batch reads and writes — Delta Lake documentation. Includes time travel and its retention behaviour.
- lakeFS documentation — lakeFS. Git-style branches and atomic commits across object-storage data.
- DVC documentation — Data Version Control. Versioning files and directories alongside code in git.
- pgvector — pgvector project README. Vector similarity search inside Postgres, for the small-corpus end of the vector fork.