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
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.
| 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
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.
| 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 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.
4 · Data plumbing — reading legacy data safely
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.
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
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.
| 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
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.
| 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
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.
| 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.