I've built four of these integrations now, connecting decades old ERP systems to modern CRM platforms, and the pattern of problems is remarkably consistent even though the specific technologies change. The most recent one connected a 2003-era on-premise ERP running on a system with no REST API at all to a cloud CRM that assumed every integration partner would speak clean JSON over HTTPS. Here's what I've actually learned building these bridges, not what the vendor documentation promises.
Before you write a single line of integration code, you need an honest inventory of what the legacy system can actually give you access to. Some old ERPs expose a SOAP API from a later add-on module. Some only allow direct database queries against replicated read-only tables. The system I worked on most recently had neither, it only supported scheduled flat file exports to a network share, generated overnight by a batch job that predated most of the current IT staff.
That flat file export became our integration point, not because it was elegant, but because it was the only reliable, supported mechanism the vendor would stand behind. Trying to query the underlying database directly would have voided our support contract and risked corrupting data during a live write. I've seen other consultants skip this step and start building against whatever access they could get technically, only to find out later that the vendor considers direct database access unsupported and won't help troubleshoot issues that arise from it.
Neither side of this integration speaks the other's language, so you need a middleware layer that does the translation. For the recent project, I built a small service that watches the network share for new export files, parses the fixed-width format the ERP produces, converts it into the CRM's expected JSON schema, and pushes it through the CRM's REST API with proper rate limiting.
The fixed-width parsing sounds trivial but wasn't. The ERP's export format hadn't been touched since the system was implemented, and the documentation for field positions was a printed manual from 2004 that didn't match the actual file byte for byte. I ended up writing a parser that validated against known good records and flagged anomalies rather than trusting the documentation blindly, because trusting it blindly caused silent data corruption in an early test run that took two days to trace back to a single misaligned field offset.
The CRM's sales team needed customer records to match the ERP's account records exactly, matched on account number, which is a field that existed in both systems but wasn't always populated consistently in the ERP due to years of manual data entry. I built a reconciliation step that runs after every sync, comparing record counts and checksums on key fields between the two systems, and generates a discrepancy report rather than silently failing or silently overwriting good data with bad.
This step caught things that would otherwise have gone unnoticed for months, duplicate account numbers in the ERP from a merger a decade earlier, customers marked active in one system and inactive in the other. Without a dedicated reconciliation pass, these discrepancies would have quietly eroded trust in the CRM data, and once sales reps stop trusting the CRM data, they stop using the CRM, and the whole integration effort was for nothing.
Legacy ERPs often have maintenance windows, nightly batch jobs, or backup processes that make them unavailable or unreliable at certain hours. Our export job ran at 2am and took forty minutes, and if the sync process tried to read the export file before it was fully written, we'd get a partial, corrupted read. I added a simple but effective safeguard: the export job writes to a temp filename and only renames it to the final filename after the write completes, and our sync process only picks up files with the final name. This kind of atomic rename pattern solves a whole category of race condition bugs that are otherwise maddening to debug because they only happen intermittently.
Fully automated pipelines sound appealing until something goes wrong at 3am and nobody notices until a salesperson complains a week later that their pipeline data looks stale. I built alerting into every stage of the pipeline, failed parses, API rate limit errors, reconciliation discrepancies above a threshold, all routed to a Slack channel that the ops team actually watches. The threshold tuning took some iteration, too sensitive and people start ignoring the alerts, too loose and real problems slip through. We settled on alerting for any discrepancy affecting more than five records or any hard failure of the sync job itself, with a daily digest for smaller anomalies that don't need immediate attention.
Don't assume the legacy system's documentation is accurate, verify against real data early. Build reconciliation and alerting before you build the happy path, because the happy path is the easy 20 percent and the failure handling is the hard 80 percent that actually determines whether the integration survives contact with production data. And set expectations with stakeholders early that this kind of bridge is not a one-time build, it's an ongoing relationship, because the legacy system's quirks will keep surfacing new edge cases for months after launch.