The Illusion of Clean Data: Why Generic Profiling Fails Healthcare Identifiers

New in TestGen Enterprise: the Commercial Pharma test pack. Twelve healthcare-identifier tests that run the check digit, tell a 10-digit NDC from an 11-digit one, and write themselves off your profiling run.

Written by Chris Bergh on August 26, 2026

PharmaDataOps TestGenData Quality
The Illusion of Clean Data: Why Generic Profiling Fails Healthcare Identifiers

Key points

  • The Commercial Pharma test pack is a new generation set in DataOps TestGen Enterprise, released on Friday, August 28, 2026. It adds 12 healthcare-identifier test types to the standard set rather than replacing anything you already run.
  • The 12 new test types cover NPI, DEA, three forms of NDC, ICD-9 and ICD-10 diagnosis, ICD-10 procedure, HCPCS J-codes, CMS Place of Service, and ClinicalTrials.gov NCT numbers.
  • An NPI's tenth digit is a Luhn check digit computed over the first nine with the 80840 prefix folded into the sum, so two transposed digits break the arithmetic. A pattern test that counts to 10 digits accepts the transposition and the join silently drops the prescriber.
  • The FDA assigns NDC as 10 digits in three segment configurations, 4-4-2, 5-3-2, and 5-4-1, and each pads to a different 11-digit value. The same 10 characters can be three different products, which is why the pack ships three NDC tests rather than one.
  • Generation requires the column name and the profiled values to agree, because shape alone cannot decide: B2111ZZ is a legal ICD-10-PCS procedure code and a legal ICD-10-CM diagnosis code. A column named code_3 gets nothing and has to be added by hand.
  • Seven of the 12 tests default to Fail and five to Warning. Fail is for values that can only be wrong, such as a broken check digit or a Place of Service code CMS never published. Warning is for convention differences, such as hyphenated NDCs meeting a product master that stores them unhyphenated.
  • These tests check format and check digits, not existence. They never call NPPES, so a failure is conclusive and a pass is not: a Luhn-valid NPI is a number that could have been issued, not proof one was.

A rep opens a ticket on Monday. Their territory is short about 40 TRx for the quarter, and they want to know where it went.

You trace it. One prescriber in that alignment has an NPI in the specialty pharmacy feed that doesn’t match the NPI in Veeva. It isn’t missing and it isn’t null. Ten digits, all numeric, sitting in the column looking exactly like an NPI. It just isn’t one. Two digits are transposed, and the join has been quietly dropping that prescriber since March.

Your pattern test on that column is green. It’s been green the whole time.

That’s the gap this release closes. On Friday, August 28, 2026, DataOps TestGen Enterprise ships a new generation set called Commercial Pharma: 12 test types that validate healthcare identifiers the way the specs define them, check digits included. They generate off your profiling run alongside the standard set, and you configure none of it. The rest of this post is what each one does and where it stops.

The check digit exists for exactly this

An NPI is 10 digits, and the tenth one isn’t data. It’s a Luhn check digit computed over the first nine, with the 80840 prefix the spec requires folded into the sum. Transpose two adjacent digits and the sum stops landing on a multiple of 10.

That’s the whole point of the design. Hand-keying and OCR produce transpositions, so the identifier carries a digit whose job is to catch them. A regex that counts to 10 throws that away.

Slide titled 'Problem 1: the arithmetic blindspot'. Ten digit tiles spell 1234567890 with the 5 and 4 swapped and highlighted in red, while a green label above reads 'PASS: 10 digits found'. Caption: a standard regex test counts to ten and stops, so it cannot catch hand-keying or OCR transpositions. NPIs do not fail because they are missing, they fail because they are mutated.

DEA registration numbers work the same way. The seventh digit has to equal the first, third, and fifth digits added together, plus twice the sum of the second, fourth, and sixth, mod 10.

IMPORTANT

Both check digits are new test types in this release. Valid NPI runs the Luhn calculation and Valid DEA runs the DEA calculation, in SQL, on the landing table, on every run.

