
Andrew Hanna

Andrew Hanna

Short answer: seeding a Salesforce sandbox well is three jobs, not one. Pull a relationship-aware slice of production rather than a record count, load it parents-first with stable external IDs so the same set can be reloaded, and anonymise deterministically so the data still behaves like real data. Then version the seed definition next to your metadata, because the thing that kills seed sets is not the first load, it is the schema change three sprints later.
Sandbox seeding is copying a subset of production records into a lower environment so the org has realistic data to develop and test against, with sensitive values masked before they land. It is a data problem, not a metadata problem: a sandbox refresh brings the configuration, and in most cases leaves you with an empty or unusable org.
The ones that do not copy data for you. Per Salesforce's sandbox licences and storage limits: Developer (200 MB) and Developer Pro (1 GB) copy metadata only and refresh daily, so those are the ones you seed. Partial Copy (5 GB, refresh every 5 days) brings a template-selected sample and you fill the gaps. Full (production-sized, refresh every 29 days) brings everything, which is exactly why masking matters more there, not less. Scratch orgs always start empty, so seeding is mandatory if you develop on them.
The common failure is selecting by volume: take 500 Accounts, take 500 Contacts, hope they connect. They do not, and testers spend their week building the records they were promised.
Select by graph instead, starting from a small set of root records:
A few hundred well-connected records beat a hundred thousand disconnected ones for every purpose except performance testing.
Strict dependency order, parents before children, and every object keyed on something stable.
upsert on it. Now the load is idempotent: run it twice and you
update rather than duplicate.
Account.ParentId, and pairs where each object points at the other,
cannot be satisfied in one insert. Load the records with the lookup blank, then run
a second pass that only sets the lookup.
Masking that produces noise is as bad as no masking, because nobody trusts a test that failed on garbage. Match the technique to the field:
Salesforce ships Anonymize for Salesforce natively, and Gearset, Odaseva and Flosum all sell seeding tooling with masking built in. Whatever you use, the rule set has to be signed off once, in writing, by whoever owns data protection.
This is the part almost nobody covers, and it is why most seed sets are dead within a quarter. A seed definition is a fixed picture of your schema: field lists, picklist values, required fields, validation rules. Ship a new required field and every load after it fails.
Treat the seed set as source, not as saved config in someone's tool:
In Serpent, a data operation is a first-class pipeline action rather than a manual chore, which is what lets the seed run sit in the same pipeline as the deployment it supports.
Finally, tie the cadence to your releases: each iteration for development sandboxes, each test cycle for UAT, always after a refresh. Because the load is idempotent and version-controlled, that is a pipeline run rather than a project. More Salesforce DevOps guides.
How much data should a seeded sandbox hold?
Enough to cover every record type, path and edge case, usually a few hundred connected records. Volume only matters for load testing.
Can I just use Data Loader?
Yes, for a small set. You will be responsible for load order, ID remapping and masking yourself, and for repeating all three by hand on every refresh.
Does a sandbox refresh keep my seeded data?
No. A refresh replaces the sandbox, so anything you seeded is gone. Plan to re-run the seed as a standard post-refresh step.
Commitment free!