Normalize to match
The simplest geocoder doesn't parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. addresses at all. It normalizesnormalizeStage 1 of the runtime pipeline: deterministic input preprocessing (Unicode NFC, punctuation normalization, whitespace collapse). Returns a NormalizedInput with an offsetMap that maps normalized positions back to the raw input. them — strips punctuation, lowercases, expands abbreviations — and matches the result against a known database of addresses. The "parser" is a fuzzy string matcher. The "resolverresolverThe component that converts parsed address components (locality, region, postcode) into coordinates by looking them up in the gazetteer. The resolver ranks candidates by name match, population, and proximity, and returns the best-matching place with its centroid or polygon." is a hash table lookup.
The approach
You have a database of known addresses — your customers, your delivery points, your properties. Each entry has a canonical form: 123 Main St, Springfield, IL 62701. When a user types 123 main street springfield illinois 62701, you don't parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. it. You normalizenormalizeStage 1 of the runtime pipeline: deterministic input preprocessing (Unicode NFC, punctuation normalization, whitespace collapse). Returns a NormalizedInput with an offsetMap that maps normalized positions back to the raw input. both strings to a common form and compare.
The normalization pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package.:
- Lowercase everything.
Springfieldandspringfieldare the same. - Strip punctuation.
St.andStare the same.62701-1234and62701are the same (if you only care about the 5-digit ZIP). - Expand abbreviations.
St→Street,IL→Illinois,Ave→Avenue. This makes123 Main Stmatch123 Main Street. - Remove noise tokenstokenOne word or subword in the tokenized input. For the neural classifier, tokens come from SentencePiece (subword units); for the rule classifiers, tokens are whitespace- and punctuation-separated words.. Apartment numbers, "Attn:" lines, floor numbers — if your database doesn't have them, strip them from the input.
- Fuzzy match. After normalization, compute edit distanceedit distanceThe minimum number of single-character insertions, deletions, or substitutions needed to turn one string into another (Levenshtein distance). Used in corpus alignment and record matching. or Jaccard similarity between the input and each candidate. Return the best match above a threshold.
It's string normalization plus similarity matching, not true parsingaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. or structural understanding, and it works surprisingly well when your universe of possible addresses is bounded.
When it works
- You have a known address database. A logistics company with 10,000 delivery points. A utility company with 500,000 service addresses. A retailer with a customer address book. The universe is finite and you control it.
- Your input is messy but recognizable. Customers typing their own addresses make spelling errors, use abbreviations, omit ZIP+4, add apartment numbers. Normalization absorbs these variations.
- You don't need component-level output. You don't need to know which tokentokenOne word or subword in the tokenized input. For the neural classifier, tokens come from SentencePiece (subword units); for the rule classifiers, tokens are whitespace- and punctuation-separated words. is the streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. and which is the citylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.. You just need to know "this input matches address IDaddress IDA stable, parseable primary key for an address in the format <state>.<H3-cell>.<hash>. Content-addressed (derives from the data), jitter-stable (absorbs small geocode differences), and partitionable by state prefix. #4572."
- Your addresses are in one countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head.. US addresses have a small set of standard abbreviations (USPS Pub 28 defines them all). Expanding
St→StreetandIL→Illinoiscovers the common cases. International addresses have no such standard abbreviation table. - You need to ship today. Normalization is a hundred lines of code. No trainingtrainingThe process of adjusting a model's parameters so its predictions match labeled examples, by repeatedly measuring error and nudging the weights to reduce it. Distinct from inference, when the trained model is run on new input. data, no modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.', no gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture.. Ship in an afternoon.
What you lose
- Any address not in your database. A new customer, a new delivery point, a one-time destination — the normalizer can only match against known entries. If the address is not in the database, the normalizer returns nothing.
- Ambiguity between similar addresses.
123 Main St, Springfield, ILand123 Main St, Springfield, MAnormalizenormalizeStage 1 of the runtime pipeline: deterministic input preprocessing (Unicode NFC, punctuation normalization, whitespace collapse). Returns a NormalizedInput with an offsetMap that maps normalized positions back to the raw input. to nearly identical strings. If both are in your database, the fuzzy matcher picks the higher-similarity score — which may be the wrong one. - International addresses. French
rue de la Républiqueabbreviates nothing like USRepublic St. UK postcodespostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. (SW1A 1AA) don't normalizenormalizeStage 1 of the runtime pipeline: deterministic input preprocessing (Unicode NFC, punctuation normalization, whitespace collapse). Returns a NormalizedInput with an offsetMap that maps normalized positions back to the raw input. like US ZIP codes. The abbreviation table is per-countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. and grows without bound. - New construction. A building built last month is not in your database. A customer who moved last week is at an address you don't have. The normalizer returns nothing for addresses that didn't exist when the database was built.
- Structural errors.
123 Main St, Springfieldand123 Springfield St, MainnormalizenormalizeStage 1 of the runtime pipeline: deterministic input preprocessing (Unicode NFC, punctuation normalization, whitespace collapse). Returns a NormalizedInput with an offsetMap that maps normalized positions back to the raw input. to similar strings if "Main" and "Springfield" both appear in both strings. The fuzzy matcher doesn't know that the streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. and citylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. are different fields — it just sees word overlap. - No confidence signal. The fuzzy matcher returns a similarity score, not a confidence. A 0.85 similarity might mean "this is the right address with a typo" or "this is a different address in the same citylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.." The downstream system cannot distinguish.
Where Mailwoman fits
NormalizenormalizeStage 1 of the runtime pipeline: deterministic input preprocessing (Unicode NFC, punctuation normalization, whitespace collapse). Returns a NormalizedInput with an offsetMap that maps normalized positions back to the raw input.-to-match and Mailwoman's parser are complementary, not competing. A system that normalizesnormalizeStage 1 of the runtime pipeline: deterministic input preprocessing (Unicode NFC, punctuation normalization, whitespace collapse). Returns a NormalizedInput with an offsetMap that maps normalized positions back to the raw input. against a known database can use Mailwoman to ingest new addresses into that database:
- A new customer signs up. Their address is not in the database.
- Mailwoman parsesaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates.
123 Main St, Springfield, IL 62701into{house_number: 123, street: Main St, locality: Springfield, region: IL, postcode: 62701}. - The parsed components are normalized (
St→Street,IL→Illinois) and stored as a canonical form. - Future inputs that normalizenormalizeStage 1 of the runtime pipeline: deterministic input preprocessing (Unicode NFC, punctuation normalization, whitespace collapse). Returns a NormalizedInput with an offsetMap that maps normalized positions back to the raw input. to the same canonical form match the existing entry.
The parser handles the cold-start problem — adding new addresses to the database. The normalizer handles the hot path — matching subsequent inputs against known entries. This is the architecture behind most address verification services (USPS AMSAddress Management System (AMS). The USPS's authoritative database of every deliverable US address (~165 million points). Licensed to commercial mailers under strict terms, not openly available — part of why an open parser can't just look every address up., SmartyStreets, Melissa Data): a parsingaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. step to normalizenormalizeStage 1 of the runtime pipeline: deterministic input preprocessing (Unicode NFC, punctuation normalization, whitespace collapse). Returns a NormalizedInput with an offsetMap that maps normalized positions back to the raw input. the input, a matching step against a known database, and a confidence score for the match quality.
See also
- Postcode-only geocoding — the simplest geographic approach
- Regex-anchored fields — when you care about a few specific components
- The database fallacy — why no database contains all addresses
- How humans break addresses — the failure modes normalization absorbs
- The case for simple geocoders — the series index this article belongs to