İçeriğe geç
wedevit

September 16, 2026 · 8 min read · software

İlhan Buğra Aslan

Should you copy production data into your test environment? Masking, subsetting and synthetic data


The short answer is no: a raw copy of live customer data does not belong in a test environment. The objection that you cannot test properly without realistic data is fair though, so the answer does not stop at "don't copy it." A setup that actually works has three layers: a small masked subset of production for realism, synthetic records for edge cases and volume, and automated tests that create the data they need. Then there is a fourth problem most teams discover the hard way, which is that the worst accidents come not from the data itself but from a test environment making real calls to the outside world.

It starts as one copy

According to a K2view survey published in March 2026, organisations keep an average of 29 full database copies across non-production environments, and 44% of respondents keep more than 26. In the same study only 4% of development and test environments were considered fully compliant with privacy requirements, while 76% reported a sensitive-data incident in a non-production environment within the past three years.

Perforce surveyed 518 enterprise leaders in July 2026 and found something more telling. 99% said their organisation has a masking policy for non-production environments. 84% said exceptions to that policy are allowed. 43% had failed an audit and 34% reported a breach or theft involving non-production data. Both are vendor surveys and should not be read as precise measurements, but the direction is consistent: a policy on paper and a policy in the environment are different things.

The reason is mundane. Test environments never get the attention production gets. The access list is wider, logging is thinner, passwords are weaker, nobody wired up alerts, and sometimes a box was opened to the internet for a demo and stayed that way. Copying data does not reduce its sensitivity. It reduces the protection around it.

Realistic is not the same as real

The developer's objection has merit. Production data carries genuine weirdness: half-finished address records from 2014, customers entered twice, notes crammed into a description field, columns nobody ever filled in. A clean seed file has none of that, which is why a suite that passes against clean seed data breaks on release day.

What you need, though, is data with the right shape, not data belonging to a real person. Once a team accepts that distinction, the rest is an engineering problem with known solutions.

Masking is not "we removed the names"

Bad masking is more dangerous than none, because it convinces you the problem is solved. Masking that holds up preserves four properties.

It has to be deterministic, so the same input produces the same output in every table. Otherwise customer.id and order.customer_id stop lining up, joins come back empty, and the team declares the environment broken and goes back to restoring production.

It has to preserve format. A national ID should still satisfy its own check digit, an IBAN should pass its checksum, an email should look like an email. If it does not, your own validation layer rejects the test data.

It has to preserve uniqueness. Mask two rows to the same value in a column with a unique index and the load fails.

It has to preserve distribution. Date ranges, country mix and null ratios should stay close to the original so query plans resemble production. If every masked customer lives in the same city, index behaviour in staging tells you nothing about production.

There is one more trap. Stripping names is not de-identification. The paper Narayanan and Shmatikov presented at the 2008 IEEE Symposium on Security and Privacy showed that subscribers in Netflix's "anonymous" prize dataset could be identified by matching their ratings against public IMDb profiles. A combination of indirect identifiers, birth date plus postcode plus order history, is usually enough on its own. The riskiest columns are free text: support ticket bodies, order notes, cancellation reasons. Names, phone numbers and account numbers sit there in plain text, and because the column is called notes it never makes it into the masking script.

Pseudonymisation and anonymisation are also worth keeping apart. The EDPB guidelines 01/2025, adopted in January 2025, are clear that if a reversible mapping table still exists somewhere, the data remains personal data and the obligations travel with it. Local data protection rules are a separate discussion; the engineering decision stands on its own, and it is to carry less data in the first place.

Subsetting: 20 GB instead of 2 TB

The cost of a full copy is not only risk, it is time. Restoring a 2 TB database per environment takes hours, a masking script over that volume runs all night, and no developer is pulling it down to a laptop.

Subsetting means extracting a vertical slice that keeps referential integrity intact. You pick an anchor, say the last 90 days of orders, then follow foreign keys to collect everything related. Two traps come with it. Lookup and configuration tables (countries, currencies, tax rates, product categories) must always be copied in full rather than sliced. And the slice needs deliberately awkward records added to it: a refunded order, a partial payment, a multi-line invoice, a cancelled subscription. A random 90 days will not contain all of them.

