A Plus Sign Is Not a Country Code
The same organisation signing up in a browser stored one thing and signing up on a phone stored another, because one path composed a dial code and the other validated nothing. One normaliser now serves all three signup routes — and it makes the shape uniform without inventing the country nobody typed.
Two records, one organisation, and no way to tell they are the same number.
A phone field is the most deceptively simple input on any form. It looks like a string. It is a string. And the same number can be written a dozen ways that all look correct to the person typing and are all different to the database.
Signup here had two doors. The browser form composed a country dial code with a local number and stripped everything else, so it stored a plus and digits. The mobile app posted to an endpoint that validated the field as a string of up to two hundred and fifty-five characters and nothing more.
The same organisation, two doors
Nothing later in the system could answer it. The number is complete, correct and locally unambiguous, and it is missing the one piece of information that makes it usable by anything that is not standing in the same country as the person who typed it.
One normaliser, three doors
All three signup routes — the browser form, the mobile registration endpoint and social signup — now call the same normaliser and share the same format rule. The rule was not redesigned: it is what the web already did, moved somewhere both other paths can reach.
That is the whole fix, and it is worth noting how unglamorous it is. Nothing became cleverer. One behaviour stopped being three.
What it does not do
Here is the part worth being precise about, because the word normalise invites an assumption the code does not support.
The rule keeps the digits, drops the spaces and brackets and dashes, and puts a plus on the front. Applied to a number that already carried its country, that produces the internationally-correct form. Applied to a national number, it produces a national number with a plus in front of it.
A leading plus means the country code follows. Putting one in front of a national number makes the shape uniform and the meaning no truer.
So the divergence between the two doors is closed — every signup now stores the same shape — and the older question is not answered by that. A number typed without its country is still a number without its country, now wearing a plus.
This is a real limit rather than a pedantic one. Anything that has to dial, message or match that number from outside the country needs the code, and the plus in front of it is a promise the value does not keep.
Scope of this article
Everything above is about the signup path — the number attached to a workspace when an organisation is created. Phone numbers on customers, suppliers and employee records are separate fields on separate forms and are not covered by any claim here.
Two small decisions worth stealing
Both concern what a normaliser should do with input that is not a number, and both are the kind of thing that produces a confusing error message when they are got wrong.
An untouched field posts an empty string, and an empty string is not null. Leave it as it arrived and an optional field fails its format check, because the check runs against the empty string rather than being skipped. So an empty value becomes null right there, in the normaliser, rather than relying on a framework setting somewhere else that a later change could quietly undo.
Input with no digits at all is handed back untouched. Somebody typing a word into a phone field should see their own words in the error message, not a stray plus sign or a blank field. The normaliser declines to salvage what is not a number and lets the format rule reject it honestly.
And a detail about ordering that follows from those: the normaliser runs before validation, not after. That is why it has to tolerate a value that is not a string at all, and why the format rule is a backstop rather than the thing doing the work.
From one shape to one meaning
A uniform shape across three doors is the foundation. Making every stored number internationally complete is the next thing, and it is a different piece of work.
A country behind every number
Composing a dial code with a local number the way the browser form already does, on every path that accepts one, so a national number cannot be stored bare.
Validation against the country plan
Checking the digits are a possible number for the country claimed, rather than checking only that there are enough of them.
One normaliser for every phone field
Extending the same treatment past signup to the phone columns on customers, suppliers and employees, so matching a number works across the product.
We publish scope, not dates.
Scope phone handlingThree questions for any form that takes a phone number
Does every entry point store the same shape?
A good answer sounds like
Yes, one normaliser.
What ours actually is
Yes for signup, across all three routes. Ask it per field rather than per product — the divergence described here existed between two doors onto the same table.
Is a number stored with its country code?
A good answer sounds like
Always, and it is composed rather than typed.
What ours actually is
Only when the person supplied one. A national number is stored with a plus and no country, which is a shape rather than a meaning.
What happens to an empty optional field?
A good answer sounds like
It becomes null before validation.
What ours actually is
Null, handled in the normaliser rather than relying on a framework default elsewhere. Small, and the cause of a great many confusing validation errors when it is missed.
Our take
Two doors onto one table will drift, and the drift is silent because both sides look correct in isolation. Consolidating on one normaliser was the right fix and it is finished. What it did not do — and what we would rather say than let the word normalise imply — is invent the country nobody typed. A plus in front of a national number makes every stored value the same shape, which is genuinely useful for comparison and genuinely useless for dialling. If a number of yours has to be reachable from outside its own country, compose the dial code at the point of entry, because no amount of tidying afterwards can recover information that was never supplied.
What AWRA OpsHub does today
- One normaliser and one shared format rule across all three signup routes — the browser form, the mobile registration endpoint and social signup — so the paths cannot drift apart again.
- Spaces, brackets and dashes removed, digits kept and a leading plus applied, so every signup stores one shape.
- An empty optional field turned into null inside the normaliser, rather than depending on a framework setting elsewhere that a later change could undo.
- Input carrying no digits handed back exactly as typed, so the format rule rejects it and the person sees their own words in the error.
- Normalisation running before validation, tolerating a value that is not a string, so the rule never runs against something the normaliser has not seen.
- A length limit of thirty characters on the shared rule, in place of the two-hundred-and-fifty-five-character string the mobile endpoint previously accepted.
More we can add to your workspace
- A country code composed onto every number at entry, on every path, the way the browser signup form already does — the item that turns a uniform shape into a uniform meaning.
- Validation against a country numbering plan, checking that the digits are a possible number for the country claimed rather than only that there are enough of them.
- The same normaliser on the phone fields of customers, suppliers and employees, so matching a number works across the product rather than at signup alone.
- A stored country alongside the number, so a nationally-formatted value keeps the context it was entered in even when the digits do not carry it.
- Duplicate detection across differently-written numbers, which becomes straightforward once every number is stored complete and is guesswork until then.
Where we point you to a specialist
- We would decline to infer a country code from the workspace address, the browser locale or the device. A wrong country on a phone number is worse than an incomplete one, because an incomplete number announces itself the first time somebody tries to use it and a confidently wrong one is simply dialled.
- Where a number must be verified as reachable rather than merely well-formed, that is a delivery question rather than a validation one, and we would rather send a code and confirm than let a format check imply a number works.
Composing the dial code at every point of entry is the single item that turns this from consistent storage into usable data, and the browser form already demonstrates the pattern. If your numbers have to be reachable from outside one country, it is the one to scope.
Check what your own signup stored
Look at the phone number on your workspace. If it begins with a plus followed by your country code, it is complete. If it begins with a plus followed by a zero, it is a national number in international clothing — worth correcting now rather than the day somebody outside the country needs to ring it.
Ask about your recordFrequently asked questions
What actually changed?
The mobile registration endpoint validated the phone field as a string of up to two hundred and fifty-five characters with no format rule, while the browser form composed a dial code and stripped everything else. The same organisation therefore stored different things depending on which door it came through. All three signup routes now call one normaliser and share one format rule, and the rule itself is unchanged from what the browser already did.
Does normalising add my country code?
No, and this is the point most worth understanding. The rule keeps the digits, removes the punctuation and puts a plus on the front. A number typed with its country code comes out internationally correct. A number typed nationally comes out as a national number with a plus in front of it — the same shape as every other stored value, and still missing the country. Composing the code at the point of entry is the only thing that fixes that, and it is on the list of work we can add.
Why does an empty field become null rather than staying empty?
Because an empty string is not null, and an optional field left untouched posts an empty string. Left alone, the format check runs against that empty string and fails, so an optional field behaves as though it were required. Converting it in the normaliser rather than relying on a framework setting elsewhere means the behaviour travels with the rule instead of depending on configuration a later change could quietly alter.
What happens if somebody types words into the phone field?
The value is handed back exactly as typed and the format rule rejects it. That is deliberate: a normaliser that stripped it to a bare plus sign or an empty field would show the person an error about something they cannot see, and the most useful thing an error message can contain is what they actually entered.
Does this cover phone numbers on customers and suppliers?
No. Everything here concerns the number attached to a workspace at signup. Customers, suppliers and employees carry their own phone fields on their own forms, and extending the same treatment to them appears on the list of work we can add rather than being claimed as done.
Could you guess the country from where the person is?
We could, and we have decided not to. An inferred country code that is wrong is considerably worse than one that is absent, because an absent one announces itself the first time somebody tries to call and a wrong one simply gets dialled. Somebody signing up while travelling, on a company connection routed through another country, or using a number from a country they no longer live in would all be recorded confidently and incorrectly.