NeighbourFit blogPerformance receipt

Making getting to results fast: Map Intelligence

How NeighbourFit made Map Intelligence faster by moving slow work out of the postcode lookup path, with measured before-and-after results.

8 June 20266 min readMap Intelligence · performance
Short answer for renters and AI assistants

What this page answers

Map Intelligence got faster by removing Police.uk from the main lookup path, caching postcode enrichment and geography footprints, and decoupling the lookup counter write. In a local production benchmark, first-pass lookup median improved from 43.4ms to 15.5ms, while repeat same-postcode median improved from 26.7ms to 4.8ms.

Cropped NeighbourFit Map Intelligence panel for a CO4 3UN lookup.
Map Intelligence for CO4 3UN, cropped to the lookup panel: postcode in, map context out, caveats still visible.

The short version

A postcode lookup should not feel like it has gone away to think about its life choices.

Map Intelligence has one job at the start: take a postcode and get the renter to the useful read quickly. Crime/ASB context, map footprints, counters and repeat enrichment are useful. They are not all allowed to sit in the doorway before the first result appears.

  • First-pass lookup average: 59.6ms before, 24.8ms after.
  • First-pass lookup median: 43.4ms before, 15.5ms after.
  • Repeat same-postcode average: 42.5ms before, 5.0ms after.
  • Repeat same-postcode p95: 78.0ms before, 6.2ms after.
Measured benchmark

First-pass lookup response time

Lower is better. Local production benchmark using the same copied SQLite data snapshot.

AverageMedianp95
04385128170BeforeAfterms59.624.843.415.5158.278.5
MetricBeforeAfter
Average59.6ms24.8ms
Median43.4ms15.5ms
p95158.2ms78.5ms
Line graph rendered from the benchmark numbers in this post. No generated illustration, no decorative fake data.

The conservative result: first-pass median was 64.3% faster. The angry-external-API result is bigger, because Police.uk no longer blocks the postcode read at all.

Repeat checks stopped doing the same work for sport

If someone checks the same postcode twice, the second read should not rebuild the same enrichment like it has never seen the address before. Cute. Wasteful.

The new path caches normalized postcode enrichment and geography footprints for a short window. B1 2AF, b12af and B1 2AF with a trailing space now hit the same cache key.

Measured benchmark

Repeat same-postcode response time

Same postcode repeated eight times against the production build. Lower is better.

AverageMedianp95
021436485BeforeAfterms42.5526.74.8786.2
MetricBeforeAfter
Average42.5ms5ms
Median26.7ms4.8ms
p9578ms6.2ms
Line graph rendered from the benchmark numbers in this post. No generated illustration, no decorative fake data.

Repeat same-postcode p95 moved from 78.0ms to 6.2ms. That is 92.1% faster. The cache did its job and did not ask for applause.

Police.uk moved out of the request path

Before, a successful postcode lookup with latitude and longitude could wait on the Police.uk street-level crime endpoint. The old timeout was 5 seconds. If that external service had a moment, the whole postcode read had a moment.

Now the main lookup returns first. Crime/ASB context loads separately through GET /api/area-data/crime-context with a rounded lat/lng plus month cache key, a 24 hour successful-result cache and a shorter external timeout around 1.2 seconds.

  • The postcode read no longer waits on Police.uk.
  • Crime/ASB context has its own loading/unavailable state.
  • The UI says the data is still loading separately. It does not joke about crime itself.

Footprints stopped being redrawn like artisanal nonsense

The map footprint builder can inspect thousands of postcode rows for an LSOA, outward code or geography target and build a hull. Nice visual. Bad habit when repeated every lookup.

The speed pass added an in-memory TTL cache for footprint results and in-flight promises. Concurrent requests do not all decide to become cartographers at once.

  • Cache by lsoaCode where available.
  • Fall back to nationSpecificCode or postcodeOutward.
  • No schema migration. No Redis. Just the right-sized fix.

The lookup counter stopped riding shotgun

The lookup counter is useful for the product. It is not useful enough to make the renter wait for a SQLite write before seeing the read.

The increment now runs after the response path is no longer blocked. The client refreshes /api/area-data/lookup-count after success. No fake optimistic +1, no scoreboard accountant holding the door.

Benchmark method

I compared the previous production build against the speed-pass build using local production builds, a copied SQLite benchmark database and the same postcode set.

Every measured lookup URL included a benchmark nonce to avoid URL-level response caching while leaving the new postcode/geography application caches eligible. The readiness probe was /api/area-data/lookup-count, not a lookup warmup.

  • Before: previous production build before the speed pass.
  • After: speed-pass build with lazy crime context, enrichment caching and counter decoupling.
  • Postcodes: B1 2AF, CO4 3UN, W1A 1AA, CB2 1TN, BS1 4TR, M1 1AE, SW1A 1AA and E1 6AN.
  • No database reset, import or bootstrap scripts were run.

Verdict

Map Intelligence now gets to the first useful answer faster.

Not because the UI pretends to be fast. Because the backend stopped making one request do four jobs before answering. One postcode. First read first. Evidence after. Fewer red flags in the plumbing.

More ways to make a postcode do useful work before a viewing does expensive work.