The 10-versus-11 problem, stated precisely

NDC is where most commercial pharma warehouses lose a day a quarter.

The FDA assigns a 10-digit code in one of three segment configurations: 4-4-2, 5-3-2, or 5-4-1. Billing wants 11 digits in 5-4-2, which you get by padding the short segment with a leading zero. That works fine as long as you can still see the segments.

Slide titled 'Problem 2: the NDC ambiguity trap'. Three labelled configurations, 4-4-2, 5-3-2, and 5-4-1, fall into a funnel marked 'hyphens stripped' where the labels are torn away, and what drops out is a single box reading 'ambiguous 10-character string'.

Now somebody hands you a 10-digit string with the hyphens stripped. Take 1234567890.

ConfigurationSegmentedPadded to 11
4-4-21234-5678-9001234567890
5-3-212345-678-9012345067890
5-4-112345-6789-012345678900

Table 1: three products, the same 10 characters, and nothing in the value to tell you which one you’re holding.

So the pack gives you three tests instead of one. Valid NDC takes the package-level and 11-digit forms your 867 files and claims data carry, and it treats a bare 10-digit string as invalid on purpose. Valid NDC Product takes the two-segment codes your product master and formulary files carry. Valid NDC Any Form is for the warehouse that strips separators as a convention and can live with the ambiguity, and it still catches the leading zero Excel ate, the UNKNOWN somebody typed into a required field, and the value that is somehow 12 characters long.

Claims-based diagnosis data

Four tests cover ICD. Valid ICD-10 Diagnosis, Valid ICD-9 Diagnosis, and Valid ICD Diagnosis for the column that spans the transition and honestly holds both, which describes most longitudinal claims extracts nobody has cleaned since 2015.

Valid ICD-10 Procedure checks the seven-character PCS shape, and it knows the code set leaves out I and O so nobody confuses them with 1 and 0. The diagnosis tests accept the dotted and undotted forms, because 837 claims drop the decimal and your vendor extract may or may not have put it back.

Slide titled 'Problem 3: contextual degradation (claims and ICD)'. A timeline running from 2015 shatters, and three examples sit above it: E119 flagged 'missing decimal (837 claim format)', a struck-through letter O flagged 'ICD-10-PCS intentionally excludes O and I', and B2111ZZ marked 'legal shape'.

Valid NCT covers ClinicalTrials.gov identifiers on the medical affairs side.

Site of care, if you sell anything infused

Valid HCPCS J-Code checks the J plus four digits that Part B buy-and-bill runs on. It’s one slice of HCPCS Level II, scoped to drugs on purpose.

Valid CMS POS checks Place of Service, and it’s the one test in the pack that checks meaning rather than shape. CMS publishes Place of Service as a small fixed list, so the test carries the actual list and checks membership against it. The copy in the pack is current as of August 2026. If your site-of-care mix suddenly shows growth in a POS code that doesn’t exist, you’d rather hear it from a test than from the slide.

Slide titled 'Problem 4: shape vs. meaning (site of care)'. A Place of Service badge showing the code 99 passes a generic shape check labelled 'PASS: 2 numeric digits' on the left, and fails an ontology meaning check on the right, labelled 'FAIL: phantom code does not exist', checked against the CMS August 2026 published list.

Four different problems, one shape. Every failure above is well-formed enough to clear a generic pattern test, which is why the column stays green while the join quietly drops rows.

Table titled 'Generic regex vs. healthcare reality', with the note that every single critical failure hiding in your vendor feeds will pass a standard regex test. Four rows, a transposed NPI, a stripped 10-digit NDC, a phantom POS code 99, and an ICD-10 code containing the letter O, each showing PASS (valid shape) under generic pattern match and FAIL (broken structure or meaning) under a healthcare-specific check.

You don’t configure any of it

Profile the schema. The tests write themselves. It’s the same mechanic that wrote data quality tests for 3 million NYC taxi rides in five minutes, pointed at identifiers instead of fare amounts.

