← Our FailuresApps Ship Empty on the User's Own Data: No Seeded Records, No Invented Numbers
intermediate6 min read · updated 2026-06-20
Market & numbers — every figure sourced
orgs_reporting_ai_financial_loss_pct99 percentEY survey: companies advancing responsible AI governance, 2025 (975 C-suite leaders, orgs >US$1B revenue) — https://www.ey.com/en_gl/newsroom/2025/10/ey-survey-companies-advancing-responsible-ai-governance-linked-to-better-business-outcomes
avg_ai_risk_financial_loss$4.4MEY survey 2025, conservative average loss per affected company — https://www.ey.com/en_gl/newsroom/2025/10/ey-survey-companies-advancing-responsible-ai-governance-linked-to-better-business-outcomes
fabricated_values_shipped_in_our_apps0 countest: Design constraint, not a measurement: the build rule mandates zero seeded/mock/fabricated values in the shipped bundle; verified by the no-data ship audit (unverifiable fields render empty, never invented).
Apps Ship Empty on the User's Own Data: No Seeded Records, No Invented Numbers
What we tried
We were building a suite of vertical apps (leads, real estate, trading, marketing, a personal assistant) and wanted every one of them to feel alive the first time someone opened it. The instinct was the obvious one: pre-populate. Seed the leads list with sample contacts. Show a dashboard with a plausible-looking pipeline number. Render the trading screen with bars and a P&L curve so the charts weren't blank. Put a "$791K" style headline metric on the marketing page so the product looked like it already worked. These were fabricated demonstration figures—not live results, not a backtest, and not a live track record.
Three different shortcuts, all the same shape:
- Seeded records — shipping the bundle with leads, probate rows, price bars, and contacts baked in so screens weren't empty.
- Mock metrics — hardcoding a number into a tile because the real value wasn't computed yet, or because empty looked unfinished.
- Optimistic fallbacks — when a value couldn't be verified (a heart rate, a trading edge, a deliverability rate), guessing a "reasonable" one instead of showing nothing.
What broke
Every one of those shortcuts is the same failure: the product asserts something it cannot back up. And the cost is not cosmetic.
- The numbers were wrong the moment they shipped. A local deploy of our storefront still carried a fabricated trading P&L long after the live site had been corrected. Anyone who saw it was being shown a track record that did not exist. Shipping that dir would have regressed truth that was already fixed.
- Seeded data leaked the wrong person's information. Dev-seeded local stores pulled from a real database. The instant any of that reaches an end user's bundle, you've shipped someone else's leads to a stranger. The end user is supposed to start on their own data, not ours.
- One fabricated value poisons the whole app. This is the part teams underestimate. When a user catches a single invented chart or phantom record, they stop trusting every output the tool produces — including the correct ones. In the broader market this is now a measured, expensive failure mode: in EY's 2025 survey, 99 percent of surveyed organizations reported financial losses from AI-related risks, at a conservative average of 4400000 USD per affected company. Confidently-stated-but-unsupported output is the exact category that drives those losses.
- "Empty looks broken" was a real concern — handled the wrong way. The fear that drove the seeding was legitimate. Nielsen Norman Group's research is blunt about it: a blank container is not neutral, and a misleading "no records" message that later fills with content teaches users "severe distrust of and distaste for the application." But the fix for an ugly blank screen is a designed empty state, not fake content.
The fix
The binding rule we now ship under: the app ships with no data and invents no numbers. It starts empty on the end user's own data.
Concretely:
- The bundle contains zero records. No leads, no probate rows, no price bars, no memory, no contacts, no tokens, no personal info, no mock/sample data. The shipped artifact is inert until the user connects their own source. We back this with a no-data ship audit; the count of fabricated values in the bundle is held at 0.
- Unverifiable values render an empty state, never a guess. If a field can't be computed from real, verified input, it shows an honest "warming up / no data yet" state. The trading store shows "warming" honestly rather than a fake P&L. Heart rate is "accurate or nothing" — we deleted the marginal-quality fallback rather than display a made-up bpm.
- Empty states are designed, following NN/G's three guidelines — communicate real system status (loading vs. genuinely empty vs. error), teach the feature, and give a direct path to populate it. This removes the "looks broken" pressure that caused the seeding in the first place.
- Dev seeding is env-gated and never reachable in production. Preview-seed hooks exist for local development only, behind an environment flag. The packaging step strips them; nothing from our reference data is in the customer artifact.
- Verify before you assert a blocker, too. The same honesty rule applies to status claims, not just UI tiles: don't surface "$X revenue" or "this is blocked" unless a re-runnable check confirms it. An unverified claim is a fabrication whether it's a dashboard number or a status note.
Apply it
- Audit your shipped bundle for data, not just for the empty-state UI. Grep the build artifact for sample names, seed JSON, fixture files, and hardcoded metric strings. If a customer could see it, it's a leak or a lie.
- Make "empty" a first-class screen. Budget design time for it. A good empty state with a clear next action kills the urge to fake content far better than any seeded row.
- Adopt an "accurate or nothing" rule per field. For every value the UI shows, write down where the real number comes from. If there's no source, the field must render empty — not a placeholder that looks like a measurement.
- Separate reference data from product data architecturally. Internal datasets you develop against must live behind an env flag that cannot be flipped on in a customer build. Treat any path from your data into a customer artifact as a shippable incident.
- Treat a single fabricated value as a product-wide trust failure, because that's how users treat it. The downside isn't one wrong tile; it's losing credibility for everything the app correctly does.