Almost every engineering organisation does it at least once. Staging has no realistic data, a bug only reproduces at scale, and someone runs pg_dump against production and restores it into a lower environment. It solves the immediate problem in twenty minutes.
What it also does is take the dataset that receives your entire security budget and copy it somewhere that receives none of it. This piece is about why that trade is worse than it looks — legally and practically — and what the alternative actually costs.
The environment is not a legal boundary
The most common misunderstanding is that data protection obligations attach to production. They do not. Under the GDPR they attach to the processing of personal data, wherever it happens.
Article 4(2) defines processing as essentially anything you can do with data — collection, storage, structuring, adaptation, retrieval, use. Restoring a dump into staging is storage. Running a test suite against it is use. Both are processing, both need a lawful basis, and both fall under Article 32's security obligations.
Three provisions bite hardest:
Purpose limitation, Article 5(1)(b). Personal data must be collected for specified, explicit, legitimate purposes and not further processed in a manner incompatible with them. A customer gave you their address so you could ship an order. Debugging the checkout flow is a different purpose. It is not automatically incompatible — Article 6(4) provides a compatibility test — but it is a question that requires an answer, and "we needed realistic data" is not one.
Data minimisation, Article 5(1)(c). Processing must be limited to what is necessary. A full production copy is the definition of unnecessary: a developer debugging a pagination bug needs a thousand rows with realistic shape, not five million rows with real people's medical histories.
Security of processing, Article 32. You must implement measures appropriate to the risk. Non-production environments are, in practice, where those measures are weakest. Access is broader — contractors, new hires, the entire engineering team rather than a specific on-call rotation. Logging is thinner. Backups are ad hoc. Credentials are shared. The environment is frequently reachable from more places, and the alerting that would catch exfiltration in production often does not exist.
That asymmetry is the whole problem. You have taken the data whose compromise would be a reportable breach and moved it to where a compromise is least likely to be noticed.
The regulatory record reflects this. Supervisory authorities across the EEA have issued fines specifically over test and development environments holding live personal data, and the pattern in the decisions is consistent: the organisation could not articulate why the data needed to be there.
Pseudonymised is not anonymous
The second misunderstanding is subtler and much more consequential, because it is where well-intentioned teams go wrong.
Article 4(5) defines pseudonymisation as processing personal data so it can no longer be attributed to a specific person without additional information kept separately. Replacing names with user_8871, hashing email addresses, swapping real dates of birth for offsets — all pseudonymisation.
Recital 26 then says the quiet part: pseudonymised data that could be attributed to a person by the use of additional information is still personal data. The full weight of the regulation still applies. Pseudonymisation is a security measure the GDPR credits you for under Article 32, not an exit from scope.
Anonymous data is a different category entirely. Recital 26 puts it outside the GDPR altogether — but the bar is irreversibility, assessed against "all the means reasonably likely to be used" by anyone, including future means. That last clause is doing enormous work. Anonymisation that is adequate against today's auxiliary datasets may not be adequate against next year's.
Why masking usually fails
The re-identification literature is unambiguous, and three results are worth knowing:
Latanya Sweeney, 1997. Using only ZIP code, date of birth and sex, Sweeney re-identified the Governor of Massachusetts in a supposedly anonymised state employee health dataset by cross-referencing a $20 voter roll. She estimated 87% of the US population was uniquely identifiable from those three fields. Philippe Golle's 2006 replication put the figure closer to 63% — lower, still catastrophic.
AOL, 2006. AOL published twenty million search queries from 650,000 users with usernames replaced by numbers. Within days, journalists identified individual users from the content of their own searches. The queries were the identifier.
Netflix Prize, 2008. Narayanan and Shmatikov matched an anonymised set of movie ratings against public IMDb reviews and re-identified subscribers. Eight ratings with approximate dates were enough to uniquely identify 99% of records.
The unifying lesson is that identity lives in the combination of fields, not in the ones labelled "identifying". Masking the name column while keeping postcode, birth date, transaction timestamps and purchase history preserves a fingerprint. And the fields teams are least willing to mask — the ones the bug actually depends on — are exactly the high-cardinality ones that carry the most identifying information.
Free text is worse still. A notes column, a support-ticket body, an address line 2 — these contain names, phone numbers and account references in unpredictable formats. Regex-based scrubbing catches the patterns you anticipated. It does not catch "spoke to Dave's wife on the mobile in her maiden name."
The operational costs nobody budgets for
Set the law aside; the engineering case is independently strong.
Every restore is a fresh exposure. Masking is not a one-off. Someone has to maintain the masking rules against a schema that changes weekly. A new column ships on Tuesday, the masking script does not know about it, and Wednesday's refresh carries it through in clear text. This failure is silent by construction.
Production data makes tests worse. It is heavily biased toward the cases that already work. Real datasets under-represent exactly what you need to test: the empty state, the account with 40,000 orders, the name with an apostrophe or a right-to-left script, the address with no postcode, the amount that is a negative zero. Production data tests the happy path, at great expense and with real legal exposure.
It cannot be committed. A fixture you cannot check into the repository cannot be part of a reproducible test. Every developer ends up with a slightly different local dataset, and "works on my machine" becomes structurally true.
Right-to-erasure requests fan out. When someone exercises Article 17, you must erase their data everywhere. If production copies flow into staging, into three developer laptops, and into a data-science notebook, your erasure obligation follows. Most organisations cannot enumerate the copies, let alone erase them.
Third countries. If a developer in a jurisdiction without an adequacy decision pulls a copy locally, that is an international transfer requiring its own Chapter V basis. Nobody signs standard contractual clauses for a laptop.
What to do instead
The alternative is not "test with three rows of foo". It is building fixtures that carry production's structure without its content.
Start from the schema, not the dump. What the failing test needs is the shape of the data: cardinalities, distributions, the ratio of accounts with zero orders to accounts with many, the fact that 3% of addresses lack a postcode. All of that can be measured in production as aggregate statistics — which are not personal data — and reproduced synthetically.
Keep the checksums real. A synthetic dataset is only useful if it survives your validators. Card numbers that satisfy Luhn, IBANs that satisfy mod-97, barcodes with correct GTIN digits. Data that fails at the first guard clause never exercises the code you are testing.
Use reserved ranges. Where a standard sets aside a block for testing — 192.0.2.0/24, example.com, the 555-01xx phone range — use it, so synthetic data that escapes into a real system fails harmlessly rather than reaching a stranger.
Make it reproducible. Generate from a seed so the same fixture can be regenerated identically rather than stored, and so a failing CI run can be replayed exactly.
Over-represent the edges deliberately. This is where synthetic data beats a production copy outright. You can decide that 5% of your names contain combining diacritics, that one account has a million rows, that some orders have a null shipping address. Production will not hand you those in the proportions you need.
Where you must transform real data, keep it local. If a specific bug genuinely requires a production record, the safest shape is a scrubbing step that runs inside the production boundary and emits only the transformed result — never a full copy moved first and cleaned later. Better still, run the redaction client-side, so the sensitive text never traverses a network at all.
A realistic policy
The version that survives contact with an engineering organisation is short:
- Non-production environments hold no personal data. Stated as an invariant, not a preference.
- Fixtures are generated and committed, from seeds, in the repository, reviewable in a diff.
- Distributions are derived from production aggregates, refreshed periodically — statistics cross the boundary, records do not.
- If a bug truly requires real data, it is debugged in production under the access controls that already exist there, with an audit trail, rather than by relocating the data to somewhere those controls do not apply.
- Automated detection. A CI check that greps non-production dumps for things shaped like real email addresses and card numbers catches the Tuesday-afternoon shortcut before it becomes Wednesday's incident.
Point four is the one that meets resistance and the one that matters most. The instinct to copy data out of production is really a complaint about production tooling: no read-only role, no safe query console, no way to inspect one record without an admin credential. Fixing that is a smaller project than maintaining a masking pipeline forever, and it removes the reason the shortcut exists.
The dump takes twenty minutes. The obligations it creates last as long as the copies do — and the copies outlive everyone's memory of having made them.
This is an engineering discussion, not legal advice. Whether a particular processing activity is lawful depends on your circumstances and jurisdiction; ask a qualified data protection adviser.