Postal code validation catches errors that country level checks never see
Postal code validation checks each record's postcode against a pattern selected by its country code, so a five digit test runs on United States rows and a two part test runs on United Kingdom rows. Report the pass rate per country rather than for the file as a whole, and flag failures instead of rejecting the record.
A mailing house came back on a 4,120 record United Kingdom file with 128 rejections, three days before a printed invitation was due to drop. Every one of those records had passed the country check, because every one of them said GB. Postal code validation was the test nobody had run, and it would have found all 128 in under a second.
The interesting part was what the failures had in common. They were not random.
Why does a valid country still produce an undeliverable record?
Because the country field and the postcode field are checked by different logic, and most teams only build the first one.
A country check asks whether the value is in a list. That is a closed question with a short answer, and it passes any record where somebody selected United Kingdom from a dropdown. The postcode in that same record can be a phone number, a house number, a fragment of an address, an empty string that the form turned into a space, or a genuine postcode with the space removed. None of those interact with the country check at all.
The gap matters because the postcode is doing more work than its size suggests. It routes the mail, it drives the geographic rollup below subdivision level, and on many shows it is one of the fields a matching pipeline uses to decide whether two registrations belong to the same person.
One pattern per country, selected by the country code
The rule table is keyed on the alpha-2 code, which is why country normalisation has to run first and why K12 owns that step. Each row of the table holds a country code and a regular expression, and the check looks up the expression by the record's country and applies it.
Three examples show the range of shapes involved.
The United States uses a five digit code, with an optional four digit extension. The Postal Service publishes the rules in Publication 28, Postal Addressing Standards, in a revision dated October 2024, which is the same document that defines the two letter state abbreviations used on the last line of an address.
The United Kingdom uses two parts. The Office for National Statistics describes the structure as an outward code of two to four characters and an inward code of three characters, so the whole thing runs from five to seven characters plus the separating space. The same source put the number of live unit postcodes at approximately 1.79 million as at August 2022, which gives a sense of how fine the geography underneath actually is.
The Netherlands uses four digits followed by two letters. Ireland uses a seven character Eircode with no relationship to proximity, so two neighbouring houses have unrelated codes.
Write the table as data, not as a case statement in a transformation. It changes, and when it changes you want a diff on a file rather than a code review.
Where should the rule table come from?
Nobody should be inventing these patterns from memory, and there is a published source for the underlying formats.
The Universal Postal Union maintains the international addressing standard S42, whose two parts were issued on 26 February 2020. Part A defines a set of postal address components used in worldwide addresses along with the languages for expressing address templates, and Part B holds the country specific templates, described as a library that can be incorporated into systems for managing addresses. The union also runs the Universal POST*CODE database, covering the postcodes of 192 member countries, and its own addressing material notes that there are more than 200 address formats in the world.
That last figure is the one to quote to anybody proposing a single global postcode regex. More than 200 formats means the file you are validating is being compared against the wrong rule for most countries the moment you generalise.
For a show with exhibitors from forty countries, you do not need all 200. Build the table for the countries that make up the bulk of your file, mark the rest as unvalidated, and report that share honestly rather than passing them by default.
Read the failures before you fix anything
Back to the 128 rejections. Of those, 119 were six characters with no space in them, and the remaining nine were genuinely wrong.
That distribution is the finding. A pattern expecting a space between outward and inward codes fails every record from a form that strips whitespace on submit, and the failure rate for that show works out at 128 over 4,120, or 3.1 per cent. Fix the pattern to accept an optional space and you recover 119 records. Fix the form and you stop producing them.
Two more things fall out of the same query. Group the failures by source system and by day, because a failure rate that jumps on one date usually means a release went out. Then compute the pass rate per country rather than for the file as a whole, because a single national figure hides everything: a file that is 80 per cent domestic can post a 98 per cent overall pass rate while failing half the records from your second largest market.
The pass rate per country is the metric to publish. Ninety-seven per cent for one country and 51 per cent for another are two different conversations, and the second one is usually a mapping problem rather than a typing problem.
One caution about reading those rates across editions. A pass rate can improve because the data got better or because the pattern got looser, and from the outside those look identical. Version the rule table, stamp each validation run with the version it used, and show the version beside the rate. Then a jump from 94 to 99 per cent between March and April is either a genuine improvement or an obvious consequence of the change somebody made on the first of April, and nobody spends an afternoon working out which.
Move the same check to the form where you can
Every failure you find in the warehouse is a record somebody has to touch twice. The same expression run at the point of capture turns most of them into a corrected keystroke.
The arithmetic favours this heavily. If a show takes 4,120 United Kingdom registrations and 3.1 per cent of them arrive with a malformed postcode, that is 128 records, and clearing them by hand at two minutes each is more than four hours of somebody's week. Running the identical pattern in the browser costs one round of testing and prevents almost all of them, because the person who can fix the value is standing right there with the correct one in their head.
Two design details decide whether this helps or annoys. Validate on blur rather than on every keystroke, so somebody halfway through typing a postcode is not being told they are wrong. Warn rather than block, for the same commercial reason that applies in the warehouse: a person who cannot complete the form does not become a clean record, they become no record.
Keep the browser pattern and the warehouse pattern in one place, generated from the same table. Two copies of the same regular expression drift within a year, and the drift is silent because both sides think they are enforcing the same rule.
Flag the failures and keep loading them
A postcode that fails a pattern is a flagged record, and the flag belongs in a column beside the raw value.
There are two reasons to resist rejection. The first is commercial: a registration blocked by a validation rule is a person who did not register, and no data quality target is worth that. The second is that your patterns are wrong more often than you think. Postcode systems get extended, new formats appear, and a country adds a range that your expression predates. Rejection makes the rule expensive to be wrong about. A flag makes it cheap.
Downstream consumers then choose. A mailing selection filters on the flag. An analysis of geographic spread ignores it and works from the country and the subdivision code that carries its own country prefix, which is K13's subject. A matching pipeline treats a flagged postcode as missing rather than as evidence, which is the correct handling and one that a rejected record could not have supported, because the record would not exist.
Where this stops
Pattern validation proves shape. It says nothing about existence and less than nothing about delivery.
SW1A 9ZZ has the correct form for a United Kingdom postcode and may correspond to no delivery point. 90210 is a valid five digit code and is the wrong one for an attendee in Ohio. A pattern check will pass both, cheerfully, and the only way past that is a reference file from the postal operator, which costs money and needs a licence, or an address verification service, which costs money per lookup. Both are worth it for a printed mailing to twenty thousand people and neither is worth it for a validation dashboard.
The honest framing for whoever reads your quality report is that the postcode pass rate measures how many records have a plausible postcode, which is a real and useful thing to know, and is a smaller claim than a deliverable address. Where the postcode feeds an identity decision inside the unified data record, a plausible but wrong postcode is a piece of false evidence, which argues for treating the flag as a downweighting signal rather than a binary.
This week, take your last edition's registration file, split it by country code, and compute the share of records whose postcode is empty or fails a simple shape test for that country. Sort the result by record count descending and look at the top five countries. If one of them fails at three per cent or more, open the form and check what it does to spaces.
Questions people ask about postal code validation
- Why validate postcodes when the country field is already checked?
- A country check confirms the record says United Kingdom. It says nothing about whether the postcode in that record has the shape a United Kingdom postcode has. Records with a valid country and a nonsense postcode pass every country level test and then fail at the mailing house, which is the point where the cost lands.
- Should a failed postcode block the registration?
- No. Load the record, set a validation flag and keep the raw value. Postcode ranges change, new formats appear, and a pattern written last year will reject something real. Blocking a registration to protect a data rule trades revenue for tidiness, and the flag gives you the same information without the trade.
- Does a passing pattern mean the postcode exists?
- No. A pattern check proves shape only. SW1A 9ZZ has the right form and may not be a delivery point. Confirming existence needs a reference file from the postal operator, and confirming that the address is real needs a delivery. Pattern validation is the cheap first filter, and it catches the large majority of typing errors.
Related reading
- Country field normalisation and the two letter codes that end the argument
- State and province codes are the quiet failure in geographic reporting