Credit for Every Touch

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

Ad spend from two ad platforms, CRM opportunities and web sessions land separately for each of about 40 clients, the largest sending roughly 50 million sessions a month. Design the pipeline that stitches them into attribution credits for campaign reporting.

They saw the ad, clicked the email, then bought. Who gets credit?

Business requirements

Worked solution and explanation

What this really is

This is a replay-safety problem dressed up as marketing attribution. The credit math is the easy part. With no updated_at, there is no watermark to load only what changed, and the ad platforms keep revising the last week of numbers. So the only honest incremental load is to re-pull a whole date range and replace what you had. The trap is an append-style incremental load. It works on day one, then every re-pull stacks a second copy of the same clicks, and every touchpoint in the revised window gets credited twice.

Walk the requirements

Step 1: Let the orchestrator own the daily run

orchestrator (Airflow) runs once a day, after the ad platforms finish exporting and before the earliest client reads. It holds dependency edges to both date_range_loader and attribution_transform, so credit never computes over a half-loaded day. Its error path reaches on_call_alerts when a run fails or slips.

Step 2: Re-pull the window, overwrite the partition

date_range_loader (Spark) pulls each source for the trailing seven days, the window the ad platforms can still revise, and lands it in raw_landing under partition_overwrite. A rerun of any date replaces that date, so nothing accumulates.

Step 3: Validate before anything is credited

load_validation (Great Expectations) checks the landed partitions for missing IDs and malformed fields before attribution_transform reads them. About one load in twenty fails here, and it fails the run instead of quietly feeding attribution.

Step 4: Credit every touchpoint, then overwrite

attribution_transform (dbt) assembles ad, CRM and web touchpoints into each customer journey and spreads credit across every touch before the conversion. It writes to attribution_warehouse, which also uses partition_overwrite, so recomputing a range restates credit instead of doubling it.

The reference design

A daily orchestrated run re-pulls each source's date range into `raw_landing`, validates it, credits every touchpoint, and overwrites partitions in `attribution_warehouse`.
  • ad_platforms (source, API)
  • crm_opportunities (source, API)
  • web_sessions (source, API)
  • orchestrator (transform, Airflow)
  • date_range_loader (transform, Spark)
  • raw_landing (storage, object storage); backfill: partition_overwrite
  • load_validation (quality gate, Great Expectations)
  • attribution_transform (transform, dbt)
  • attribution_warehouse (storage, warehouse); freshness: < 24h; backfill: partition_overwrite
  • campaign_dashboard (consumer, dashboard)
  • on_call_alerts (consumer, alerting)
  • ad_platforms -> date_range_loader
  • crm_opportunities -> date_range_loader
  • web_sessions -> date_range_loader
  • orchestrator -> date_range_loader
  • orchestrator -> attribution_transform
  • orchestrator -> on_call_alerts
  • date_range_loader -> raw_landing
  • raw_landing -> load_validation
  • load_validation -> attribution_transform
  • attribution_transform -> attribution_warehouse
  • attribution_warehouse -> campaign_dashboard
Append-only incremental load
Each run inserts whatever the source returned for the window. A source with no updated_at returns rows you already have, so revised clicks land twice and credit inflates every day the windows overlap.
Range re-pull with overwrite
Each run replaces the date partitions it pulled, in raw_landing and in attribution_warehouse. Running the same day twice, or ten times, leaves exactly one copy.

Common follow-up questions