A newly refreshed developer sandbox contains your metadata and almost no records. You cannot test a flow that fires on Opportunity stage changes without opportunities, and you cannot demo a screen with three seeded accounts named Test Account 1.
The obvious fix — a full copy sandbox — solves the data problem and creates a much worse one: every customer name, email address, and phone number now exists in an org where developers and contractors have broad access, and it is captured in every subsequent refresh.
The three problems seeding has to solve
1. Relationship order
Records do not load in isolation. Contacts need Accounts. Opportunities need Accounts and maybe Contacts via OpportunityContactRole. Cases need both. Load them in the wrong order and every insert fails on an invalid cross-reference.
A seeding tool has to build the dependency graph from the object relationships and load in topological order — parents first, children after, and lookups that point back into the same object handled as a second pass.
2. ID remapping
Salesforce IDs are org-specific. An Account inserted into the sandbox gets a new ID, and every child record that referenced the old production ID has to be rewritten to point at the new one before it loads. Any tool that does not maintain this old-to-new map produces a sandbox full of orphaned records.
3. PII
The part most home-grown scripts skip. Masking has to happen before the data leaves the source org, not as a cleanup pass afterwards — otherwise the real values existed in the target, however briefly, and in its backups.
What masking actually looks like
Useful masking is field-level and strategy-based, not a blanket scramble. In orgadmin.ai’s data seeding each field in the plan gets one of eleven strategies:
- Pass through — the value is not sensitive and copies as-is. Stage, Amount, CloseDate.
- Null and redact — clear it, or replace with a fixed placeholder.
- Static — every row gets the same supplied value. Useful for pinning every email domain to something you control.
- Fake name, first name, last name, email, phone, company — plausible substitutes, so the sandbox still looks like real data to whoever is testing.
- Hash — deterministic. The same input produces the same output, so records that matched on a value still match after masking, without the value being recoverable.
That last one matters more than it sounds. Deterministic hashing is what lets you keep referential meaning — deduplication logic, matching rules, external ID joins — while destroying the underlying PII.
Sensitive fields are also detected up front rather than left entirely to you: a scan flags likely PII by field type and name so the plan starts from a safe default instead of an empty checklist.
Two things to know
Seeding refuses production targets outright, and the target’s sandbox status is re-checked live when the run starts, not when the plan was written — so a plan cannot be pointed at production later by editing a saved config.
There is no date-shifting or randomise-in-range strategy. If your tests depend on relative dates, plan for that — the eleven strategies above are the whole set.
Practical advice
- Seed narrow, not deep. Fifty accounts with full related-record trees beats fifty thousand flat accounts for almost every testing purpose.
- Dry run first. A dry run shows what would be inserted, in what order, with masking applied, before any record is created.
- Save the plan. The value compounds after a sandbox refresh, when you need the same shape of data again with no manual work.
- Pin email domains. Static-mask every email field to a domain you own. It is the cheapest possible insurance against a sandbox workflow emailing real customers.