Build the wipe-the-demo-data button before your first prospect logs in
The worst thing a prospect can see in your demo is your own junk data, and the page you build to wipe it is itself the next security hole.
When one system runs both real operations and live sales demos, the most dangerous thing in the room isn’t a bug. It’s your own junk: a half-broken order from last week’s testing, an invoice for “Test Customer 4,” a delivery stuck in a state no real customer ever reaches. The prospect doesn’t see a feature. They see a mess, and they quietly decide your software is a mess too.
So before the first prospect ever logs in, you build the unglamorous thing nobody puts on a roadmap: a button that wipes the demo data clean. And the moment you build it, you’ve opened a second hole that’s worse than the first.
The reset that keeps the things you can’t recreate
A naive wipe is TRUNCATE everything and a fresh seed. That’s a disaster, because it throws away the parts that took real setup. The reset has to be surgical: it clears the transactional churn while preserving the spine the platform stands on.
- Wipe the transactional layer. Orders, invoices, payments, delivery events, anything that accumulates as people poke at the system. This is the stuff that looks embarrassing in a demo and means nothing the day after.
- Keep users, settings and config. The accounts you’ll log in with, the roles, the branding, the integration keys. Recreating those by hand before every demo is how you end up demoing with a broken login at 9am.
- Reseed a clean, deliberate baseline. Not empty, not random. A small set of plausible orders that show the happy path, so the prospect sees the product working, not an onboarding wizard.
The test for what gets wiped is simple: would I be embarrassed if a stranger saw this row? If yes, it goes. If recreating it would cost me twenty minutes of clicking before a sales call, it stays.
A preflight, because “clean” and “ready” are different words
A wiped database can still demo badly. I learned this the hard way when a reset left me with no default pricing configured, so every order I tried to create in front of someone threw a validation error. The data was clean. The system wasn’t ready.
So the reset got a companion: a preflight that runs before the demo, not during it, and checks the things that make a demo possible at all. Is there a default price list? Does at least one driver exist to assign? Are the integration keys present and non-expired? It returns a plain list of what’s missing, in words a tired person can read at 8:45am.
A wipe makes the system clean. A preflight makes it ready. You need both, because a prospect can’t tell the difference between an empty system and a broken one, and neither can you under pressure.
A secret path is not access control
Here’s the trap, and it’s the whole reason this is a security post and not an ops post. You build this destructive reset, and naturally you tuck it away at a URL nobody would guess, /developer or /admin/reset, and you tell yourself that’s fine because no customer knows it exists.
It is not fine. The instant a button can erase every order, “nobody knows the URL” is the entire defense, and a defense made of obscurity is no defense. A bored user pasting around, a leaked link in a screen recording, a logged-in account from a churned client: any of them can reach a path that isn’t behind a permission check. Security through a hard-to-guess address is a door you forgot to lock because you painted it the same color as the wall.
The fix is boring and non-negotiable. The reset endpoint sits behind the same authorization layer as everything else, gated on an explicit developer-or-owner permission, and it refuses to run at all in any environment marked live. The hidden URL is a convenience, never a credential.
So the order of operations is: build the wipe before your first prospect, build the preflight right after, and the very second you create that secret page, ask the question that actually matters. What happens when a regular user types that URL? Answer it in code, not in hope.