Generation reads the column name and the profiled values together, and it needs both to agree. A column matching %npi% whose most frequent pattern is 10 digits gets Valid NPI. Unless the name says phone, fax, or contact. Unless profiling already tagged the values as a US phone number, which happens more than you’d like on vendor feeds where the HCP contact block sits three columns over.

The two-signal rule isn’t caution for its own sake. B2111ZZ is a legal ICD-10-PCS procedure code and a legal ICD-10-CM diagnosis code. The shape can’t tell you which one you’re holding. The column name can.

The cost is that a column named code_3 gets nothing. Add that one by hand, the same way you shop the rest of the test catalog. That’s the residue no profiler reaches, and on a commercial team your best data quality rules live in someone else’s head: the analyst who knows which specialty pharmacy reports units instead of scripts.

The specific test replaces the generic one

You already had Pattern Match, and on a column Valid NPI now covers, it’s noise.

So when a Commercial Pharma test generates on a column, it removes the Pattern Match test that generated on that same column in the same run. Only that run’s tests. Only unlocked ones. Anything you wrote by hand or locked stays exactly where it is. Before you confirm generation, the dialog tells you the most it will delete.

What these tests won’t tell you

These check format, and a check digit where the spec defines one. They never call NPPES.

Hold onto that distinction. A Luhn-valid NPI is a number that could have been issued. It isn’t proof one was, and it says nothing about whether the provider is still active or whether the NPI belongs to the prescriber your feed says it does. A failure is conclusive. A pass isn’t.

Same discipline down the list. A well-formed ICD-10 code from a category retired in 2019 passes. A DEA number carrying a registrant-type letter the DEA never assigned passes. Valid NCT accepts any eight digits, so on a column that stores the numeric part alone, 20260815 passes. Valid CMS POS is the exception, and only because that code set is small enough to carry.

None of this replaces Veeva Network or whatever you use for HCP mastering. MDM tells you what it could match. These tell you what arrived broken, per column, per feed, on the landing table, before a match rate has the chance to hide it. That distinction is the same one that makes testing commercial pharma data harder than anyone tells you: the failures live between the feeds, and the feed itself never errors.

Empty and null values don’t count as invalid anywhere in the pack. That’s a Required Entry question and a different test. The format checks ignore trailing spaces, so an identifier sitting in a fixed-width CHAR column doesn’t fail for its padding. The tests fold case first, since NCT and ICD codes come back lowercase from URLs and hand-built extracts constantly.

Introducing the Commercial Pharma test pack

All 12 tests ship as one generation set, and they sort into three kinds of check.

Arithmetic is Valid NPI and Valid DEA: compute the check digit the spec defines, compare it against the one sitting in the value, fail on a mismatch. Configuration is the three NDC tests plus Valid NCT, where the only question is which segment layout you are holding. Ontology is the other six, which check a value against a code system somebody actually publishes: the four ICD tests, Valid HCPCS J-Code, and Valid CMS POS.

The grouping is worth keeping in mind when a test fires, because it tells you who to go and talk to. An arithmetic failure goes back to whoever keyed the value. A configuration failure is usually a conversation about file format with the vendor sending it. An ontology failure means somebody is using a code the code system does not contain.

Slide titled 'The solution: healthcare identifiers designed to spec'. The 12 tests grouped into three families: Arithmetic holds Valid NPI and Valid DEA, Configuration holds Valid NDC, Valid NDC Product, Valid NDC Any Form and Valid NCT, and Ontology holds Valid ICD-10 Diagnosis, Valid ICD-9 Diagnosis, Valid ICD Diagnosis, Valid ICD-10 Procedure, Valid HCPCS J-Code and Valid CMS POS.

Why some fail and some only warn

Seven of the 12 default to Fail. Five default to Warning. The line isn’t arbitrary.

A test defaults to Fail when a bad value can only be a bad value. Valid NPI and Valid DEA both run a check digit, so a failure there is arithmetic rather than opinion. Valid CMS POS checks membership in a code set CMS actually publishes, so a failure is a code that doesn’t exist. The four ICD tests check shapes the code systems define exactly, and a value outside them is wrong.

