Retrofitting AI into a legacy system
A full end-to-end walkthrough of adding AI into a large legacy system — a 20-year-old insurance claims platform — without a rewrite and without a big-bang cutover. Constraints → integration patterns → the strangler-fig approach → data plumbing → risk containment → incremental rollout, one defensible decision at a time. Engineering-leadership framing; the legacy platform is a representative composite.
Learning objectives
- Frame the real problem of a legacy retrofit: you cannot rewrite, cannot break the system of record, and cannot pause the business.
- Choose integration patterns (anti-corruption layer, adapter, event tap, sidecar) that add AI without coupling it to legacy internals.
- Apply the strangler-fig pattern to route a slice of traffic through the AI path while the legacy path stays authoritative.
- Design the data plumbing — change-data-capture, read models, and a clean contract — so the AI reads legacy data safely.
- Contain risk with feature flags, shadow mode, kill switches, and a legacy fallback that is always one config away.
- Roll out incrementally by slice, measure against the legacy baseline, and know the exact rollback path at every step.
Meet "Continental Insurance" — a representative composite. Its claims platform is 20 years old: a monolith on an older stack, a database that is the undisputed system of record, batch jobs no one wants to touch, and a business that processes thousands of claims a day on top of it. Leadership wants AI to triage incoming claims and draft adjuster summaries. What they cannot have is a rewrite, a multi-quarter freeze, or any risk to the system of record. This is the most common enterprise AI job that isn't a greenfield demo, and it is dominated by three forces a new project never feels: you must not break what works, you must not couple to legacy internals, and you must be able to roll back in seconds.
1 · Framing the retrofit — the constraints that rule out a rewrite
Picture the meeting you've probably sat in. A VP has seen an AI demo, it triaged a claim beautifully, and now the ask lands on your desk: "Put this into the claims platform." You open the codebase — a 20-year-old monolith, a database ten teams read from, batch jobs whose authors left the company a decade ago — and the demo's confidence evaporates. The temptation is to say the honest-sounding thing: "This is too tangled; let's rebuild the claims flow cleanly and put AI in the new version." That sentence is how enterprises spend two years, burn a budget, and ship nothing while the old system keeps running anyway.
Here's the reframe that separates people who succeed at retrofits from people who stall. A retrofit is not a software problem first — it's a constraints problem. Before you draw a single box, you write down what you are not allowed to break, and those forbidden things do most of the design work for you. Think of it like renovating a house you still have to live in: you can't demolish the load-bearing wall, you can't shut off the water for a month, and every change has to be reversible if it leaks. Those aren't annoyances that get in the way of the design — they are the design. You don't get to pour a new foundation; you get to add a room the old house can support.
The formal version: a legacy retrofit is bounded by three hard limits a greenfield project never feels. You must not break what works — the claims platform processes real money every day, so the legacy path has to keep running untouched. You must not couple to legacy internals — the moment your AI code imports the legacy schema's quirks, that schema can never change, and you've made the old system more brittle by "improving" it. And you must be able to roll back in seconds — not after a deploy, not after a hotfix, but instantly, because the business cannot wait for your incident review. The common mistake is treating these as obstacles to route around; the skilled move is to treat them as the specification. Read them carefully and they name their own solutions: "keep the legacy path live" is a feature flag, "don't couple" is a translation boundary, "roll back instantly" is a kill switch. The table below turns each business constraint into the engineering consequence it forces — and by the end of the section the architecture has essentially written itself.
The instinct is "let's just rebuild the claims flow with AI in it." In a real enterprise that is how you spend two years and ship nothing. Start by writing the constraints that make a rewrite a non-starter, because they also point straight at the pattern you should use instead.
Read it left-to-right, one row at a time: a business constraint (red) with an arrow to the engineering mechanism (green) it forces.
- The red boxes on the left are the six things the business says you may not do — the non-negotiables from the meeting, not design choices.
- Each arrow is a forced move: the constraint doesn't just suggest a mechanism, it rules out every alternative until only one shape is left.
- The green boxes on the right are the mechanisms — and the blue bar at the bottom is the payoff: read them top-to-bottom and you've described the strangler-fig retrofit without ever having "chosen" it.
In short: Write down what you can't break, and the constraints hand you the architecture.
| Constraint | Business statement | Engineering consequence |
|---|---|---|
| No rewrite | The claims monolith is too risky and expensive to replace. | Add capability around the edges; the legacy code stays the system of record. |
| No downtime | Claims processing cannot pause for a migration. | Every change ships behind a flag; the legacy path always still works. |
| No coupling | The AI must not reach into legacy internals and freeze them in place. | An anti-corruption layer isolates the AI from the legacy schema and quirks. |
| Data integrity | The system of record must never be corrupted by the AI. | AI writes are advisory/queued; the legacy DB stays authoritative and validated. |
| Instant rollback | Any regression must be reversible immediately. | A kill switch reverts to the pure-legacy path in one config change. |
| Prove value per slice | Leadership funds what demonstrably works. | Roll out by claim-type slice; measure each against the legacy baseline. |
2 · Integration patterns — how the AI touches the legacy system
Every retrofit that goes wrong goes wrong at the seam. Not in the AI, not in the model — at the exact point where the new code reaches into the old system. Someone, under deadline, calls the legacy database directly, references a column named clm_stat_cd, learns that NULL in that column secretly means "pending," and hard-codes that quirk into the AI path. It works on Tuesday. Six months later the legacy team wants to rename that column, discovers the AI now depends on it, and freezes — the "improvement" has made the old system less changeable than before you touched it. That is the failure this section exists to prevent, and it's almost always caused by how the AI connected, not what the AI did.
The mental model to hold is a power adapter for foreign travel. You don't rewire the hotel's electrical system to charge your laptop, and you don't rewire your laptop to accept the hotel's socket — you put a small, dumb adapter between them that translates one shape into the other. Neither side has to know or care about the other's internals; either can be replaced without touching the other, as long as the adapter keeps translating. In a retrofit that adapter is the anti-corruption layer (ACL): a translation boundary where the legacy system speaks its 20-year-old dialect on one side and the AI speaks a clean, modern domain model on the other. The name is deliberate — its whole job is to stop the legacy schema's "corruption" (its quirks, its cryptic enums, its nulls-as-flags) from leaking into and infecting your new code.
Precisely: the ACL is the boundary; the other three patterns are the specific mechanisms you place inside it depending on how the legacy system lets you connect. An adapter/façade wraps a callable surface the legacy already exposes (an API, a stored procedure) behind a clean interface. An event tap (change-data-capture) reads the legacy database's change log as a stream, so you get current data without ever touching its write path. A sidecar/strangler façade is a new service that sits in front and routes some requests to the AI and the rest to legacy. The common mistake is to reach for one pattern and think you're done; in practice you combine them — an event tap for reading, a façade for intercepting, both wrapped in an ACL so neither welds to the legacy schema. The table lays out when each earns its place.
The core decision is how the new AI capability connects to the old system without becoming welded to it. Four patterns cover almost every case; you usually combine them. The unifying idea is an anti-corruption layer (ACL): a translation boundary so the AI speaks a clean domain model and never learns the legacy schema's 20 years of quirks.
Scan left-to-right: the old world, the translation boundary, the new world.
- The grey box on the left is the legacy system speaking its cryptic 20-year-old dialect (enums, nulls-as-flags, odd column names).
- The big boundary box in the middle is the anti-corruption layer. Notice the two mechanisms sit inside it: the event tap (CDC) for reading changes without touching the write path, and the adapter/façade for intercepting calls. The ACL isn't a fifth alternative — it's the container the other patterns live in.
- The green box on the right is the AI, which only ever sees a clean domain model. The arrows cross the boundary, so translation happens exactly once and neither side ever imports the other's internals.
In short: Put one dumb translation layer between old and new, and both can change at their own speed.
| Pattern | How it works | Use it when |
|---|---|---|
| Anti-corruption layer | A boundary that translates legacy models ↔ a clean domain model. | Always — it's the wrapper the other patterns sit inside. |
| Adapter / façade | Wrap a legacy API/DB call behind a clean interface the AI calls. | The legacy system already exposes a callable surface (API, stored proc). |
| Event tap (CDC) | Capture DB changes as an event stream the AI consumes read-only. | You need current data but must not touch the legacy write path. |
| Sidecar / strangler façade | A new service intercepts requests, routes some to AI, rest to legacy. | You want to peel off one behavior at a time behind one entry point. |
3 · The strangler-fig architecture
The pattern is named after a tree, and the tree is worth picturing before the architecture. A strangler fig starts as a tiny seed dropped in the canopy of an existing host tree. It sends roots down along the trunk, slowly, over years — and the host keeps growing, keeps bearing fruit, keeps doing its job the entire time. Only gradually, branch by branch, does the fig take over the work the host used to do, until one day the fig stands on its own and the host is gone. At no point was the forest without a working tree. That is exactly the migration guarantee you want on a live claims platform: at no point is the business without a working system.
Contrast this with the alternative every executive secretly wants — the big-bang cutover, where you build the new thing in parallel and flip everyone over on a Saturday night. Big-bang has one property that makes it seductive (it's conceptually simple) and one property that makes it catastrophic (its blast radius is everything, at once). If the new system has a bug, every claim is affected, there's no old path to fall back to because you just decommissioned it, and your rollback is a weekend of restoring backups while the business is down. The strangler-fig trades that terror for patience: you route one thin slice of traffic through the new AI path, keep the legacy path fully authoritative for everything else, and grow the slice only as it earns trust. Your blast radius at any moment is just the slice you've enabled — and you can always shrink it back to zero.
The mechanism that makes this possible is a façade: a single entry point that sits in front of the legacy system and, per request, decides "does this one go through the AI path, or straight to legacy?" The critical detail — the one beginners get wrong — is the direction of the default. The façade does not replace the legacy system; it fronts it, and legacy remains the fallback for every request the flag hasn't enabled or the AI can't handle. New capability is always an addition you can switch off, never a bridge you've burned. Read the diagram below as one claim's journey through that façade: it arrives, the façade checks whether this slice is AI-enabled, and if so the AI path reads legacy data through the ACL and emits an advisory output — while the legacy system stays the system of record the whole way through.
The strangler-fig pattern (a well-known, publicly documented approach to legacy modernization) is the backbone: put a façade in front of the legacy system, route a slice of traffic through the new AI path, and grow that slice only as it earns trust — while the legacy path stays authoritative the entire time. Read the diagram as one claim's journey: it enters the façade, which decides whether this slice is AI-enabled; the AI path reads legacy data through the ACL, produces an advisory output, and the legacy system remains the system of record.
Follow one claim left-to-right along the arrows — it's a single journey through the façade, not a set of alternatives.
- The first box is an incoming claim arriving from intake; it hits the strangler façade, the one entry point that fronts everything.
- The middle boxes are the decision and the new path: the façade does route by slice + flag (flag-gated), and only enabled slices go through the green AI path via ACL, which reads through a clean domain model.
- The payoff on the right is the pair of endpoints: the AI's result lands in an advisory output queue marked "never authoritative," while the grey legacy system of record "always still works" — so the new path is only ever an addition.
In short: One façade routes a flag-gated slice to an advisory AI path while legacy stays the system of record you can always fall back to.
4 · Data plumbing — reading legacy data safely
Here is the tension that quietly sinks retrofits. The AI is only useful if it sees current claim data — a stale view means it triages against last week's state and gives confidently wrong answers. But the write path to that data is precisely the thing you swore in §1 you would not touch. So you're caught: you need fresh data, and the obvious way to get fresh data (query the live database, or worse, write your enrichments back into it) is exactly the move that couples you to the legacy schema and risks corrupting the system of record. Every instinct pulls toward "just read the claims table directly," and every one of those instincts is a trap.
The resolution is a single discipline, and it's worth saying as a slogan you can carry into any retrofit: read, never reach in. Think of the legacy database as a river you're not allowed to dam or divert — but you're allowed to stand on the bank and watch what flows past. That watching is change-data-capture (CDC): instead of querying the database, you tap its change log — the same log the database already writes for its own replication and recovery — and turn every insert and update into an event. You've added zero code to the legacy write path; you're just observing changes it was already recording. Those events flow through the ACL (so they arrive as clean domain objects, not raw legacy rows), into a separate read model — a store shaped for exactly the queries the AI needs. The AI reads only from there, so it's decoupled from both the legacy schema and the legacy database's load.
Two consequences follow, and both catch people out. First, this read model is eventually consistent: there's a small lag between a change hitting the legacy DB and appearing in the read model. That's usually fine — but it means you must monitor freshness and degrade gracefully (fall back to legacy for a request) if the lag grows too large, rather than silently acting on stale state. Second, and non-negotiable: the AI's own outputs never flow back into the system of record directly. They land in an advisory queue, where a human or the legacy system validates them before they become truth. The distinction that matters is this — eventual consistency on the read side is acceptable; a corrupted system of record is not recoverable trust. Lose data integrity once and you lose the project, because no one will believe the AI's outputs again. The numbered flow below traces a change from the legacy DB all the way to a safely-quarantined advisory output.
The AI needs current claims data, but the write path to the system of record is exactly what you promised not to touch. The answer is to read, never reach in: capture changes as a stream and build a purpose-shaped read model the AI queries, translated through the ACL. The legacy DB stays authoritative and untouched; the AI works off a derived, eventually-consistent view.
Follow the solid blue arrows left-to-right along the top — that's the safe read path — then down into the advisory queue. The red dashed arrow is the path you must not build.
- The top row traces one change: it leaves the legacy DB's change log, gets tapped by CDC, is translated by the ACL into a clean event, lands in the purpose-built read model, and only there does the AI read it — never the legacy DB directly.
- The downward arrow shows what the AI produces going into an advisory queue, where a human or the legacy system validates it before it becomes truth.
- The red dashed arrow looping back to the legacy DB is crossed out on purpose: the AI never writes the system of record. That single prohibition is what keeps the data integrity constraint from §1.
In short: Reads are a one-way derived stream; the AI only ever proposes — it never writes the authoritative database.
The read-side data flow
- Change-data-capture (CDC) taps the legacy DB's change log and emits events — no code changes to legacy writes.
- An ACL transform maps legacy rows into a clean domain event (stable names, resolved enums, no nulls-as-flags).
- A read model (a separate store shaped for the AI's queries) is built from those events.
- The AI path queries the read model, never the legacy DB directly — decoupling load and schema.
- Any AI output is written to an advisory queue, never back into the system of record without human/legacy validation.
5 · Risk containment — flags, shadow mode & the kill switch
Ask yourself the question that decides whether you can sleep at night. It's 2 a.m., the AI path has started mis-triaging claims, and the pager goes off. How many claims are affected before you can stop it — ten, or ten thousand? How long until traffic is back on the safe legacy path — five seconds, or a five-hour incident bridge waiting on a deploy? Everything in this section exists to make those answers "a handful" and "seconds." That's what containment means: not preventing every mistake (you can't), but guaranteeing that any mistake stays small and is reversible fast.
The useful analogy is a ship's hull, which isn't one big open space but a series of sealed watertight compartments. A breach floods one compartment, the bulkheads hold, and the ship stays afloat — the design assumes damage will happen and limits how far it spreads. Your containment layer is the same idea applied to an AI retrofit: several independent mechanisms, each sealing off a different failure so no single problem can sink the whole platform. And crucially, they only work layered — a flag alone, or a kill switch alone, leaves gaps that the others cover.
Walk the layers from widest to narrowest, because each contains a different-shaped failure. A feature flag controls which slices or tenants ever see the AI path at all — default off, per-slice, so an unproven claim type simply never routes to AI. Shadow mode controls whether the AI's output is acted on or merely logged for comparison — the AI runs on real traffic but its answers stay invisible, letting you measure quality with zero risk. A circuit breaker guards against cascading failure: if the model gets slow or starts erroring, it trips automatically and sheds load back to legacy before the AI path drags claims processing down with it. A per-request legacy fallback catches the single failing request — a timeout or exception on one claim quietly serves the legacy result instead. And the kill switch is the master valve: one config change routes 100% of traffic back to pure legacy, in seconds, with no deploy. The mistake teams make is treating the kill switch as something to build after the first incident. It is a launch requirement — you must be able to demonstrate, in a rehearsed drill, that you can flip to 100% legacy in seconds before a single real claim touches the AI path. If you can't prove the rollback, you are not cleared to roll forward.
A retrofit lives or dies on containment: the blast radius of any AI mistake must be small and reversible. Four mechanisms, layered, give you that. Notice they're the same mechanisms a mature deployment uses everywhere — the retrofit just makes them non-negotiable because the legacy system is unforgiving.
Read the stacked bars top-to-bottom, widest to narrowest — each one contains a smaller, more specific failure than the one above it.
- The widest bar (feature flag) decides which slices ever reach the AI at all; below it shadow mode decides whether the AI's answer is acted on or just logged; circuit breaker catches a model that's gone slow or error-prone; the narrowest legacy fallback catches one single failing request.
- The red master valve on the right is the kill switch — its arrow shows it can cut across every layer at once and route 100% of traffic back to legacy in seconds, no deploy.
- The bar along the bottom is the legacy path: the guaranteed floor every layer falls back onto. That floor never disappears, which is why any breach stays survivable.
In short: Several independent seals plus a master kill switch, all resting on a legacy floor that always works — that's how the blast radius stays small.
| Mechanism | What it contains | How it's wired |
|---|---|---|
| Feature flag | Which slices/tenants see the AI path at all. | Per-slice, per-tenant; default off. |
| Shadow mode | Whether AI output is acted on or merely logged for comparison. | AI runs, output compared to legacy offline, never shown. |
| Kill switch | The whole AI path, instantly. | One config → façade routes 100% to legacy. |
| Circuit breaker | Cascading failure when the AI/model is slow or erroring. | Trip on error/latency threshold → fall back to legacy. |
| Legacy fallback | Any single request the AI can't handle. | Timeout/exception → serve the legacy result. |
6 · Incremental rollout — one slice at a time
You've now built every safety mechanism — and here is where teams throw them away. The pilot slice works, the demo dazzled leadership, and the ask comes: "It's clearly working, turn it on for everything next week." The pull to say yes is enormous, because momentum feels like progress. But "it worked on one slice" proves the mechanism works for that one claim type — it proves nothing about whether the AI generalizes to auto claims, or workers'-comp, or the weird catastrophe-claim edge cases, each with different data, different failure modes, and different risk. Enabling everything at once also throws away the entire point of the strangler-fig: it maximizes the blast radius you spent §3 and §5 shrinking. Speed in a retrofit does not come from skipping the gate; it comes from running the gate on many slices in parallel.
The discipline to hold is the one a doctor uses for a new medication: you don't dose the whole population on day one, you run staged trials, and each stage has to earn the next by clearing a pre-agreed bar. The bar here is always the same honest one — the legacy baseline, the old system's own numbers. You never grade the AI against a hoped-for target you invented; you grade it against what the legacy system already does today, because that's the bar the business is actually clearing right now. The stages climb in risk as trust accumulates: shadow (AI runs on one low-risk claim type, output logged and never used — does it agree with legacy?) → assist (AI drafts summaries a human sees and can reject — do adjusters accept them?) → expand (turn on more slices, each re-passing the shadow-then-assist gates) → steady state (enabled slices run with full monitoring). Notice the pattern: nothing acts on the AI's output until shadow proved it agrees, and nothing goes autonomous until assist proved humans trust it.
The rule that ties it together, and the one to say out loud in the leadership meeting: never enable a slice you couldn't instantly disable, and never advance a slice that hasn't beaten the baseline. Any slice that regresses below the legacy numbers gets flagged back off — no debate, no "let's give it another week." The rollback stays one config away at every single stage, which is what lets you move quickly because you can always undo. The table sequences the stages, the AI's scope at each, and the exact gate that advances it.
Now sequence the actual rollout. The strangler-fig only works if you grow the AI slice deliberately, measure each step against the legacy baseline, and keep the rollback one config away the whole time. Never enable a slice you couldn't instantly disable.
Read left-to-right through the four stages; the label on each arrow is the gate you must clear to advance, and the whole thing is measured against the bar in the top bar.
- The four boxes climb in risk: shadow (AI runs but output is logged, never used) → assist (it drafts summaries a human can reject) → expand (more slices turn on) → steady state (full monitoring). The amber boxes are the trust-building stages; green is where it's earned autonomy.
- The arrow labels ("agrees?", "accepted?") are the gates — each stage must clear its bar before the next opens.
- The blue bar at the top is the one honest yardstick: the legacy baseline. The red dashed arrow at the bottom is the payoff — from any stage, a slice that regresses is flagged back off in one config, so rollback is never more than one step away.
In short: Earn each stage against the legacy baseline, and keep a one-config rollback wired to every stage.
| Stage | AI scope | Gate to advance |
|---|---|---|
| 1 · Shadow | Runs on one low-risk claim-type; output logged, never used. | AI agrees with legacy at target rate on the shadow set. |
| 2 · Assist (1 slice) | Drafts adjuster summaries for that slice, shown to humans. | Adjusters accept drafts at target rate; no integrity issues. |
| 3 · Expand slices | Enable additional claim-type slices behind flags. | Each new slice re-passes the shadow → assist gates. |
| 4 · Steady state | AI path handles enabled slices with full monitoring. | Legacy fallback + kill switch verified; baseline held. |
7 · Failure modes & the containment that hardens each
There's a tell that separates a design that will survive contact with production from one that won't. Sit a seasoned tech lead down in front of your architecture and they won't ask "how does it work when everything's fine?" — they'll ask "what happens when the model is down? when the read model goes stale? when the AI writes something wrong?" And here's the diagnostic: for a hardened design, every one of those answers points at a mechanism already in the architecture. For a fragile design, the answers are "hmm, we'd add a fallback" or "we'd probably build monitoring for that." A failure mode whose containment is something you'd bolt on after an incident is not a hypothetical — it is your next incident, already scheduled.
The way to think about this is the reverse of how you designed the system. You built the architecture forward — constraints, patterns, strangler-fig, plumbing, containment, rollout. Now you run it backward from the failures, the way an aviation safety engineer works: instead of asking "what should this do," you enumerate every way it can break and demand that each broken path already lands somewhere safe. This is a failure-mode-to-mechanism map, and its real value is as a completeness check — if you can name a failure with no matching containment, you've found the gap before the gap finds you. Good news for this retrofit: because §1–§6 derived each mechanism from a constraint, the map should be complete, and the table proves it by pairing each failure with the section that already hardens it.
Read the table as a set of promises the architecture keeps. Legacy coupling? The anti-corruption layer (§2) already isolates the AI. System-of-record corruption? The advisory-write queue (§4) means the AI never writes authoritative data. A big-bang blast radius? Per-slice flags (§3, §6) keep any one bug to one slice. Model outage or latency? The circuit breaker and legacy fallback (§5) shed load to the path that always works. A silent quality drop? Shadow comparison and per-slice baseline monitoring (§5, §6) catch drift before it spreads. No way back? The rehearsed kill switch (§5). Stale data? The CDC read model with freshness monitoring (§4). The common mistake this section guards against is a design that's beautiful in the happy path and silent about the failures — every row here is a failure you'd rather meet on paper than at 2 a.m.
A retrofit "designed for production" is one where every failure mode maps to a containment mechanism already in the architecture. If a row points at something you'd bolt on after an incident, that is your next incident.
Read each row left-to-right: a way the system can break (red) with an arrow to the mechanism that already contains it (green), tagged with the section that built it.
- The red boxes on the left are the questions a seasoned tech lead asks — model down? record corrupted? data stale? — the failure cases, not the happy path.
- Each arrow is the promise: the failure already lands somewhere safe, and the section tag in the green box proves the mechanism was derived earlier from a constraint, not bolted on.
- The blue bar at the bottom is the completeness test: every failure maps to something already built. An arrow pointing at "we'd add that later" would be a gap — and a gap is your next 2 a.m. incident.
In short: If you can name a failure whose containment isn't already in the design, you've found the gap before it found you.
| Failure mode | What goes wrong | The containment that hardens it |
|---|---|---|
| Legacy coupling | AI freezes the legacy schema in place. | Anti-corruption layer isolates the AI (§2) |
| System-of-record corruption | AI writes bad data into the authoritative DB. | Advisory-write queue; legacy stays authoritative (§4) |
| Big-bang blast radius | One bug affects all claims at once. | Per-slice flags; grow the slice only as it earns trust (§3,§6) |
| Model outage / latency | AI path down takes claims down with it. | Circuit breaker + legacy fallback (§5) |
| Silent quality drop | AI drifts below the legacy baseline unnoticed. | Shadow comparison + per-slice baseline monitoring (§5,§6) |
| No way back | A regression can't be reversed fast. | Kill switch → 100% legacy in one config, rehearsed (§5) |
| Stale data | AI acts on out-of-date claim state. | CDC-fed read model with freshness monitoring (§4) |
| Dimension | Meets bar | Above bar |
|---|---|---|
| Constraints first | Names no-rewrite, no-downtime, integrity, rollback. | Derives the pattern (ACL + strangler + flags) directly from the constraints. |
| Decoupled integration | Wraps legacy behind an interface. | Anti-corruption layer keeps AI and legacy independently changeable; no legacy internals leak. |
| Strangler-fig rollout | Routes some traffic to the AI path. | Legacy path stays authoritative throughout; the slice grows only as it earns trust. |
| Safe data plumbing | Reads legacy data somehow. | CDC → ACL → read model; AI never writes the system of record; freshness monitored. |
| Risk containment | Has a feature flag. | Flags + shadow + circuit breaker + rehearsed kill switch + legacy fallback, layered. |
| Baseline-gated rollout | Rolls out gradually. | Each slice measured against the legacy baseline; regressions flagged off instantly. |
Score each dimension Meets or Above. All six at least Meets = a retrofit you could run against a real system of record. Any dimension you can't hit is where the legacy system will bite you.
A teammate proposes having the AI write its triage decision straight into the claims table "so the downstream jobs just pick it up." Why is that dangerous, and what's the safer design?
Show answer
You've enabled the AI path for one claim-type slice and quality looks good. Leadership asks you to "turn it on for everything next week." What's your answer, and why?
Show answer
🪜 Practice ladder beginner → industry
Six graded exercises, easy to real-world. Try each before opening its solution.
Context: The most expensive mistake in enterprise AI is proposing a rewrite of a working legacy system. Being able to argue against it clearly is a leadership skill.
Your task: Write three constraints that rule out a rewrite of the claims platform, and name the pattern each one points toward instead.
Requirements:
- No-downtime → the legacy path must keep working while you add the AI
- No-coupling → the AI must not depend on legacy internals
- Instant-rollback → any change must be reversible in seconds
- For each constraint, name the mechanism it implies (flag/kill switch, ACL, strangler façade)
💡 Hint: The constraints aren't obstacles to the design — read together they are the design.
Show solution
- No downtime: claims can't pause for a migration → ship behind a feature flag with the legacy path always live; the strangler façade adds capability without removing the old one.
- No coupling: the AI can't freeze the legacy schema → an anti-corruption layer translates between a clean domain model and legacy internals, keeping both changeable.
- Instant rollback: any regression must reverse fast → a kill switch routes 100% of traffic back to pure legacy in one config change.
A rewrite violates all three at once: it pauses the business, couples everything, and has no cheap rollback. The constraints point straight at the strangler-fig retrofit instead.
Context: Connecting new AI to an old system is mostly about how you touch it. The wrong connection welds the two together forever.
Your task: For the claims platform, pick the integration patterns you'd use to (a) read current claim data and (b) intercept incoming claims — and justify each.
Requirements:
- Reading current data without touching the write path
- Intercepting incoming claims to route some to the AI
- Keeping the AI's domain model clean of legacy quirks
- Justify each choice against the no-coupling / integrity constraints
💡 Hint: One boundary (the ACL) should wrap whatever specific mechanisms you choose.
Show solution
| Need | Pattern | Why |
|---|---|---|
| Read current claim data | Event tap (CDC) → read model | Gets fresh data without touching the legacy write path; decouples AI query load from the legacy DB. |
| Intercept incoming claims | Strangler façade / sidecar | One entry point routes a slice to the AI and the rest to legacy; lets you peel off behavior gradually. |
| Keep the AI clean | Anti-corruption layer (wraps both) | Translates legacy models ↔ a clean domain model so neither side is frozen by the other. |
The ACL is the outer boundary; CDC handles reads and the façade handles interception inside it. Together they satisfy no-coupling (nothing imports legacy internals) and integrity (no writes to the system of record).
Context: The strangler-fig only helps if the façade routes precisely and the legacy path is always reachable. A sloppy façade turns a safe pattern into a single point of failure.
Your task: Design the façade's routing logic: how it decides AI-vs-legacy per request, and how the legacy path stays guaranteed-available.
Requirements:
- Route by claim-type slice AND a feature flag (both must be on to use AI)
- Every request has a guaranteed legacy path, even for enabled slices (fallback on error/timeout)
- A kill switch forces 100% legacy regardless of flags
- Shadow mode: run the AI but don't act on its output, for comparison
- Explain why the legacy path is never removed, only bypassed
💡 Hint: Make 'serve the legacy result' the default that the AI path opts out of, not the other way around.
Show solution
route(request):
if KILL_SWITCH_ON: return legacy(request) # instant, config-only
slice = classify_slice(request)
if not flag_on(slice): return legacy(request) # slice not enabled
try:
ai = ai_path(request) # reads via ACL/read model
if SHADOW_MODE: log_compare(ai, legacy(request)); return legacy(request)
return ai
except (Timeout, Error): return legacy(request) # circuit-breaker fallback
The invariant: every path ends in a valid result, and the default is legacy. The AI path is reachable only when the kill switch is off, the slice's flag is on, and the call succeeds — otherwise the request quietly falls back to the legacy system that always still works. The legacy path is never removed, only bypassed, which is exactly what makes the retrofit reversible.
Context: The AI needs fresh claim data, but the write path is the one thing you promised not to touch. Get this wrong and you either corrupt the system of record or act on stale state.
Your task: Design the read-side data flow from the legacy DB to the AI, and the write-side rule that protects the system of record.
Requirements:
- Read: CDC from the legacy DB → ACL transform → a read model shaped for AI queries
- The AI queries the read model, never the legacy DB directly
- Write: AI output goes to an advisory queue, validated before it becomes truth
- Handle eventual consistency: monitor read-model freshness and degrade gracefully if stale
- Explain why direct writes to the system of record are prohibited
💡 Hint: Reads are derived and eventually consistent; writes to the system of record are someone else's validated job.
Show solution
- CDC tap: capture the legacy DB's change log as events — zero changes to legacy write code.
- ACL transform: map legacy rows to clean domain events (stable names, resolved enums, no nulls-as-flags) so the AI never learns the schema's quirks.
- Read model: build a separate store shaped for the AI's queries; the AI reads only from here, decoupling it from legacy load and schema.
- Advisory write: AI output lands in a queue; a human or the legacy system validates it before it becomes authoritative. The system of record is never written by the AI.
- Freshness: monitor read-model lag; if it exceeds a threshold, degrade (fall back to legacy for that request) rather than act on stale state.
The rule: read derived data, propose advisory outputs, and let the legacy system stay the single validated source of truth. Eventual consistency is acceptable; a corrupted system of record is not.
Context: Containment is what lets you roll a retrofit forward without fear. Each mechanism shrinks a different blast radius, and they only work layered.
Your task: Design the containment layer: name the mechanisms, what each contains, and prove the rollback is real with a drill.
Requirements:
- Feature flags (which slices/tenants), shadow mode (act vs log), kill switch (whole AI path)
- Circuit breaker (cascading failure) and per-request legacy fallback (timeout/exception)
- Describe a rollback drill that proves 100%-legacy in seconds with no deploy
- State what each mechanism contains and how they layer
- Make the kill switch a launch gate, not a post-incident addition
💡 Hint: For each mechanism, answer: what specific failure does it stop, and how fast?
Show solution
| Mechanism | Contains | Speed |
|---|---|---|
| Feature flag | Which slices/tenants ever see the AI path. | Config, per-slice, default off. |
| Shadow mode | Whether AI output is acted on at all. | Off by default; flip to act only after shadow passes. |
| Circuit breaker | Cascading failure from a slow/erroring model. | Auto-trips on threshold → legacy. |
| Legacy fallback | Any single failing request. | Per-request, on timeout/exception. |
| Kill switch | The entire AI path. | One config → 100% legacy, seconds, no deploy. |
Rollback drill: in a game day, flip the kill switch and confirm 100% of traffic serves legacy results within seconds, with no deploy and no data loss. If you can't demonstrate it, you're not cleared to enable a single real slice. Layered, these shrink the blast radius from "all claims" down to "one request, logged."
Context: The pilot slice works; now you must roll AI across a live claims platform, prove value per slice, and never once put the system of record at risk. This is the tech-lead deliverable.
Your task: Lay out the end-to-end retrofit plan: architecture, data plumbing, containment, and the staged, baseline-gated rollout — with the exact rollback path at each step.
Requirements:
- Architecture: strangler façade + ACL; legacy stays the system of record throughout
- Data: CDC → ACL → read model for reads; advisory queue for writes
- Containment: flags + shadow + circuit breaker + rehearsed kill switch + legacy fallback
- Rollout: shadow → assist (1 slice) → expand slices → steady state, each gated on the legacy baseline
- For every stage, state the metric that advances it and the one-config rollback
- Sequence it: prove the mechanism on one low-risk slice before widening
💡 Hint: Lead with containment and the ACL; earn each new slice against the legacy baseline, never on a demo's momentum.
Show solution
- Architecture. A strangler façade fronts the legacy platform; an anti-corruption layer isolates the AI. The legacy system stays the authoritative system of record for every claim, always.
- Data plumbing. CDC → ACL transform → a read model the AI queries; AI outputs go to an advisory queue, validated before they become truth. No AI writes to the system of record.
- Containment first. Per-slice flags, shadow mode, circuit breaker, per-request legacy fallback, and a rehearsed kill switch (100% legacy in seconds). Prove the rollback in a drill before enabling any real slice.
- Staged, baseline-gated rollout. Shadow on one low-risk slice (AI must agree with legacy at target rate) → assist on that slice (adjusters accept drafts at target rate) → expand slice by slice, each re-passing the gates → steady state with full monitoring.
- Rollback at every step. Each stage names the metric that advances it and the single config that reverts it. Any slice that drops below the legacy baseline is flagged off, no debate.
Order: containment and the ACL before anything; then prove the mechanism on one slice; then widen only against the legacy baseline. You add capability around a system that keeps working, and you can always switch the new growth off — that is what a safe retrofit at scale looks like.
✓ Checkpoint — you can move on when you can…
- Argue why a rewrite is the wrong first move and derive the strangler-fig retrofit from the constraints.
- Choose integration patterns (ACL, adapter, CDC event tap, strangler façade) and justify each against no-coupling and integrity.
- Design strangler-fig routing where the legacy path is always the guaranteed default.
- Design read-side plumbing (CDC → ACL → read model) and an advisory-write rule that protects the system of record.
- Layer the containment mechanisms and rehearse a kill-switch rollback to 100% legacy.
- Sequence an incremental, baseline-gated rollout with a one-config rollback at every stage.