Every engineering team eventually wants the same forbidden thing: a copy of the production database to test against. Real data has the shapes, the edge cases, and the weird distributions that synthetic data never captures. It's also, legally speaking, radioactive. The moment a customer's name, card number, or medical record lands in a staging environment or a developer's laptop, you've created a breach waiting to happen and, depending on where you operate, a violation of GDPR, HIPAA, or PCI DSS.
CodeVeil exists to resolve that tension. It takes real data and produces data that behaves exactly like it — same formats, same relationships, same statistical shape — but contains none of the actual sensitive values. This post is about how it does that without falling into the traps that make naive masking useless.
Why masking beats deletion
The obvious approach is to just strip sensitive fields: null out the emails, blank the card numbers, delete the names. It's also the approach that breaks everything downstream.
If you null an email column, every code path that validates, parses, or joins on email now behaves differently in your test environment than in production. Your tests pass against data that can't occur in reality. The bugs you were trying to catch hide in exactly the fields you destroyed.
Masking takes a different stance: preserve everything about the data except its meaning. A masked email is still a syntactically valid, unique, correctly-formatted email — it just belongs to no one. That's the difference between test data you can trust and test data that lies to you.
Format-preserving obfuscation
The core technique is format-preserving masking. The output looks like the input at every level a program might inspect, so nothing downstream can tell the difference.
- A credit card number is replaced with a different number that still passes a Luhn checksum and keeps the issuer's prefix, so payment-code paths behave identically.
- A phone number keeps its country and area code structure.
- A national ID keeps its length, check digits, and internal format rules.
- A date of birth shifts within a bounded window, so ages stay realistic and cohort analytics don't collapse.
Referential integrity is the part people underestimate. If a customer's ID appears in an orders table, a payments table, and a support-tickets table, masking has to replace it with the same new value in all three, or your joins break. CodeVeil masks deterministically: the same input, under the same key, always produces the same output. That means customer_9f3a becomes the same masked token everywhere it appears, and every foreign-key relationship in your schema survives the process intact.
When you genuinely need to recover the original — a support escalation, a legal request — reversible tokenization is available. The mapping is held under managed keys, so recovery is possible for the people authorized to do it and impossible for everyone who holds only the masked copy.
The batch API
Masking one record is easy. Masking a hundred million is an engineering problem, and it's the one that determines whether a tool is usable in practice.
CodeVeil's batch API accepts up to 10,000 records per request. The job runs the same deterministic transforms across the whole set, which is what keeps referential integrity consistent even when a single logical dataset is split across thousands of requests — the key, not the request boundary, decides the output.
A typical pipeline call looks like this:
curl -X POST https://api.codeveil.com/v1/mask/batch \
-H "Authorization: Bearer $CODEVEIL_KEY" \
-H "Content-Type: application/json" \
-d '{
"schema": "customers",
"records": [
{ "id": "c_1042", "email": "[email protected]", "card": "4539511234567890" },
{ "id": "c_1043", "email": "[email protected]", "card": "4485119876543210" }
],
"rules": {
"email": "email_preserve_domain",
"card": "luhn_preserve_prefix",
"id": "tokenize_deterministic"
}
}'Each field names the rule that applies to it, so one schema definition drives every run. For teams that don't want a data pipeline at all, direct connectors for PostgreSQL, MySQL, MongoDB, and S3 read the source, mask in place or to a destination, and write the result — no glue code in between.
Compliance is a design constraint, not a feature
It's tempting to treat GDPR and HIPAA as a checklist you tack on at the end. In practice, compliance shapes the architecture from the start.
- Data minimization (GDPR Article 5) is the whole premise: masked data isn't personal data, so it falls outside much of the regulation's scope. That's only true if the masking is irreversible for the parties who hold it — which is why the tokenization keys live separately from the masked output.
- HIPAA de-identification has a specific bar: remove or transform 18 categories of identifiers. CodeVeil ships rule templates mapped to those categories so a health dataset can be de-identified against the actual standard, not a guess at it.
- Audit trails matter because "we masked it" is a claim you may have to prove. Every batch job records what ran, against which schema, under which rules, so you have an audit-ready record instead of a promise.
The subtle failure mode here is masking that's technically reversible by inference. If you shift every date of birth by exactly 30 days, someone who knows one real value can unmask the entire column. CodeVeil's transforms are keyed and non-linear specifically to close that door — the realism can't come at the cost of the anonymity.
Where this fits
The teams that get the most out of CodeVeil are the ones who've felt the pain: the QA lead who needs realistic data but can't touch production, the platform team building a compliant analytics warehouse, the company preparing for a SOC 2 audit and discovering how much PII has leaked into places it shouldn't be.
The pitch is simple. You shouldn't have to choose between test data that's realistic and test data that's safe. Format-preserving, referentially-consistent masking gives you both — and it's the only version of "give me a copy of prod" that a security team can actually approve.