ZIP, Postcode, PLZ: Postal Code Systems Explained for Developers
If your address form ships with a five-digit postal code field and a regex copied from a US tutorial, it is already broken for most of the world. The United Kingdom mixes letters and digits, Japan wants seven digits with a hyphen, the Netherlands ends its codes with two letters, and Hong Kong has no postal codes at all. Every one of these differences is a bug waiting to be filed.
This guide walks through how the major postal code systems are structured, where naive validation goes wrong, and how to design forms and databases that survive international input. It pairs naturally with generated multi-country test data: the fastest way to learn whether your checkout accepts EC1A 1BB is to paste one in and watch.
What a postal code actually encodes
A postal code is a routing instruction, not a location. Postal operators invented these systems to sort mail mechanically: the code tells a sorting machine which regional hub and which local delivery office should handle an envelope. Any correlation with geography is a side effect, not a promise.
This matters because developers routinely treat postal codes as something they are not: a precise coordinate, a stable administrative boundary, or a universal required field. Codes are reassigned when routes change, one code can span several towns, and a large office building can have a code entirely to itself.
Numeric systems: ZIP, PLZ, and their relatives
The US ZIP code is five digits: the first marks a broad region (0 in the Northeast, 9 on the West Coast) and the rest narrow to a processing facility and delivery area. The optional ZIP+4 extension appends a hyphen and four digits to reach a block face or large building, so validation must accept both 62704 and 62704-1234. Germany’s Postleitzahl (PLZ) is also five digits, redrawn nationwide in 1993 after reunification, with the leading digit identifying one of ten postal zones. France also uses five digits, with a helpful twist: the first two are the département number, so 75001 is in Paris and 33000 in Bordeaux.
Elsewhere the digit counts diverge. China uses six digits that narrow from province level down to the delivery post office. Japan uses seven digits written NNN-NNNN, as in 100-0001; the hyphen is customary even though the code itself is just seven digits. Australia gets by with four digits, which surprises validators that assume a five-digit minimum. None of these use letters, yet they still break simplistic code: a five-character cap truncates Japanese codes, and a digits-only filter strips the hyphen users naturally type.
Alphanumeric systems: UK, Canada, and the Netherlands
The UK postcode is the classic stress test. It splits into an outward code (area plus district, such as EC1A) and an inward code (sector plus unit, such as 1BB), separated by a single space: EC1A 1BB. Outward codes vary from two to four characters, so full postcodes run six to eight characters including the space. A9 9AA, A99 9AA, AA9 9AA, and AA9A 9AA all occur, which is why hand-rolled UK regexes are a cottage industry of subtle mistakes.
Canada uses the format A9A 9A9, alternating letter-digit-letter, digit-letter-digit; the first three characters are the Forward Sortation Area (FSA), the last three the Local Delivery Unit (LDU). The letters D, F, I, O, Q, and U never appear in a Canadian postcode because they are easily confused in handwriting and OCR — a detail strict validators must encode and generated test data should respect. The Netherlands uses NNNN AA, as in 1012 AB; postcode plus house number identifies a Dutch address almost uniquely, which is why Dutch checkouts ask for those two fields first and auto-fill the street.
All three invite the same normalization bugs: users type lowercase, omit the space, or add extra ones, so uppercase the input, collapse whitespace, and reinsert the canonical space rather than rejecting "ec1a1bb" outright.
Countries with no postal codes at all
Some territories do not use postal codes at all. Hong Kong has none; mail is routed by district and street. The United Arab Emirates historically relied on PO boxes, and its Makani system is a building identifier, not a postal code in the classic sense. Ireland had none until Eircode launched in 2015, and plenty of Irish users still do not know their Eircode by heart.
For form design the implication is blunt: a required postal code field with no way to skip it locks these users out of your product. The fix is to make the requirement conditional on the selected country and to let your schema store an empty or null code without treating the record as invalid. If your test fixtures only contain US addresses, this path never runs — which is exactly how "postal code is required" bugs reach production.
Validation and storage guidance
First, store postal codes as strings, never integers. New Jersey’s 07030 becomes 7030 the moment it passes through an integer column, and the corruption is silent: the value still looks plausible and fails only when a carrier API rejects it. The same applies to spreadsheets and CSV pipelines that helpfully coerce numeric-looking columns.
Second, do not apply one country’s regex globally. A pattern for five US digits rejects every UK, Canadian, Dutch, and Japanese code. The practical approach is per-country validation: a lookup table of patterns keyed by country code, applied after the user picks a country, with a permissive fallback of letters, digits, spaces, and hyphens for everywhere else. Normalize before validating — trim, uppercase, collapse internal whitespace — and store the normalized form so comparisons and deduplication behave predictably. Finally, resist being stricter than the postal operator: rejecting a valid code loses a customer; accepting a slightly off one merely costs a correction later.
Geolocation caveats and testing with generated data
A postal code is an area, not a point. A geocoding service given a code returns the centroid of that delivery area — which may sit in a lake or another suburb entirely. US ZIP codes are especially treacherous: they are route collections rather than closed polygons, and the Census Bureau’s ZCTA shapes are only approximations. Postal-code centroids feeding distance calculations, tax lookups, or "stores near you" are usually close and occasionally very wrong — document that tolerance instead of discovering it in support tickets.
This is where multi-country generated test data earns its keep. A batch of addresses from the US, UK, Germany, Japan, Canada, the Netherlands, and Hong Kong exercises every path above in one pass: leading zeros surviving storage, alphanumeric codes passing validators, a seven-digit hyphenated code fitting your column width, and an empty code flowing through a form that must not demand one. Because the data is synthetic, you can generate hundreds of records for automated tests and share failing cases freely — no real person’s address ever lands in your bug tracker.
Frequently Asked Questions
Should I validate postal codes with one universal regex? +
No. There is no single pattern that accepts all valid codes without also accepting garbage. Validate per country after the user selects one, and use a permissive fallback for countries you have no specific rule for. Rejecting valid input is worse than accepting an occasional malformed code.
Why must postal codes be stored as strings? +
Because many codes carry leading zeros (02134, 07030) that integer types silently drop, and many systems include letters, spaces, or hyphens that cannot be represented numerically at all. A string column with normalization on write is the only representation that works for every country.
Can I turn a postal code into exact coordinates? +
Only approximately. A code identifies a delivery area or route, so geocoding returns a centroid that can be far from any specific address. That is fine for rough distance sorting or regional analytics, but not for navigation, precise tax boundaries, or anything legally sensitive.
How do I handle countries without postal codes in a required form field? +
Make the requirement conditional on the selected country. For Hong Kong, the UAE, and similar territories, hide the field or accept it empty, and let your schema store a null. Test this path explicitly with generated addresses from a codeless territory before release.