Almost every company we talk to has the same shape of problem. There is one old system that everything depends on — an ERP from 2009, a Windows app someone's cousin wrote, an accounting package that only runs on one machine in the back office — and there are ten newer things bolted around it. People move data between them by hand, by email, and by spreadsheet. Somebody suggests AI. The project stalls three weeks later.
It does not stall because the model is not smart enough. It stalls at the data boundary: nobody can reliably get clean, current records out of the old system, and nobody wants to be the person who lets an AI write back into it. Solve that boundary and the AI part becomes almost boring. This guide is how we approach it.
Why replacing the legacy system is usually the wrong first move
The instinct is to rip it out. It is old, it is ugly, the vendor may not even exist anymore. But that old system is usually the only written record of fifteen years of business rules — which customers get which pricing, which jobs need a second approval, what happens on a partial shipment. Nobody has that written down anywhere else. A replacement project has to rediscover all of it, and it has to be finished before anyone gets any value.
- Replacement is a 12-to-24 month bet that pays nothing until the end.
- Integration starts paying in weeks, and it does not foreclose replacement later.
- Once a modern layer sits in front of the legacy system, retiring it becomes an incremental job instead of a cliff.
So the rule we start with: the legacy system stays the system of record. Everything new reads from it and proposes changes to it. Nothing new is allowed to become a second, competing source of truth.
The four ways data actually leaves an old system
In practice there are only four, and they trade off cleanliness against how much access you can actually get. Pick the highest one on this list that the system and the IT owner will allow.
| Path | When it works | Latency | Fragility |
|---|---|---|---|
| Documented API | Vendor still active, API licensed and enabled | Real time | Low — versioned contract |
| Read replica / direct DB read | You control the database or can get a read-only account | Near real time | Medium — schema changes silently break it |
| Scheduled export (CSV, XML, SFTP, report scheduler) | Almost always available; the report writer is the back door | 15 min to nightly | Medium — column drift, encoding, silent empty files |
| UI automation / screen scraping | Last resort, when nothing else is exposed | Minutes | High — a UI change breaks it |
A detail people miss: option three is far better than its reputation. Nearly every legacy ERP has a report scheduler that can drop a file on a share or an SFTP server on a timer. That is a supported, upgrade-safe integration point that no vendor will ever take away, and for most operational workflows a 15-minute lag is invisible to the people using it.
The integration layer
The architecture that has worked for us on every one of these projects is boringly consistent. Three pieces:
01
A sync service
Pulls from the legacy system on a schedule or a trigger, normalizes the records into a clean shape, and stores them with the original identifiers attached. Every record keeps a pointer home. Nothing is invented here.
02
An operational database and app layer
The thing your team actually uses all day: the queue, the approvals, the mobile view, the dashboard. It owns the new workflow state that the legacy system has no field for — who is reviewing this, what was the exception, which photo was attached.
03
A write-back path with a queue
Changes that belong in the legacy system go into an outbound queue as explicit, idempotent operations. They are applied one at a time, logged, retried on failure, and reconciled against a read of the system afterwards to confirm the write actually landed.
The write-back queue is the part teams skip, and it is the part that saves them. When a sync fails at 2am you want a durable list of what did not get applied, not a stack trace and a guess.
{
"id": "wb_01J8ZQ3F...", // idempotency key, generated once
"target": "erp.purchase_order",
"operation": "update_status",
"legacy_ref": { "po_number": "PO-44182" },
"payload": { "status": "APPROVED", "approved_by": "d.reyes" },
"source": { "app_event": "evt_7741", "actor": "user:214" },
"attempts": 0,
"state": "pending" // pending | applied | failed | reconciled
}Because the envelope carries an idempotency key, replaying the queue after an outage cannot double-apply anything. Because it carries source, you can answer the question every auditor eventually asks: who caused this change, and from where?
Where AI actually pays for itself
Once the boundary exists, AI becomes a component you drop into specific spots rather than a strategy. The spots that consistently return more than they cost:
- Document intake. Turning a supplier PDF, a scanned packing slip, or a photographed handwritten form into structured fields. This is the single highest-yield use in operations work, and it has enough detail to warrant its own guide.
- Classification and routing. Reading an inbound email or ticket and deciding which queue, which division, and which urgency it belongs to.
- Matching and reconciliation. Proposing which invoice line goes with which purchase order line when the descriptions do not match exactly — the fuzzy half of a job that then gets confirmed by exact code.
- Search over history. Letting someone ask "have we quoted this customer on a compactor before?" against ten years of records that were never indexed for that question.
- Drafting. First-pass replies, work summaries, change-order descriptions — always presented as a draft a human edits.
Classification and routing is the one most people underestimate, and it has enough moving parts to deserve its own write-up — see building an AI agent that turns inbound messages into scheduled jobs.
And the places we deliberately do not use it: totals, tax, pricing rules, permission checks, approval thresholds, anything that must be identical every time it runs. Those are deterministic code. A language model is the wrong tool for arithmetic that has to be right, and using it there is how you lose the trust you need for everything else.
AI at the edges, deterministic code in the middle. The model reads the messy world and proposes; the code decides and records.
Guardrails that make it survivable
01
The model never writes to the legacy system
Model output becomes a proposal object. Application code validates it against the schema and the business rules, then decides whether it goes into the write-back queue. There is no path from a generated token straight into the ERP.
02
Confidence thresholds with a human lane
Extraction above threshold with all required fields present flows through. Anything else lands in a review queue with the source document beside it. Set the threshold high at launch and lower it as you measure.
03
Everything is logged with its input
Store the prompt inputs, the raw output, the version of the model, and what the human did with it. That log is your evaluation set later, and your evidence when someone asks why the system did something.
04
One reversible step at a time
Every automated action should have an obvious undo, and the first version of every automation should be reviewable before it takes effect. Trust is earned in that order.
A realistic first 30 days
- Week 1 — Map one workflow end to end: every tool it touches, every manual handoff, every place a person retypes something. Pick the single workflow with the most retyping.
- Week 2 — Establish the read path and let it run. Compare what the sync sees against what people believe is in the system. Fix the mismatches you find; there will be some.
- Week 3 — Ship the thin app layer for that one workflow to a handful of users. No AI yet. Confirm that the queue, the statuses, and the audit trail match how the work is really done.
- Week 4 — Add the one AI step with the highest manual cost, behind a review queue. Measure how often the human accepts the proposal unchanged. That acceptance rate is the number that tells you whether to widen it.
The point of that sequence is that value shows up in week three, before a single model call. If the workflow app is not useful on its own, adding AI to it will not save it.
Signs your situation fits this pattern
- People export from one system and import into another as part of their normal week.
- You pay for SaaS tools that each cover one slice of a single process.
- The real status of a job lives in someone's inbox or in a spreadsheet on a shared drive.
- Nobody wants to touch the old system, but nobody can turn it off either.
- Your reporting takes a person a day a month to assemble.
If the second one on that list is the loudest, start with the audit in how to replace five SaaS subscriptions with one custom app — it decides what should be consolidated before anything gets built.
Frequently asked questions
What if the legacy system has no API at all?
That is the normal case, not the exception. Most integrations we build run on a scheduled export from the system's own report writer, or on a read-only database account. Both are supported, upgrade-safe paths that do not require the original vendor to still exist.
Is it safe to let AI touch operational data?
It is if the model never writes directly. In our architecture model output is a proposal that application code validates against the schema and business rules before anything is applied, and every automated action is logged with its inputs and is reversible.
How long before we see anything working?
The read path and a first thin app layer for one workflow typically land inside a month. We deliberately sequence it so the workflow app is useful before any AI step is added.
Do we have to replace our existing software?
No. The whole approach assumes the legacy system stays the system of record. The integration layer sits in front of it, which also makes an eventual replacement incremental instead of a single cutover.