The rest default to Warning because a format mismatch is usually a convention difference. Your vendor sends NDCs with hyphens and your product master stores them without. Neither side is broken. Somebody just never agreed on a form. A J-code column that also carries the rest of HCPCS Level II lights up the same way, and that’s a scoping decision rather than a data defect.

Change the default on any of them. Severity is set per test definition, so if unhyphenated NDCs really do break your gross-to-net, promote that one to Fail and let it stop the run.

The twelve new test types

TestWhat it checksSeverity
Valid NPI10 digits plus Luhn check digit with the 80840 prefixFail
Valid DEALetter, letter or 9, seven digits, with the DEA check digitFail
Valid NDC11-digit or hyphenated package form (5-4-2, 4-4-2, 5-3-2, 5-4-1)Warning
Valid NDC ProductHyphenated two-segment product form (4-4, 5-3, 5-4)Warning
Valid NDC Any FormAny recognized NDC form, including bare 10-digitWarning
Valid ICD-10 DiagnosisICD-10-CM shape, dotted or undottedFail
Valid ICD-9 DiagnosisICD-9-CM numeric, V-code, and E-code shapesFail
Valid ICD DiagnosisEither ICD-9-CM or ICD-10-CM, for mixed columnsFail
Valid ICD-10 ProcedureSeven characters, no I or OFail
Valid HCPCS J-CodeJ followed by four digitsWarning
Valid CMS POSMembership in the published CMS Place of Service setFail
Valid NCTEight digits, with or without the NCT prefixWarning

Table 2: all 12 are Validity tests on a single column, and all belong to the Commercial Pharma generation set.

Where it runs

Every test is hand-written per database flavor, from PostgreSQL and Snowflake to Oracle and SAP HANA, because there’s no portable way to express Luhn arithmetic in SQL and faking it produces a test that passes everything.

When a test fails you get the failing values and their counts, ranked. Not a percentage. The actual codes, so you can take them back to the vendor with the file name and the row count.

Slide titled 'Actionable evidence, not just percentages'. A report headed 'URGENT: NPI structural failures detected' lists ranked failing values: 1932458872 with a Luhn check digit mismatch at 4,102 rows, 1885443901 with the same error at 2,845 rows, and 0000000000 with an invalid root structure at 912 rows, above an 'export to vendor (.csv)' button.

That is the same reporting that turns a feed-by-feed inventory of what tends to break into something a launch team can act on rather than argue about.

Turning it on

The new pack ships with TestGen Enterprise. Open Generate Tests and select Commercial Pharma alongside Standard.

Threshold defaults to zero invalid values on every test in the pack. That’s the right default for an identifier and the wrong one for a vendor feed you’ve never profiled. Point it at last week’s drop first and see what comes back.

Start with the free version

Everything underneath the Commercial Pharma pack is open source: the profiler that reads your schema, the standard test set it generates, the scoring, the hygiene detection. You can run it against a copy of last week’s specialty pharmacy file this afternoon, behind your firewall, without talking to anyone.

Closing slide on dark navy reading 'Stop losing days to ambiguous data', with the line 'Test identifiers the way they were designed. Activate the Commercial Pharma pack in TestGen Enterprise.'

TIP

Download and install DataOps TestGen. It’s free, it runs in your environment, and the profiling run it produces is the same one the new Commercial Pharma tests generate from. When you want the identifier pack, request a demo of TestGen Enterprise.

And if the answer is “we don’t have the people for this”

Profiling tells you what’s broken. Fixing it is a different job, and on most commercial teams there is nobody free to do it.

We do that part too. DataKitchen builds and runs commercial pharma data warehouses: the specialty pharmacy and claims feeds landed, the identifiers reconciled, the alignment and gross-to-net logic tested, the reports the launch team actually asks for. Same tests, run by the people who wrote them, and you keep the platform at the end of it. Our most recent customer story is exactly this job: a rare-disease biotech with a phase III indication ahead of it and a finite runway told us they had a Cadillac and needed a Corolla. Consolidating two hourly vendors into one managed service cut the annual cost of running their commercial data by about 58%, and the savings went into the launch.

