Three map providers, one codebase
In short: the app was hard-wired to Google Maps. Now the map vendor is a plug-in: three providers behind one interface, switched with a single config entry — and every new feature works on all of them automatically.
Context
The same GIS platform as the rendering and event-delegation case studies. The product started on Google Maps — and then needed to run on Leaflet and Baidu too, without forking the app.
Problem
google.maps was hardcoded in every corner of the
application. Components created Google markers directly, features called
Google geometry helpers, services held Google map instances. Every
feature knew the vendor API — so a second provider wasn't a feature
request, it was a rewrite. And every Google version bump was a round of
shotgun surgery across the codebase.
The mechanism
1. Extract every provider touchpoint into a dedicated
map library — the rest of the app loses the right to import the vendor.
2. Wrap the provider objects in our own components —
markers, polylines, popups — so features talk to our API, never the
vendor's.
3. Put a strategy behind the components: each provider
is one adapter implementing the same contract. Adding Baidu meant
writing that adapter up front — the bulk of the work — and then the rest
propagated on its own; everything just snapped into place.
4. Switch providers with a single config entry.
5. Cover the adapter contract with tests — when a
provider ships a breaking change, a test goes red before any feature
does.
Before / after
| Aspect | Hardcoded google.maps | Adapter layer |
|---|---|---|
| Provider API references | google.maps calls hardcoded across the whole app — every feature knew the vendor API | zero provider references outside the map library |
| Adding a provider | effectively a rewrite — the vendor was welded into every feature | write one adapter up front (the bulk of the work) — then everything else falls into place on its own |
| Switching providers | not possible | one config entry — Google, Leaflet or Baidu at runtime |
| Provider breaking changes | surfaced at runtime, in random features | caught by tests at the adapter boundary — a version bump that breaks something fails a test, not production |
| Design / behavior changes | hunted down across every usage site | changed in one place — the shared components |
Why it pays off
Feature teams stopped caring who draws the map. New map features are written once against the shared components and work on all three providers for free. Vendor upgrades went from a risk to a routine — the adapter tests say exactly what broke and where. And when the design changes, it changes in one place instead of many.
A hardcoded vendor API isn't a dependency — it's a decision you're re-making in every file that touches it. Put it behind an adapter once, and "which map do we use" becomes config, not surgery.