Done properly, environment setup drops to minutes. That is what makes throwaway environments per branch possible in your delivery pipeline, and a fresh environment per branch is the real fix for the test data problem.

Where synthetic data helps and where it does not

Synthetic data covers two gaps masked production data cannot. Edge cases first: negative stock, a 40-character name, 29 February, an address with emoji in it, a right-to-left script, an amount that breaks on rounding. Those either do not exist in production or exist once and never land in your subset. Volume second: if load testing needs 50 million rows, you do not have to source them from real customers.

What it cannot do is reproduce the historical mess a ten-year-old system has accumulated. That is why teams that use all three approaches do better than teams arguing about which one is correct. In the Perforce survey, 86% use static masking, 60% use dynamic masking and 51% use synthetic data. The numbers add up to well over 100 because these are layers, not alternatives.

The accident usually happens in someone's inbox

Say the masking is done. If the test environment still points at a real SMTP relay and the masked addresses happen to resemble real ones, an overnight job cheerfully tells your customers their order has shipped. This happens more often than data leaks and it is far more visible when it does.

The same risk runs through every outbound integration: real invoices issued at the e-invoicing provider, real shipments created at the carrier, real charges attempted at the payment gateway, real SMS delivered. The controls are cheap:

  • Run a mail catcher and never let non-production SMTP reach the internet.
  • If real delivery is unavoidable, allowlist recipients so only your own domain can receive.
  • Use each provider's sandbox credentials and make sure production keys never exist outside production.
  • Default-deny outbound network traffic and open third-party endpoints one at a time.

These four take less effort than masking a database and pay off on the first day.

What the standards actually say

Control 8.33 of ISO/IEC 27002:2022 treats test information as its own topic and asks for four things: the same access controls in test environments as in production, a separate authorisation each time operational data is copied, logging of that copying and use, and deletion of the data once testing is done. The "separate authorisation each time" clause is the one that gets skipped, because in most shops taking a copy is a single command that no approval flow ever sees.

For systems handling card data the rule is sharper. Requirement 6.5.5 of PCI DSS v4.0 prohibits live PANs in pre-production environments. The only exception is bringing that environment into the cardholder data environment and protecting it under every applicable requirement, which is a cost most teams would rather not carry.

Leaving staging open to the internet

Even masked data should not sit on an unprotected host. The minimum set for a non-production environment is short: no access without authentication, blocked from search engines, hosted on a separate domain, admin accounts inherited from a production restore reset after every load, and a named owner plus an expiry date for the environment itself.

The statement of claim the Australian regulator filed against Optus shows why that last item matters. The allegation is that an access control coding error went live in 2018, the endpoint became internet-facing in 2020, and the error survived several reviews until the 2022 breach. An endpoint nobody owns can stay unnoticed for years, which is why an inventory counts as a security control rather than paperwork.

How to start

Do not begin by procuring a test data platform. Follow a sequence that finishes inside three weeks.

  1. Inventory it. Which environments hold a copy of production data? Include the demo box, the analytics replica, an old backup file and the dumps on developer laptops.
  2. Flag the sensitive columns. By reading sample rows, not by scanning column names. Treat free-text fields as their own workstream.
  3. Write one masking job. A script is enough before you buy anything. Run it nightly and produce a single sanitised dump.
  4. Verify the masking. Are there values in sensitive columns that still match production exactly, do foreign keys still resolve, do unique constraints still hold? Automate these checks or the first schema change will break them silently.
  5. Cut the outbound calls. Mail catcher, sandbox credentials, default-deny egress.
  6. Set a rule. Every copy taken from production needs written approval, a log entry and an expiry date. No copy lives forever.

One thing to do this week

Connect to your test database and select ten random rows from the customer table. If a real person's name, email and phone number come back, you have your answer. Then place an order through staging and check whether an email actually left the building. Both checks take half an hour, and both tend to surprise the people who run them.


Need help with this topic?

get in touchall posts