Comparison

Why not just copy production and scrub it?

It is the most tempting option on the list, because the realism is free — it is the real thing, with your actual distributions, your actual edge cases, and the one customer whose name breaks the CSV export. The catch is not technical. It is that a scrubbed copy is usually still personal data, and it is still your responsibility on whatever box it lands on.

Side by side

Filling a staging or development environment with something to work against.

A scrubbed production copyReal Fake Data
RealismPerfect — the distributions, cardinality and oddities are the real onesRealistic per record, but not your actual distributions
Regulatory scopePseudonymised data is still personal data (GDPR Recital 26)No data subject exists, so there is nothing to be in scope
If staging leaksA production breach that happened to occur on a weaker perimeterSynthetic records tied to no one
Getting a fresh copyA pipeline someone owns — extract, mask, load, and re-mask when a column is addedA request
Joins after maskingBreak unless masking is consistent across every table that shares a keyA composed dataset is emitted already consistent
Giving a new developer accessAn access request, an approval, and possibly a contractNothing to approve
VolumeBounded by what production actually holdsAs many records as you ask for

Pseudonymised is not anonymised

This is the distinction the whole question turns on, and GDPR Recital 26 draws it explicitly. Data that has been pseudonymised, and “which could be attributed to a natural person by the use of additional information”, is to be “considered to be information on an identifiable natural person”. It stays personal data. It stays in scope.

Only genuinely anonymous information falls outside the Regulation — data “rendered anonymous in such a manner that the data subject is not or no longer identifiable”. That is a high bar, and replacing the name column does not clear it, because the identifying power usually does not live in the name. A full date of birth and a postcode already narrow a population to a handful of people; add one rare attribute and it is frequently a single person, whatever the name field now says.

The practical consequence is the part teams miss: if the copy is still personal data, then the retention limits, the access controls, the breach-notification duty and the record of processing all still apply — to staging, to the laptop that dump was restored onto, and to the backup of that laptop. Synthetic data avoids the question rather than answering it. There is no data subject, so there is nothing to protect.

the name is masked; the person is not
name              born         postcode   condition
"Anna Kowalska"   1987-06-17   00-950     <rare>
 ^ replaced        ^ kept        ^ kept     ^ kept

The three kept columns are the identifying ones.
A full birth date plus a postcode already narrows
a city to a few people; a rare attribute finishes
the job.

Recital 26: data that "could be attributed to a
natural person by the use of additional information"
is still personal data.

Masking breaks the joins — and fixing that recreates the problem

Mask each table on its own and referential integrity is gone: the customer in `orders` no longer matches the customer in `customers`, and half your application paths stop working. So the masking has to be consistent — the same input must map to the same output everywhere the key appears.

Consistent masking means keeping a mapping. And a mapping from real identifier to masked identifier is precisely the “additional information” Recital 26 describes: hold it, and the dump is re-attributable by definition. You have not removed the risk, you have concentrated it into one table that now needs guarding more carefully than the database it came from.

You can avoid keeping the mapping by deriving it — a keyed hash, say — but the key is then the mapping, with the same consequence and worse ergonomics. This is a genuine engineering problem with genuine solutions; the point is only that it is a project, not a `UPDATE` statement.

The scrubber is a subsystem that ages badly

The masking rules encode your schema, so every schema change is a change to them. A column added on Tuesday is a column nobody masked on Wednesday, and the refresh carries it through in the clear. Nothing fails; the pipeline reports success.

That is the failure mode worth planning around, because it is silent and it compounds. A free-text notes field, a support-ticket body, an address line someone typed a phone number into — the leaks are rarely in the columns the rules were written for. Catching them requires someone to audit the output against the current schema, on a schedule, forever.

A generated dataset has the opposite property: it does not degrade when the schema changes, because it never contained anything that needed removing in the first place.

When a production copy is the right answer

For performance and capacity work, use the real data, under real controls. Synthetic records will not reproduce your cardinality, your skew, or the one table where 4% of rows hold 90% of the volume — and those are exactly the properties that decide whether a query plan survives. A generator that claimed otherwise would be selling you something.

The same goes for investigating a specific incident: if you need to know what happened to one account, no amount of fake data helps. Do it with access controls and an audit trail, on the smallest slice that answers the question.

The realistic arrangement is not one or the other. Keep the tightly-governed production copy for the narrow cases that genuinely need it, and stop using it as the default filling for every developer laptop and every ephemeral preview environment — which is where the exposure actually accumulates.

None of this is legal advice, and the line between pseudonymised and anonymous is fact-specific. If you are relying on that line, get it reviewed by someone qualified to review it.

Fill an environment without inheriting the liability

One compose call returns a whole nested dataset — customers, their orders, every line item — internally consistent, valid against your checks, and tied to no one at all.