Concept

The demo that kept falling apart

Priya had a week to build a convincing demo dataset. She spent most of it discovering, one broken invariant at a time, that the hard rules in data are never about a single row.

CrossRow team7 min read

Priya needed a demo database by Friday. A prospect wanted to see the product against something that looked like their own business, so she reached for the usual fixture library and generated a few thousand customers, orders, payments, and shipments. It looked great in a spreadsheet. Then she started actually using it, and it came apart one piece at a time.

The pieces it broke on are worth cataloguing, because they are the same ones every naive synthetic dataset breaks on. They share a property: none of them is visible in a single row.

Monday: the orders pointed nowhere

The first join returned almost nothing. Her order rows carried a customer_id drawn at random from a plausible range, and almost none of those IDs matched a customer she had actually created. Referential integrity is the floor, and column-at-a-time generation fails it by default. The harder version bit her the next day: a manager_id that was supposed to point back into the employees table, which cannot be filled from a list that does not exist yet while the employees are still being generated.

Tuesday: the totals lied

Her invoice demo asserted that an order total equalled the sum of its line items. It never did, because the total and the items were generated independently. She could have hand-computed each one, but that is the moment you realize you are doing the generator's job. The same trap was waiting on tax = subtotal × rate and on every date that was meant to be another date plus an offset.

Wednesday: the timeline ran backwards

She sorted a shipment's events by time to demo the tracking view. A package was marked delivered before it was picked up. Because the statuses had been chosen at random, roughly half of every multi-event history was physically impossible. What she actually needed was a state machine: statuses that only move along legal transitions, with believable gaps between them.

shipments.eventsthe fix: a legal state machine
shipment  status       event_ts
SHP-51    picked_up    09:14
SHP-51    in_transit   11:40
SHP-51    out_for_del  Jul 12
SHP-51    delivered    Jul 12
          every entity's timeline now makes sense

Thursday: the money didn't reconcile

The wallet demo had a running balance column. It was full of believable numbers that had no relationship to the amounts beside them. A ledger is a chain: each balance is the previous one plus this row's amount, in time order, landing on a known closing figure. You cannot fake a chain with independent draws.

wallet.ledgerrunning balance, ordered by time
entry  amount      balance
1      open       2,400.00
2      -318.55    2,081.45
3      +540.10    2,621.55
          balance = prior + amount, every row

By Thursday afternoon Priya had a list. Foreign keys that resolve. Totals that are actually derived. Statuses that walk a legal path. Balances that chain and foot. Allocations that sum to a target, shipments that fit in a truck without overflowing, org charts where a manager out-ranks their reports. Every item was a rule that lived between rows, and every one had been silently violated by a generator that only ever looked at one field at a time.

The thing they have in common

The reason these are hard is also the reason they are fixable. They only hold if every row is produced with the others in mind. That is the whole idea behind CrossRow: it generates each row in the context of the rest of the table, infers which of these rules fit your schema, and shows them in the plan for you to review. Then, after generation, it re-verifies every declared rule against the rows it actually produced and reports the share that comply. A rule that was asked for but not satisfied shows up as a failure, never a green checkmark it did not earn.

Priya rebuilt the demo in an afternoon by describing the business instead of hand-filling columns. The prospect never saw a package delivered before it shipped. If you have ever lost a day to fixtures that pass the eye test and fail the real one, you have met this list already, probably in the same order.

Put a cross-row rule on your own schema

Generate a sample and watch the invariant hold, in minutes.

Open CrossRow