The Patients We Cannot Move

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

Models trained on any single hospital's patients keep failing to generalize, so the clinical research team wants one model and one set of population statistics drawn from 40 hospitals in 8 countries. Design the pipeline that trains the models and computes the statistics across every site.

Patient data stays local. Insights have to be global.

Business requirements

Worked solution and explanation

What this really is

This is a privacy boundary dressed up as an ML pipeline. Everyone says 'ship the compute to the data', and that part is table stakes. The trap is assuming the boundary is safe once raw rows stay home. A count over five patients is a patient. The privacy law that keeps records in place also covers what you send out. Ask about publication and you learn the result must reproduce for fifteen years from data you will never hold. If you check cohort sizes after results leave the site, re-identifiable numbers have already crossed the border. If you archive only the model, the regulator's reproduction request has no answer.

Walk the requirements

Step 1: Push rounds out, pull only results back

The orchestrator sends each training round into every hospital, tracks which sites finished, and decides whether a round waits for a slow hospital or closes without it. Raw records never cross the boundary, because nothing outside the hospital is built to receive them.

Step 2: Harmonize codes inside the hospital

Forty EHRs encode the same diagnosis forty ways. local_normalizer maps local codes to one canonical clinical schema before training, so gradients mean the same thing at every site. Harmonizing centrally would require moving the data first.

Step 3: Gate at the site, before the network

release_gate runs inside each hospital, between local_trainer and the edge out. A cohort under the privacy threshold is withheld or coarsened, and a failed check means the result never leaves. Only gated output reaches the aggregator.

Step 4: Archive the round, not just the model

study_archive stores, per study and per hospital, the snapshot id, code version, and gradient hash for the full retention window. It is fed from the gate's output, so each site's contribution is recorded, along with the combined result from the aggregator.

Normalizing, training and the small-cohort check all run inside each hospital. Only gated gradients and aggregates leave, and `study_archive` keeps what each site contributed to every round.
  • hospital_ehrs (source, PostgreSQL)
  • orchestrator (transform, Airflow); on error: alert; alerting: Hospital missing from round past SLA
  • local_normalizer (transform, Spark); on error: alert
  • local_trainer (transform, Spark); idempotency: staging_table
  • release_gate (quality gate, Great Expectations); on error: alert; alerting: Cohort under privacy threshold blocked
  • aggregator (transform, Spark); idempotency: upsert
  • study_archive (storage, object storage); backfill: incremental
  • model_warehouse (storage, warehouse); freshness: < 24h
  • researcher (consumer, dashboard); freshness: < 24h
  • hospital_ehrs -> local_normalizer
  • orchestrator -> local_normalizer
  • local_normalizer -> local_trainer
  • local_trainer -> release_gate
  • release_gate -> aggregator
  • release_gate -> study_archive
  • aggregator -> study_archive
  • aggregator -> model_warehouse
  • model_warehouse -> researcher
  • In-hospital compute: hospital_ehrs, local_normalizer, local_trainer, release_gate
  • Coordinator: orchestrator, aggregator
  • Archive + serving: study_archive, model_warehouse
  • Researcher: researcher

Common follow-up questions