MonkeyLoader All articles
Performance

Only Load the Rules That Apply: A Postcode-Driven Config Loader for UK Services

MonkeyLoader
Only Load the Rules That Apply: A Postcode-Driven Config Loader for UK Services

Photo: Basile Morin, CC BY 4.0, via Wikimedia Commons

Every UK service ends up with a file called something like region-rules.js. It starts with three conditionals for Scotland and Wales. Two years later it is 400 lines, half of it dead, and the content team cannot change a single sentence without a release.

Splitting those rules out is not a framework rewrite. It is a lookup, a fetch and a cache — plus a bit of care around the border cases that make UK geography entertaining.

Why the rules should not ship with the app

Bundled rules have two problems. They make your main bundle heavier for people in Cornwall who will never see the Scottish rules, and they tie content changes to deploys. A recycling calendar change in April should not require a pull request from a developer who is on leave.

Move the rules into versioned JSON on a CDN, keep a small default in the app for the unhappy path, and both problems mostly disappear.

Decide what 'region' actually means first

This is where most implementations go wrong. Nation (England, Scotland, Wales, Northern Ireland) is not the same thing as postcode area. The CH area covers Chester in England and parts of Flintshire in Wales. LL stretches across the border near Wrexham and Oswestry. SY and NP do the same trick further south.

So pick your grain deliberately:

Write that decision down. It will save an argument in eighteen months.

The loader in three steps

Resolve, fetch, cache. Roughly this:

const BASE = 'https://cdn.example.co.uk/region-config/v3';
const memo = new Map();

export async function loadRegionRules(postcode) {
  const key = normalise(postcode);          // 'll11 2ab' -> 'LL112AB'
  if (!key) return FALLBACK;
  if (memo.has(key)) return memo.get(key);

  const area = await resolveArea(key);      // 'W06000006' or similar
  const rules = await fetch(`${BASE}/${area}.json`, { cache: 'force-cache' })
    .then(r => (r.ok ? r.json() : FALLBACK))
    .catch(() => FALLBACK);

  memo.set(key, rules);
  return rules;
}

The obvious optimisation is to key the memo on the outward code where only coarse rules are needed, so LL11 2AB and LL13 9XX share a fetch. Keep a separate, finer cache for the legal stuff.

Choosing a lookup API

A quick and deliberately unscientific comparison of the usual options:

Option Shape Good for Watch out for
postcodes.io Free, open data, bulk endpoints Resolving postcodes to administrative areas Community-run, no SLA, rate limits on public instances
Ideal Postcodes Paid, per-lookup pricing Address search plus rooftop coordinates Costs scale with volume; check current terms
getAddress Paid, autocomplete-first Forms where typing speed matters Less useful if you only need area codes
OS Places API Licensed, authoritative Addresses plus UPRN, all four nations Licensing and pricing need reading properly
Royal Mail PAF Licensed dataset The reference source Display and storage restrictions; not a casual dependency

Check pricing and licensing yourself before committing — these things shift. For area resolution alone, a free open dataset is usually enough. For address capture, pay for it.

Cache at the edge like you mean it

Version the folder. /region-config/v3/ changes only when the config schema changes, and individual files inside it are immutable. Set a long max-age, add stale-while-revalidate, and let the CDN do the heavy lifting. Regenerating a config means writing a new file and bumping an index, not purging a cache blindly at 4pm on a Friday.

Keep config files small — a few kilobytes each. If one grows past 20KB, it is probably holding data that belongs in a database query, not a static file.

Fail safe, not fast

Never block rendering on the postcode lookup. Show the generic version of the page, mark anything region-specific as unconfirmed, and retry once in the background. A user in Dundee seeing an English default with a small note is a much better outcome than a blank form and a support ticket.

Log the failure with the outward code only, and alert if the failure rate crosses a threshold. Otherwise you will discover the API outage from a tweet.

Border cases worth a test card

Build those into an automated test and you will catch most of the silly bugs before a user in Stornoway does.

Measure what you added

Track the config fetch latency at p95, the cache hit ratio at the CDN, and the bytes per region bundle. Prefetch on blur of the postcode field so the fetch overlaps with typing the rest of the form. If the numbers look bad, the answer is usually coarser rules or a warmer cache, not a bigger framework.

All Articles

Related Articles

Peak Season With Two Pairs of Hands: A Loader Checklist for Small UK Retail Teams

Peak Season With Two Pairs of Hands: A Loader Checklist for Small UK Retail Teams

Cron, Clocks and the Missing Hour: Scheduling Loaders for Europe/London

Cron, Clocks and the Missing Hour: Scheduling Loaders for Europe/London

The Two-Second Illusion: Making Council Pages Feel Quick on a Wobbly Connection

The Two-Second Illusion: Making Council Pages Feel Quick on a Wobbly Connection