AngoraTek FR
← Back to Blog
Methods 6 min read

The One-Line Map Switch Is the Easy Half

HERE implements all ten of the spatial domains our Rust client models, from geocoding to tour planning. Radar implements four. Both run behind one trait in our code, and the gap between them is the most honest thing in this post.

The usual advice runs one of two ways: commit to a single vendor and stop worrying, or wrap every call in an abstraction layer and sleep better. Both are half right, which is another way of saying both are wrong about the important half. Our position, sharpened by maintaining 35 provider implementations across HERE, Google, TomTom, Mapbox, and Radar: abstract the shape of the call, and never pretend you have abstracted the substance of the data. The trait buys you benchmarking and a fallback in day-to-day work, plus a way out you hope never to use. It cannot buy you a provider's traffic model, and claiming otherwise is the lock-in that hurts later.

The tax arrives even if you never switch providers

Lock-in to a single geospatial API is quiet because the invoice has one line item. You pay it four ways. Pricing changes land on the vendor's calendar, not yours, and geospatial APIs bill per call at volumes that grow with your users whether your revenue does or not. Deprecations arrive with notice periods a migration can outlast your patience for. Coverage shifts: a region that matched beautifully can get silently worse after a data refresh, and your support inbox notices before your SLA does. And there is the exit itself, the rewrite you may pay for two years from now, already priced into this year's contract because nobody built the seam.

Ground all of that and it looks like this: the dispatcher whose map is suddenly wrong for one county after an otherwise routine vendor update, while your code, which never changed, absorbs the blame.

There is also a tax inside the contract. A renewal negotiation is won with evidence, and the only evidence a single-provider shop can table is a threat nobody believes. The price you pay next year is partly a function of how credibly you can leave this year.

Provider asymmetry is real, and the narrowest provider sets your floor

An abstraction layer is not free, and the cost has a name. A common interface is always a little smaller than any one provider, and the floor is set by the narrowest vendor you support. Of the ten domains everymap-core models, HERE answers all ten, TomTom eight, Mapbox seven, Google six, Radar four. A trait cannot give Radar the other six. Anyone who claims full equivalence is either lying or narrowing the interface down to Radar, and the second one quietly costs the other four providers their depth.

So we made the failure loud instead of elegant. Ask a provider for a capability it lacks and the unsupported_* macros in everymap raise an error that says google does not support traffic, in those words, before production cares. The lazy design returns an empty answer and converts a provider limitation into a data-quality investigation next quarter. An empty result and a bad dataset are indistinguishable in your dashboards, and that is precisely the trade the error refuses to make.

One thing we will not pretend: our interface, like every common interface, is smaller than the sum of its parts. What gets dropped is documented in the repo rather than buried in this paragraph.

What the trait buys you: benchmarking first, escape second

The payoff for accepting a smaller interface is that all five vendors become testable from one seat. This is the whole selling point of the library, on the command line or in code:

everymap --provider google --api-key $GOOGLE_KEY geocode "Brandenburg Gate, Berlin"

Change google to tomtom and the same question goes to a different provider, same input, different evidence. That is the one-line switch. What it actually buys happens in three places.

Benchmarking comes first, and it is the underrated one. An everymap-bench harness, in development when this was written, runs the same routing or geocoding question against several providers and prints a comparison table. You can walk into a renewal with measured accuracy and latency for the calls you actually make, instead of marketing decks. Benchmarking is also how you find out that the provider with the famous name answers your specific region's addresses worse than the cheap one, which is a conversation worth having before the contract, not after.

Fallback comes second: when your primary geocoder rate-limits on a traffic spike, a second provider behind the same trait is a degraded answer instead of a blank page. Exit comes last and matters most. The migration you may never run is cheap to keep possible, and a cheap exit changes the conversations you have about pricing and deprecation. That is the tax from the first section being paid back.

What the trait can never carry: the part you actually chose the provider for

A switched call has the same syntax and different substance. HERE's traffic model, its data freshness, its particular way of guessing that a construction zone will clear by Thursday: none of that survives the trait. Run the same route across two providers for the same Tuesday at 5pm and you will get two honest answers that disagree, each correct within its own picture of the road.

This is where abstraction turns into superstition. If your product's quality is that provider's quality, if your drivers reroute minutes faster because of one vendor's traffic feed, then the trait is flattering you. The behavior your users experience has vendor assumptions baked into product logic: how ETAs display, when reordering triggers. Changing the provider then costs a product audit, not a code audit. Believing otherwise is the deeper lock-in, because you can leave the vendor at any time and still be written against its worldview.

When to abstract, and when to stand absolutely still

Abstract when the capability is commodity-shaped and your negotiating position depends on evidence. Geocoding and isochrones are the classic cases: interchangeable enough to benchmark, expensive enough at volume that the benchmark pays for itself. Young projects belong here too, since the second provider costs you a flag today and a negotiation later.

Stand still when your product's value is one provider's distinctive strength. Buy the traffic feed for what it is, a dependency with a moat around it, and spend the abstraction budget somewhere honest.

And never abstract by mirroring a single vendor's full surface. A client that reproduces every parameter of one API is that API translated, and translation is just a rewrite you scheduled for later with extra steps.

Our own honest accounting: the 35 implementations are young code, and the 654-test count is the README's tally rather than something we have audited under adversarial conditions. A common interface is worth its floor, no more, and the floor for you is whichever providers you actually support.

The decision in one line: pay the trait's premium while the calls still look alike, keep the traffic model where it lives, and let the benchmark tell you when the premium stops being worth it.

AngoraTek maintains everymap-rs, an MIT-licensed Rust client for five geospatial providers. Talk to us if you want the benchmark before the renewal.

Thanks for reading this far. If this sounds like the problem you're wrestling with, tell us about it. A principal reads every message.

Start a conversation