FLOWLOGIC
module /data-shaping-and-validation

unit U55 of 5

Canonical forms

one spelling per concept, normalised on the way in

The same concept arrives spelled five ways. A phone number comes as `+44 7700 900123`, `07700900123`, and `(0)7700 900-123`. A country is `GB`, `UK`, `United Kingdom` or `Great Britain`. An email arrives with capitals and a trailing space. If you store these as they arrive, deduplication fails, lookups miss, and the client ends up with four records for one person.

Normalisation is deciding the one canonical form and converting everything to it on the way in. Lowercase and trim emails. Store phone numbers in a single international format. Map country variants through an explicit lookup to ISO codes — an explicit table, not a clever guess, because guessing is how `GE` becomes Germany instead of Georgia. Do it once, at the boundary, right after validation, so every step downstream compares like with like.

Where it breaks: normalising at comparison time instead of at write time. A dedup check that lowercases both sides works, but the stored records stay inconsistent, so the next query written by someone else misses them again. Normalise once on the way in and the property holds for every consumer forever.

worked example

Canonical forms applied at the boundary, so every downstream step compares like with like.

const COUNTRY = {
  gb: 'GB', uk: 'GB', 'united kingdom': 'GB', 'great britain': 'GB',
  de: 'DE', germany: 'DE',
  ge: 'GE', georgia: 'GE',   // explicit, because guessing gets this one wrong
};

export const code = async (inputs) => {
  const r = inputs.record;
  const country = COUNTRY[String(r?.country ?? '').trim().toLowerCase()] ?? null;

  return {
    // one canonical form each, decided here and nowhere else
    email: String(r?.email ?? '').trim().toLowerCase(),
    phone: String(r?.phone ?? '').replace(/[^0-9+]/g, ''),
    country,
    // keep what arrived, so a bad mapping is diagnosable later
    rawCountry: r?.country ?? null,
  };
};

field checklist

common failure — Four records for one person

Leads arrived from three sources with emails in mixed case and trailing spaces. Dedup compared the stored strings directly, so `[email protected] `, `[email protected]` and `[email protected]` became three contacts, and a fourth appeared when a phone-only record could not be matched at all. Normalise to one canonical form at the boundary — lowercase, trimmed, single phone format — and the dedup property holds for every consumer without anyone remembering to lowercase at query time.

check your understanding

Your dedup query lowercases both sides at comparison time and works correctly. Why is normalising at write time still the better fix?

sandbox validation

The check above confirms you followed the unit. Marking the module COMPLETED takes more: build the automation in your own engine and submit the exported flow and its run evidence, signed, to your unique validation URL. See the module page for that spec.