If the argument for testing this data is the part you’re still making internally, the rest of the case is here: why testing commercial pharma data is harder than anyone tells you, the commercial data most likely to be wrong, how a rare-disease biotech cut its data bill to fund the next launch, and what to cut when you’re hoarding cash before the FDA gate opens.

The whole argument on one page

Something to send to the person who owns the feed:

Infographic titled "Beyond the false green: precision validation for commercial pharma data", split into two halves. The left half, "The hidden failures of generic testing", shows a transposed NPI still earning a PASS stamp from a 10-digit pattern test while the join breaks, one 10-digit NDC resolving to three different products under 5-4-1, 4-4-2, and 5-3-2 segment padding, and appearance checks that count characters contrasted against arithmetic that computes check digits. The right half, "Spec-aware validation and automation", shows two-signal generation combining column name with pattern profile, severity split between mathematically incorrect values that Fail and convention mismatches that Warn, and three example tests: Valid NPI at Fail, Valid NDC at Warning, and Valid CMS POS at Fail.

What a generic pattern test misses, on the left, and what the Commercial Pharma pack checks instead, on the right.


FAQ

What are the key points in this blog?

DataOps TestGen Enterprise has a new generation set called Commercial Pharma, shipping 12 healthcare-identifier test types as of Friday, August 28, 2026. They check the NPI and DEA check digits, the three NDC segment configurations, ICD-9 and ICD-10 shapes, HCPCS J-codes, CMS Place of Service, and NCT numbers. Tests generate from your profiling run when the column name and the profiled values agree. They validate format, not existence.

Why does a valid-looking NPI still break a join?

Because 10 numeric digits is not the whole spec. The tenth digit is a Luhn check digit computed over the first nine, with the 80840 prefix folded into the sum. Transpose two adjacent digits and the sum stops landing on a multiple of 10. A pattern test that counts to 10 accepts the transposition, the join drops the prescriber, and every number downstream is quietly low.

Why are there three NDC tests instead of one?

Because a bare 10-digit NDC is ambiguous. The FDA assigns 10 digits in three segment configurations, 4-4-2, 5-3-2, and 5-4-1, and each pads to a different 11-digit value. Valid NDC takes the package-level and 11-digit forms, Valid NDC Product takes the two-segment product forms, and Valid NDC Any Form accepts any recognized shape for warehouses that strip separators.

How does TestGen decide which identifier test to generate?

It needs two signals to agree: the column name and the profiled values. A column matching %npi% whose most frequent pattern is 10 digits gets Valid NPI, unless the name says phone, fax, or contact, or profiling already tagged the values as a US phone number. Shape alone is not enough, because B2111ZZ is a legal ICD-10-PCS procedure code and a legal ICD-10-CM diagnosis code.

Do these tests confirm that an NPI was actually issued?

No. They check format, and a check digit where the spec defines one. They never call NPPES, so a Luhn-valid NPI is a number that could have been issued, not proof one was. A failure is conclusive. A pass is not. The tests sit on the landing table beside your MDM, catching what arrived broken before a match rate hides it.

Why do some tests default to Fail and others to Warning?

Seven of the 12 default to Fail and five to Warning. A test fails when a bad value can only be a bad value: a broken check digit is arithmetic, and a Place of Service code outside the published CMS list does not exist. It warns when the mismatch is usually a convention difference, such as a vendor sending hyphenated NDCs when your product master stores them without.

Install Open Source TestGen Free, no vendor lock-in Request a Demo See the Commercial Pharma pack in TestGen Enterprise
Chris Bergh

Chris Bergh

CEO and Head Chef at DataKitchen. He is a leader of the DataOps movement and is the co-author of the DataOps Cookbook and the DataOps Manifesto.

LinkedIn →