The Bucket Full of Resumes

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
L7

Problem

Thousands of resumes arrive each month as PDFs and scanned images, landing as files in an S3 bucket. What end-to-end pipeline extracts each candidate's name, skills, work history and education from every document and makes them queryable?

A thousand resumes. Structured data inside each one.

Business requirements

Worked solution and explanation

What this really is

This is a reprocessing and data-boundary problem dressed up as resume search. Anyone can draw OCR feeding a search index. The real question is what happens on the second extraction run: the model improves, every resume is re-extracted, and a pipeline that writes over its last output silently erases every field HR fixed by hand. The one-store reflex also breaks the other asks. Recruiters cannot filter on 'five plus years' over raw text, and analysts reading the same store get names along with skills.

Walk the requirements

Step 1: Archive originals before extracting anything

Uploads land in original_archive and are never mutated. extraction_pipeline reads from the archive, so a better model means a rerun, not a re-upload. Running it on a schedule under an hour keeps recruiters within hours of upload.

Step 2: Emit text and typed fields from one pass

Each resume yields full text for search_index and typed rows for structured_warehouse: skills as a list, experience as a number, degree as an enum. Without typed fields, 'five years of experience' turns into a regex.

Step 3: Overlay corrections, never overwrite

HR edits flow from hr_correction_console into corrections_store, keyed on resume and field. The served value is the correction when one exists, otherwise the extracted value. A rerun replaces extraction output and never touches corrections.

Step 4: Remove identifiers on the analytics path

Recruiters read through access_policy, which grants PII per user at the platform level. Analytics does not go through that gate. skills_analytics reads only from pii_removal, a transform that strips names and contact details and passes only skills, experience and degree. Restricting who may read is not the same as removing what they could read.

The reference design

Immutable originals feed a rerunnable extraction; corrections overlay the warehouse; recruiters pass `access_policy`, and `skills_analytics` reads only the skills, experience and degree that `pii_removal` lets through.
  • resume_uploads (source, S3)
  • original_archive (storage, object storage)
  • extraction_pipeline (transform, Spark)
  • search_index (storage, PostgreSQL); freshness: < 1h
  • structured_warehouse (storage, warehouse); freshness: < 1h
  • hr_correction_console (consumer, API)
  • corrections_store (storage, PostgreSQL)
  • access_policy (quality gate, custom)
  • recruiter_ui (consumer, API)
  • pii_removal (transform, SQL)
  • skills_analytics (consumer, dashboard)
  • resume_uploads -> original_archive
  • original_archive -> extraction_pipeline
  • extraction_pipeline -> search_index
  • extraction_pipeline -> structured_warehouse
  • hr_correction_console -> corrections_store
  • corrections_store -> structured_warehouse
  • structured_warehouse -> access_policy
  • search_index -> access_policy
  • access_policy -> recruiter_ui
  • structured_warehouse -> pii_removal
  • pii_removal -> skills_analytics
  • Upload + archive: resume_uploads, original_archive
  • Extract + corrections: extraction_pipeline, search_index, structured_warehouse, corrections_store, hr_correction_console
  • Access + consumers: access_policy, recruiter_ui, pii_removal, skills_analytics
Overwrite on rerun
Extraction writes straight into the served table. HR fixes a skill, the model reruns, and the fix is gone. Nobody notices until a recruiter does.
Overlay corrections
Extraction and corrections live apart. The served value is COALESCE(correction, extracted). Reruns can replace extraction output freely because they never touch corrections_store.

Common follow-up questions