Development has twelve users, three orders, and one product. Every query is instant, every page loads immediately, and nobody has ever seen the N+1 query that will make the orders page take eight seconds in production.
Seed data shapes what developers can see. Data that is too clean and too small makes an entire category of problem invisible until users find it.
What changed in 2026
- Anonymisation tooling matured. Purpose-built tools for producing masked subsets became widely available.
- Compliance pressure increased. Copying production data to development environments came under more scrutiny.
- Referential-integrity-aware subsetting improved. Tools that follow foreign keys when extracting a subset became standard.
- Synthetic generation got better. Generating realistic-looking data with correct distributions became practical.
The three approaches
|
Handwritten seeds |
Production copy |
Anonymised subset |
| Realism |
Low |
Perfect |
Good |
| Compliance risk |
None |
High |
Low |
| Size |
Tiny |
Full |
Controllable |
| Edge cases |
Only what you thought of |
All of them |
Preserved if sampled well |
| Maintenance |
Ongoing |
None |
Moderate tooling |
| Setup speed |
Instant |
Slow |
Moderate |
Handwritten seeds are the default and the weakest. They contain what the author imagined, which excludes the unusual data that causes real problems: names with apostrophes, addresses in unexpected formats, records with unusual relationship counts, and the customer with fourteen thousand orders.
Production copies are perfectly realistic and put personal data on laptops, in test databases with weaker access controls, and in backups nobody tracks. That is a reportable incident waiting to happen, and it is why this practice has become much less acceptable.
Anonymised subsets are the workable middle: real structure and distributions, no real people.
Subsetting without breaking references
The hard part of building a subset is referential integrity. Take ten percent of orders and you have orders referencing customers you did not take.
Proper subsetting follows foreign keys: select a set of root records, then include everything they reference and, where needed, everything referencing them. That produces a smaller database that is internally consistent.
Getting this wrong produces a dataset that fails on load or, worse, loads with dangling references that cause confusing failures later.
The other thing to preserve deliberately is distribution shape. Real data is skewed — most customers have few orders, a handful have thousands. A uniform sample flattens that and hides exactly the cases that cause performance problems. Sampling should deliberately include the outliers.
Anonymisation that survives contact
Naive anonymisation breaks things. Replacing every email with the same value violates unique constraints. Randomising a postcode breaks anything validating format. Replacing names with random strings makes the data unusable for anyone looking at a screen.
Better anonymisation is consistent and format-preserving: the same input maps to the same fake output every time, so joins and references still work, and the fake value has the same shape as the real one so validation still passes.
Consistency across tables matters particularly — a customer's name must be replaced identically everywhere it appears, or the data becomes internally contradictory.
Some columns need no anonymisation and some need removal entirely rather than replacement. Classifying columns once, and encoding that classification in the tooling, is what makes this repeatable rather than a manual exercise each time.
Common mistakes
- Seed data too small to reveal anything. Hides N+1 and missing indexes.
- Production copies in development. Compliance exposure.
- Subsetting without following foreign keys. Broken references.
- Uniform sampling. Flattens the distribution that causes problems.
- Anonymisation breaking uniqueness or format. Data that will not load or validate.
- Inconsistent replacement across tables. Contradictory data.
- Seeding the full production volume locally. Slow for little benefit; shape matters more than size.
FAQ
How much data do I need locally?
Enough that performance problems are visible — thousands rather than tens, with realistic skew. Full production volume is rarely necessary and makes environments slow to set up.
Should tests use the same seed data?
Usually not. Tests want small, deterministic, purpose-built fixtures; development wants realistic volume and variety. Different requirements — see fixture management.
Can I generate purely synthetic data instead?
Yes, and it takes effort to make realistic. Synthetic generation avoids compliance concerns entirely and needs care to reproduce real distributions and edge cases.
How often should I refresh?
Frequently enough that the shape reflects current production. Monthly is a reasonable default; more often if the schema changes rapidly.
Where to go next
For loading seed data efficiently, read COPY bulk loading. For test-specific data, fixture management, and for keeping environment schemas aligned, schema drift detection.