Build a data platform for a global SaaS product

Six sources, six consumers, and a starting point of one nightly CSV export that two people rely on. Eleven requirements arrive in order, then ten things go wrong. The exercise is not to draw the finished architecture — it is to arrive at it one forced step at a time and keep it trustworthy.

Scale

Millions of users across several regions, growing.

Where you start

PostgreSQL → a nightly CSV export → one dashboard. It works. Two people rely on it.

The rule

Nothing is added because it is standard. Every component must be forced by a requirement that has actually arrived, and every one you add is something you now operate.

The sources

Each one is awkward in its own specific way, and the awkwardness is the design constraint — not the volume.

Application PostgreSQL

Transactional tables — users, accounts, subscriptions, orders. Current state only.

What makes it hard

It holds the truth and forgets history. Hard deletes leave no trace, and the schema changes whenever a feature ships.

Application events

High-volume clickstream and product usage, emitted by web and mobile clients.

What makes it hard

Clients are offline sometimes, so events arrive late by minutes or by days. Duplicates are normal. The client version decides the payload shape.

Payment provider

Charges, refunds, disputes and payouts, available by API and by webhook.

What makes it hard

You do not control it. It is the authoritative source for money, its webhooks are at-least-once, and its idea of a "transaction" does not match yours.

Support system

Tickets, conversations and satisfaction scores in a SaaS product.

What makes it hard

API-only, rate limited, paginated, and it contains free text written by customers — which means it contains everything.

Object storage drops

Partner files arriving on a schedule that is loosely honoured.

What makes it hard

CSV with no schema, occasional encoding changes, and a partner who reissues a corrected file for last Tuesday without telling anyone.

Agent traces

Prompts, tool calls, retrieved documents, outcomes and user feedback from an in-product assistant.

What makes it hard

The most useful field for debugging is the one containing the customer's own words, and the volume grows with usage rather than with headcount.

The consumers

Design from this column, not from the one above it. What each of these needs — and what each of them does when the data is wrong — is what decides every choice you are about to make.

Executive dashboard

A small number of headline metrics, defined once, that do not change after being reported.

Freshness

Daily is fine. Correct is not optional.

When the data is wrong

Trust collapses across every dataset at once, including the ones that were right.

Finance

Revenue reconciled to the payment provider, reproducible months later, with every adjustment explainable.

Freshness

Monthly close. Reproducibility matters far more than latency.

When the data is wrong

A restatement, and a conversation about controls rather than about pipelines.

Product analytics

Event-level behaviour, cohorts, funnels, experiment results.

Freshness

Hours. Tolerant of small gaps, intolerant of silent definition changes.

When the data is wrong

A shipped feature is judged on a broken metric, and the decision is not revisited.

Machine learning

Features computed identically for training and for serving, with the values that were true at prediction time.

Freshness

Training is batch; serving is immediate. The two must agree.

When the data is wrong

A model trained on features it will never see again, degrading quietly in production.

Agent evaluation

A sampled, privacy-filtered set of production traces with outcomes attached.

Freshness

Weekly refresh, versioned so a score is comparable across runs.

When the data is wrong

Evaluation scores move because the dataset moved, and nobody can tell that from the model improving.

Search and retrieval

An index of documents with an embedding version, a chunk strategy and a source version recorded.

Freshness

Minutes for new documents; a full re-index is a migration.

When the data is wrong

Retrieval silently returns worse results, which no monitor detects and every user feels.

Ask these first, in this order

Before any component is chosen. The order is the argument: you cannot decide batch or streaming until you know the grain, and you cannot decide the grain until you know who is asking.

  1. 1
    What is the source of truth for each business concept?

    Three systems hold a customer's country and they disagree. Until one is authoritative, every reconciliation is an argument rather than a check.

  2. 2
    What is the grain of each dataset?

    If you cannot say what one row represents, you cannot write a correct aggregate against it — and the wrong one will not raise an error.

  3. 3
    Batch or streaming, per source?

    It is a per-source decision driven by what a consumer needs, not a platform-wide identity. Most sources here should be batch.

  4. 4
    How do we replay?

    Every fix you will ever make depends on being able to reprocess. Decide the mechanism and its window before you need it.

  5. 5
    How do we deduplicate, per source?

    At-least-once is the normal case across every source here. The business key differs per source and an id-based dedup will miss producer retries.

  6. 6
    How is schema evolution handled at each boundary?

    Each hop chooses to absorb, reject or pass on a change. Deciding that per-hop in advance is what separates a platform that survives upstream changes from one that breaks weekly.

  7. 7
    How is data partitioned?

    It is the highest-leverage and least-visible decision in the platform, and it is expensive to change once a hundred queries depend on it.

  8. 8
    How do we model facts and dimensions, and where does history need preserving?

    Finance needs the customer's country at order time. That is a Type 2 dimension, and if it is not built before the change happens the information is gone.

  9. 9
    How is data quality measured, and by whom?

    Every check has a blind spot, so quality is a portfolio. Ownership decides whether it is maintained.

  10. 10
    How is freshness monitored, per dataset, against a stated SLO?

    A platform average hides the one table that stopped on Friday. The word "complete" in "latest complete data" does all the work.

  11. 11
    How do we backfill without corrupting what is already correct?

    A backfill is a write to production data. Bounded range, isolated compute, validation against the source, and an atomic publish.

  12. 12
    How do we trace lineage, in both directions?

    Upstream during an incident, downstream before a change. Without the second one, blast radius is discovered rather than known.

  13. 13
    How is sensitive data classified, minimised, masked and deleted?

    The copy inherited the obligation and none of the mechanism. Designing this on day one is far cheaper than discovering it during an audit.

  14. 14
    How do we control and attribute cost?

    A shared platform makes every cost everyone's and therefore nobody's. Attribution to datasets is what makes a conversation about it possible.

The requirements, in the order they arrive

Each one forces exactly one change. Read the trigger, decide what you would build, then open it and compare against the question that should have been answered first.

Then things go wrong

Ten injected failures. Each carries the plausible wrong fix, because the wrong fix is where the learning is — most of these have a response that makes the symptom disappear while making the platform worse.

Where to go next

Each of the injected failures has a lab where it is the whole subject.