Most Salesforce data migrations do not fail during the load. They fail three weeks later, when someone in sales opens an account record and finds the contact history attached to the wrong parent, or a report returns 40% fewer opportunities than the legacy system did, and nobody can say when it broke.
The transfer is the easy part. Data Loader moves records. What determines whether a migration succeeded is the work either side of it — what you decided not to bring, what order you loaded it in, what automation you remembered to switch off, and what you actually verified afterwards.
This is the checklist we work from. It assumes a real migration into a production org with existing users, not a sandbox exercise.
Phase 1 — Decisions before you touch any data
Most migration pain traces back to a decision nobody made explicitly.
- Name the single owner. One person who can decide what gets dropped. Migrations stall when every exclusion needs a committee.
- Define what \u201cdone\u201d means, in numbers. Record counts per object, a list of reports that must return matching totals, and named users who will sign off.
- Decide the cutoff. All history, or the last N years? This is a business decision with a cost attached, and it needs answering before mapping, not during.
- Decide what you are deliberately not migrating. Closed opportunities beyond a certain age. Inactive accounts. Attachments over a size threshold. Write the exclusion list down.
- Agree the freeze window. When does the legacy system become read-only? Who tells users? What happens to records created during the freeze?
- Confirm the rollback position. Not \u201cwe will restore from backup\u201d — specifically: what does the org look like if we abort at hour six, and who makes that call?
- Check storage. Salesforce data storage is allocated per org and per record type. A large historical load can exceed it. Verify capacity before load day.
Phase 2 — Assess the source before you map anything
You cannot map data you have not looked at.
- Profile every source object. Row counts, null rates per field, distinct value counts. This surfaces the fields nobody uses and the ones with three different date formats.
- Find the duplicates now. Duplicates in the source become duplicates in Salesforce, and they are far more expensive to fix after go-live when they have acquired activity history.
- Identify the natural keys. What uniquely identifies an account in the legacy system? You need this for External IDs, and if there is not one, you need to construct it.
- Check referential integrity in the source. Orphaned child records — contacts whose account no longer exists — will fail on load. Decide now whether they are dropped or reparented.
- Inventory picklist values. Every distinct value in every source picklist field, compared against what exists in Salesforce. Mismatches fail silently into blank fields more often than they error.
- Check date and number formats. Regional date formats, thousands separators and currency fields with symbols embedded are three of the most common load failures.
- Flag encoding problems. Non-ASCII characters, smart quotes and em dashes from Excel exports corrupt on load if the file encoding is wrong.
- Assess data quality honestly. If 30% of contact email addresses are invalid in the legacy system, they will be invalid in Salesforce. Migration is the cheapest moment to clean them, and the only moment anyone will agree to fund it.
Phase 3 — Field mapping
- Build the mapping document before building anything else. Source field, target field, transformation rule, owner of the decision. This is the artifact everyone will argue over — better that they argue over it now.
- Map to External ID fields. Create a unique, external-ID-flagged field on each target object holding the legacy record identifier. This is the single most important item on this list. It makes loads idempotent, makes relationships resolvable without Salesforce IDs, and makes re-running a failed batch safe.
- Confirm field data types and lengths. A 500-character legacy field mapped to a 255-character Salesforce field truncates without warning.
- Handle required fields. Every required field on the target object needs a value or a documented default. \u201cWe will fill it in later\u201d means a blocked load.
- Decide record ownership. A migration user, the mapped legacy owner, or a queue? Ownership drives sharing, and getting it wrong is visible to every user on day one.
- Decide record types. If the target object uses record types, every incoming record needs one assigned.
- Plan for fields that do not exist yet. Custom fields needed for the migration must be created and deployed before the load, through your normal release process.
Phase 4 — Load order
Salesforce enforces parent-child dependencies. Load in the wrong order and child records fail because their parent does not exist yet. The general order:
- Users — must exist before anything can be owned or reference them
- Record types, picklist values and custom fields — configuration before data
- Accounts — parents of almost everything else
- Contacts — reference Accounts
- Opportunities — reference Accounts, and Contacts via roles
- Opportunity line items — reference Opportunities and Products
- Cases — reference Accounts and Contacts
- Activities (Tasks, Events) — reference almost anything
- Attachments and Files — reference their parent records
- Custom objects — in their own dependency order
Two complications worth naming. Self-referencing hierarchies — parent accounts referencing other accounts — create a chicken-and-egg problem: load accounts without the parent reference first, then run a second pass to populate it. Circular references have the same answer. Account has a primary contact, contact belongs to an account. Load, then update.
Phase 5 — Configure the org before loading
This is the phase most commonly skipped, and it causes the most damage.
- Deactivate validation rules. Legacy data will violate rules designed for new records. It is not a data problem, it is a sequencing problem.
- Deactivate workflow rules, process builders and record-triggered flows. Otherwise a 200,000-record load fires 200,000 automations, sends emails to real customers, and may hit governor limits.
- Deactivate or bypass Apex triggers. Where triggers must stay active, use a bypass mechanism — a custom setting or hierarchy switch the trigger checks.
- Disable email deliverability. Set org-wide email to \u201cSystem email only\u201d during the load window. This is the safeguard that prevents the migration from emailing your client\u2019s entire customer base.
- Enable Set Audit Fields. This permission allows CreatedDate, CreatedById, LastModifiedDate and LastModifiedById to be set explicitly on load. Without it, every migrated record shows as created today by the migration user, and every historical report is wrong.
- Turn off duplicate rules during the load, if the source has already been de-duplicated.
- Consider sharing recalculation. Large loads into orgs with complex sharing can trigger lengthy recalculation. Defer sharing rule calculation where the org supports it.
- Write the reactivation checklist as you go. Everything switched off must be switched back on. Maintain the list during this phase, not from memory afterwards.
Phase 6 — Run the load
- Full dress rehearsal in a full sandbox. Not a partial copy. Same volume, same sequence, same tooling. Time each step — this is where the go-live runbook timings come from.
- Choose the right tool for the volume. Data Import Wizard for small, simple loads. Data Loader for most work. Bulk API for large volumes.
- Tune batch sizes. Smaller batches for objects with heavy automation, larger for simple objects. Serial rather than parallel mode where record locking is a risk.
- Load in dependency order, verifying between steps. Do not queue all objects and walk away.
- Capture success and error files for every batch. Keep them. They are the audit trail and the basis for retries.
- Handle record locking. Concurrent loads touching related records cause UNABLE_TO_LOCK_ROW. Sorting source data by parent ID reduces this.
- Track counts as you go. Expected in, actual loaded, errors, cumulative. A discrepancy at object three is cheap. At object nine it is expensive.
Phase 7 — Validate before anyone is told it worked
- Record counts per object, source versus target, reconciled and documented.
- Relationship integrity. Count child records with null parents. Should be zero, or match your documented exclusions.
- Spot-check high-value records end to end. Pick 20 accounts across different shapes and walk every related record.
- Run the reports that must match — the list you defined in Phase 1. Compare totals to the legacy system.
- Verify audit fields. Confirm CreatedDate reflects the historical date, not load day.
- Check ownership and sharing. Log in as a real user in a real profile. Do they see what they should? Nothing they should not?
- Verify picklist values landed. Query for blanks in fields that should be populated — the silent failure mode from Phase 2.
- Confirm attachments open. File records can load successfully and still be unreadable.
- Reactivate everything from Phase 5 and verify each one, using the list you wrote at the time.
- Get named sign-off from the users you identified in Phase 1.
Phase 8 — The two weeks after go-live
This is the phase that separates a migration that worked from one that merely completed.
- Watch the error logs. Reactivated automations meeting migrated data produce failures that never appeared in the sandbox.
- Run duplicate detection after the fact. Migrated records meeting records users create in week one is where duplicates actually appear.
- Track report discrepancies as they surface. Users will find them. Give them a route to report rather than to complain.
- Keep the legacy system readable for an agreed period. Do not decommission on go-live day.
- Retain the migration artifacts. Mapping document, error files, load logs, count reconciliation. When someone asks in six months why a field is empty, this is the only way to answer.
- Book the cleanup. Every migration leaves residue. Scheduling a cleanup window in advance is the difference between fixing it and living with it.
The five failures we see most often
Audit fields not set. Every record shows as created on migration day. Every trend report, every tenure calculation, every “how long has this account been with us” question is wrong. Recoverable only by reloading.
Automation left running. Emails to live customers. Tasks assigned to real users. Occasionally a mass notification that reaches the client’s own customer base.
Picklist mismatches loading as blanks. No error, no warning. Discovered weeks later when a report returns half the expected rows.
No External IDs. Every retry becomes a manual reconciliation. Every relationship depends on Salesforce IDs that only exist post-load. This single decision determines whether a failed batch takes ten minutes or two days.
Migrating data nobody agreed to migrate. The default of “bring everything” is a decision made by not deciding. It inflates timeline, storage and cleanup, and it imports every quality problem the legacy system had.
Frequently asked questions
How long does a Salesforce data migration take?
It depends far more on data quality and object count than on record volume. Loading a million clean rows into three objects is faster than fifty thousand messy rows across twenty objects with custom automation. Profiling the source is what makes an estimate credible.
Can you migrate to Salesforce without downtime?
You can minimise it, but a genuine freeze window is usually the honest answer for a CRM cutover. What you can control is its length and its timing.
What tool should we use for Salesforce data migration?
Data Import Wizard for small, simple loads. Data Loader for most engagements. Bulk API for high volume. The tool matters less than the load order and the External ID strategy.
Can we clean the data after migrating?
You can, but it costs more. After migration, records have acquired activity, ownership and report history. Cleaning in the source system is cheaper — and migration is the only moment the business will agree to fund it.
What is the most common cause of Salesforce migration failure?
Not setting audit fields, and leaving automation active during the load. Both are fully preventable, and both require reloading to fix.
Migration discipline is not Salesforce-specific. The same principles — dependency order, verification before declaring success, a rollback position agreed in advance — apply to any large data move. We wrote about the database side of it in 300M Records in Postgres, Migrated With Zero Downtime.