Integrating sixty systems you don't control
Some expose an API, some only a database, each with its own idea of reality — mapped onto one model. This is a case study about restraint: the abstraction I refused to build until the real cases forced my hand.
Our platform schedules appointments, but it doesn't own the source of truth — the clinics do, through the practice-management systems they already run. To be useful, the platform has to stay in sync with roughly sixty of those external systems and make them all look the same to the rest of the product.
These systems agree on almost nothing. Some expose a clean API; some only a database you read directly; many run on-premise, reached across a network. And the moment we grew past dental into optometry, chiropractic, and orthodontics, each new vertical broke an assumption the dental-shaped core had quietly baked in.
Protect the interior.
Let the exterior stay chaos.
Dental-only, integration was contained: one canonical notion of appointment, provider, and operatory (the room or chair), with each vendor mapped onto that shape. The invariant that held it together was the most important decision in the system —
An appointment stays an appointment; a patient stays a patient. No per-vendor database changes. Ever.
The design that works for two vendors strains at ten. By the time you're contemplating dozens across four verticals, the naive paths fail — and the tempting overcorrection (build the grand framework up front) is a trap, because you don't yet know the shape of the variability you're abstracting over.
operatory · patient
variability is absorbed at the boundary — vendor quirks never leak inward
There's no profiler for this. The work was understanding, vertical by vertical, how each one differs from the canonical model and why. Two findings shaped everything — and they're two separate axes I kept apart in my head.
And the maintenance pain was itself the signal. As vendor-specific conditions accumulated, the sync paths got harder to reason about. That accumulating friction was the data point I'd been waiting for — the branching had done its job; it was finally time to abstract.
Rejecting the second one is the senior move — not building it.
Per-vendor bespoke code, no shared model
RejectedShort-term velocity, long-term paralysis. Nothing reusable; a shared-behaviour change means touching N places. Doesn't survive past a handful of vendors.
A universal framework, designed up front
RejectedIt feels like senior engineering; it's usually a mistake. You can't design the right abstraction before real cases teach you what varies. Optometry alone would have shattered a dental-shaped framework's "every appointment has a room."
Change the core model per vendor
NeverThe tempting shortcut under deadline: "just add a vendor-specific field to the appointment." Every time, you leak exterior chaos into the interior and pay for it forever.
Canonical model + branch now, extract later
ChosenKeep the stable interior, absorb variability at the edges, duplicate-and-adapt when a divergence is too large to branch inline — and wait to build the reusable abstraction until the patterns are undeniable.
A discipline, more than a diagram — four commitments, held in sequence.
One canonical model, defended
A single internal shape every vendor maps onto. The interior never changes shape because a vendor is weird.
Adapters keyed to the access surface
Meet each system where it lives — API adapter, direct-database adapter — whose only job is to produce canonical shapes.
Variability isolated at the edges
Contained conditions — the missing-operatory guard, optional rooms — never smeared through shared code. When a divergence is large, duplicate-and-adapt rather than contort a shared path with if vendor == ….
The abstraction, extracted when earned
Once the per-vertical clustering was undeniable and the branching hurt, I built the extensible per-vertical engine on our newer platform — the framework came after the patterns, shaped by real cases, not before them.
Don't build the framework
until the real cases demand it.
The legacy branching wasn't debt to clean up. It was the phase that taught me the abstraction.
Branch-then-abstract, read as a timeline. Each stage was correct for its moment — the mistake would have been skipping straight to the end.
Branch per vendor. The right place to start.
Conditions accumulate across verticals.
Maintenance pain = permission to abstract.
Each vertical a clean strategy, not a pile of conditions.
the abstraction you build in the abstract is almost never the one the real cases need
Deliberate, temporary debt
Read a vendor's DB directly
Hold the canonical line
Being precise about ownership is part of the trade too: the largest integration I authored is the chiropractic vertical — the most divergent from the dental baseline — and the orthodontics adapter, whose hours-derivation on the vendor side belonged to another team.
External systems integrated onto a single canonical model — API, database, or UI, all made to look the same inside.
reachClinical verticals brought onto a dental-only platform — three new markets the business could sell into, with no per-vendor change to the core model.
marketTimes the interior fragmented. After dozens of integrations across four verticals, an appointment is still an appointment — the non-event that is the whole return.
stabilityThe core approach I'd repeat exactly. Three things I'd finish.
Formalize the adapter contract
The abstraction has been earned — so the per-vertical engine should become the only way integrations are written, and the remaining in-line branching migrated onto it. The job now is to finish collecting the dividend.
Per-vendor integration-health monitoring
Sixty external systems, many on-premise, are sixty things that can quietly break. A per-vendor sync-health dashboard turns "a client noticed their appointments stopped syncing" into "we noticed first."
Shrink the direct-database surface
Where a vendor now offers an API it didn't before, migrating off direct-DB trades brittleness for a stable contract. Unglamorous — but reducing what can break out from under you is real reliability work.