Most ERP migration plans are a spreadsheet with a tab per table and a column called notes. That is not a plan; it is a place to record surprises after they happen. A real plan is a set of decisions made before the first extract runs: what comes across, what does not, what a correct record looks like, and how you will prove the result matches. This is the checklist we work from, in the order the decisions have to be made.
Inventory and profile before you map anything
The inventory is the boring part: every object in the source system, where it lives, who owns it in the business, its row count, its date range, and every integration that reads or writes it. Most teams can produce it in a few days. What gets skipped is profiling, which means querying the data to find out what is actually in it rather than what the schema claims. Profiling is a set of queries run against a copy of production, and it should produce a written finding per object.
- Null and blank rates per field, especially fields the target system will require
- Distinct value counts against what the business expects, such as forty-one payment terms where the controller expected six
- Orphaned records: line items whose header no longer exists, transactions pointing at deleted customers
- Duplicate candidate rates on every master data object
- Dates, quantities, and currency amounts stored as free text
- Records untouched in three or more years, as a share of each table
- Free-text fields carrying structured meaning the old system never modeled
Profiling takes two to four weeks on a mid-sized ERP and it is the cheapest month of the project. Every defect found here becomes a mapping rule with a named owner. Every defect found during cutover becomes an outage.
The biggest lever is deciding what not to migrate
Left to itself, a project team migrates everything, because nobody wants to be the person who agreed to leave something behind. Everything is the most expensive option and rarely the most useful. The default should be to migrate what the business operates on, archive what it must be able to look up, and leave behind what only exists because the old platform demanded it.
| Data class | Default position | Reasoning |
|---|---|---|
| Open transactions (orders, jobs, invoices, purchase orders) | Migrate in full | The business cannot operate on Monday without them, and they have to be right on day one. |
| Active master data (customers, vendors, items, employees) | Migrate, after deduplication | Every future transaction attaches to these records, so defects here compound for the life of the system. |
| Financial position (AR, AP, inventory, general ledger) | Migrate as balances, not as history | You need the position, not the path that produced it. Bring opening entries your accountants have signed. |
| Closed transaction history | Archive; migrate a bounded window | Two to three years covers nearly every operational lookup. Older history is a reporting and compliance need, not an operating one. |
| Attachments and documents | Migrate by reference where the platform allows it | Document volume drives storage and processing cost with almost no effect on daily work. |
| Custom fields nobody can explain | Leave behind until someone claims them | A field with no named owner and no downstream consumer is a habit, not a requirement. |
The archive is what makes this negotiable. A read-only copy (a reporting database, or an exported extract behind a simple search) costs a fraction of migrating the same records into a live system. When someone insists on twenty years of closed orders, ask what question they would actually ask, how often, and how fast the answer has to come back. The honest answers describe a query against an archive, not a record in production.
Master data and the deduplication decision
Deduplication is the piece clients consistently underestimate, because it looks like a technical task and is not. The same customer exists under two IDs with its history split between them. A vendor appears three times with different spellings and remit-to addresses. An item code retired in 2019 was reissued in 2022 for something unrelated.
Two decisions have to be written down before anything can be merged. Matching rules define which records represent the same entity, using exact match on tax ID or email, fuzzy match on name plus address, and human review for anything in between. Survivorship rules define which value wins field by field when records merge: most recent, most complete, or whichever source system is authoritative for that field. Neither of these is a decision engineers can make.
Automate the obvious matches and route the rest into a human review queue. On a mid-sized ERP that queue runs to a few hundred or a few thousand records, and it has to be worked by someone who knows the customers. Budget it as named people and calendar weeks; it sits on the critical path and adding engineers does not compress it.
Field-level mapping and the defects you will find
The mapping document is per-field, not per-table: source field, target field, transformation rule, default when the source is empty, and (the line that gets skipped) what happens when the rule fails. Reject the row, load it with a flag, or halt the batch. That column is what you will be reading at two in the morning during cutover.
| Defect | How it shows up | How to handle it |
|---|---|---|
| Duplicate master records | One customer under two IDs with the history split between them | Match and merge before load, with a human review queue for anything the rules cannot decide alone. |
| Orphaned child records | Line items pointing at a header that no longer exists | Decide once: reject, or attach to a clearly labelled holding record. Never drop them silently. |
| Dates and numbers stored as text | ASAP, 3/4/22, and see note sharing one column | Parse what parses, report the remainder as a worklist, and let the business fix or abandon each item. |
| Meaning encoded in notes fields | Priority, payment terms, or routing living in a comment box | Extract into real fields where a rule can be written; otherwise migrate as text and accept the loss knowingly. |
| Codes reused over time | An item or account code that meant one thing before 2019 and another after | Split into distinct target records and map by date range. This is the defect most often found after go-live. |
| Balances that do not tie | Subledger totals disagreeing with the general ledger in the source system | Fix it in the source before migration. Carrying an unexplained variance forward means inheriting the argument. |
| Records nobody has touched in years | Obsolete items and closed vendors still flagged active | Load them inactive rather than active. Cleanup is far cheaper before the load than after it. |
One rule holds across all of them: transformation logic belongs in code that runs identically every time, not in somebody's spreadsheet session. You will run this migration at least four times, and anything done by hand once will be done differently on the second pass.
Reconciliation, dry runs, and real acceptance
Reconciliation means control totals agreed in advance with the people who will sign them off, usually the controller and the operations lead, never the engineers. Row counts per object. Open AR and AP. Inventory quantity and value by location. Count and value of open orders. A trial balance that ties. Agree the tolerance for each, in writing, before the first load.
- 1
Dry run one, technical. Load into an empty target and count what arrives. Success here means only that the pipeline completed and every row count is explainable. Expect failures and expect it to run slowly.
- 2
Dry run two, reconciled. Load again and run every control total. Each variance gets an owner and a written explanation: a defect to fix, a business decision to record, or a rounding rule to accept. Nothing is closed as close enough.
- 3
Dry run three, user acceptance. Load a full copy and let the people who do the job work in it for a week against their own records. Dispatchers look up their own customers, AR clerks pull their own aging. They find things no query will.
- 4
Dry run four, timed rehearsal. Run the whole sequence end to end with the clock going, in the real order, by the people who will do it at cutover. This produces the runbook and the true duration, which is always longer than the estimate.
- 5
Dry run five, if needed. If run four surfaced material defects or overran the freeze window, run it again. Adding a dry run is cheaper than shortening the freeze.
Acceptance testing performed by a steering committee is theater. It has to be done by the people who do the work daily, with their own logins and explicit permission to say no. They are the only ones who will notice that a shipping address is subtly wrong or a pricing tier landed on the wrong customer group, defects that reconcile perfectly and are still incorrect.
The team used a temporary bridge period to validate workflows and clean legacy data with real users, then migrated in controlled stages with parallel validation and a rollback plan at each step.
Cutover, verification, and what you are obliged to keep
Cutover is a freeze plus a rehearsed sequence. The freeze, a defined period during which no new transactions are entered in the source system, is what makes the final delta small and provable. Teams try to avoid it because it is operationally painful. The alternative is chasing a moving target and never being able to show that the two systems agree.
- Announce the freeze in writing, with the exact time entry stops and the manual process for anything urgent during the window
- Take the final source backup and record its checksum; this is both your rollback point and a retention artifact
- Run the migration from the rehearsed runbook rather than from memory
- Run the control totals and obtain written sign-off before any user is let into the new system
- Enter the transactions captured manually during the freeze, then reconcile a second time
- Keep the source system readable but not writable for a defined period, typically 90 days to a year
Post-cutover verification is not a dashboard. For the first weeks, watch specific business events complete end to end: the first invoice raised and paid, the first order picked and shipped, the first payroll run. The real test is the first month-end close, weeks after go-live. Do not release the delivery team before it.
Finally, record-retention obligations do not transfer just because the data did. Tax authorities, regulators, and customer contracts may require you to produce records in a defensible form for years. Decide before decommissioning what must be retained, in what format, who can retrieve it, and how you show it has not been altered. Keeping the old ERP licensed as a read-only archive is the most expensive answer and, absent a decision, the one you end up with.
Common questions
- How long does ERP data migration take?
- Profiling takes two to four weeks, mapping and deduplication four to eight, and each dry run adds two to three weeks including the fixes it generates. On a mid-sized ERP replacement, migration typically runs three to six months alongside the build. It stretches past that when data ownership is unclear, because the delay is decision-making rather than engineering.
- How much transaction history should we migrate?
- Migrate open transactions in full and bring financial position across as opening balances rather than as the history behind them. Two to three years of closed history covers nearly every operational lookup. Everything older belongs in a read-only archive, which is dramatically cheaper and answers the same questions.
- How many dry runs do we actually need?
- Plan for three and budget for four: a technical run, a reconciled run, a run used for genuine user acceptance, and a timed rehearsal that produces the cutover runbook. If the rehearsal overruns the freeze window or exposes material defects, run it again. An extra dry run costs a fraction of a failed cutover.
- Who should own data cleanup, us or the delivery partner?
- The business owns the decisions and the partner owns the tooling. Engineers can identify duplicate customers, orphaned records, and fields that do not parse, but they cannot decide which address is correct or which of two customer records should survive. Name the people who will work the review queues and protect their time, because that work sits on the critical path.
- Can we run the old ERP and the new system in parallel?
- Parallel read-only access works well and is worth keeping for a defined period after cutover. Parallel writing, entering every transaction in both systems, almost always fails, because the duplicate effort is unsustainable and the two systems drift apart within days. If continuity is the concern, move capability in stages with a rollback path rather than running dual entry.
- Does migration cost depend on how much data we have?
- Far less than people expect. Volume is handled with batching and overnight runs; it is largely a fixed engineering problem. Cost tracks the number of judgment calls hiding in the data (duplicates, unexplained codes, meaning buried in notes fields), because each one requires a business decision before a rule can be written.