Most migration plans are a mapping spreadsheet with a date on it. Source field, target field, transformation rule, owner. Useful, and not the part that fails.
Migrations fail on sequence. Some operations in Salesforce can be repeated until they are right, and a few can be done exactly once, with the only remedy being to delete what you loaded and load it again. A plan that does not distinguish between those two categories is a schedule, not a plan.
What follows is organized by that distinction rather than by phase, because phases are where this topic usually gets written and phases are not what hurts you.
Audit fields accept values once, and a mistake means export, delete and reload
Start here, because this is the hardest door in a Salesforce migration to walk back through.
Salesforce lets you write normally read-only system fields during a migration, so that records appear to have been created at the time they were originally created on your old system. Four fields are in scope: CreatedByID, CreatedDate, LastModifiedByID and LastModifiedDate.
Two scoping details get missed, and both change a plan. The feature applies to an enumerated list of objects, which Salesforce gives as Account, CampaignMember, CareService, Case, CaseComment, CodeSet, Contact, ContentNote, ContentVersion, Contracts, EmailMessage, FeedComment, FeedItem, FinancialGoal, FinancialPlan, HealthCondition, Idea, IdeaComment, Lead, Opportunity, Quote, Vote, Work Order, Task, Event, Attachment, and all custom objects. Check your object list against that one before promising historical dates on anything, because several standard objects that turn up in migrations, Opportunity line items and Orders among them, are absent from it.
The second detail is a gift rather than a constraint. On Lead, five conversion fields can also be set on insert: IsConverted, ConvertedDate, ConvertedAccountId, ConvertedContactId and ConvertedOpportunityId. Lead conversion history is normally the first casualty of a migration, and this is how it survives, provided the Accounts, Contacts and Opportunities those fields point at already exist when the Leads load, since a lookup cannot reference a record that is not there yet. Converted Leads therefore land later in the load sequence than their place in the business story suggests, which is worth stating in the plan rather than discovering mid-load.
The constraint is in Salesforce’s own words on the Create Audit Fields article. Since these fields are intended to be audit fields, they can only be set on create, not on update, and if the records already exist in Salesforce, they must be exported to a CSV file, deleted in Salesforce, and then imported with the updated information as new records.
Read what that implies for your plan. There is no corrective update. A load that goes in with today’s date on five years of history is not fixed by a second pass, it is fixed by deleting the records and starting over, which means re-pointing every child record and every relationship you had already loaded on top of them.
Three practical consequences belong in the plan document:
- Salesforce states these fields can be set on INSERT and not on UPDATE or UPSERT, so an upsert-based load and historical audit dates cannot coexist on the same pass.
- Setting audit fields requires the Modify All Data permission, and a System Administrator has to enable the feature.
- Salesforce recommends enabling it only during data migration, which makes switching it off a named step in your cutover runbook rather than an afterthought.
Activity history loses its original dates in two documented cases
Here is the detail that separates a plan written by someone who has done this from one assembled out of best-practice articles.
Even with Create Audit Fields enabled, Salesforce documents two situations where the dates you supply do not survive. When a Task is inserted with an association to a Contact or Lead through WhoID, or to records such as Account, Opportunity or Campaign through WhatID, the LastModifiedDate and LastModifiedBy fields will not reflect the inserted value. And when Enhanced Email is enabled and EmailMessages are inserted, the Task records auto-created alongside them do not take the CreatedDate of the EmailMessage, they get the date of insertion.
Activity history is usually the largest object set in a CRM migration and the one business users check first. Discovering this after cutover produces the worst version of the conversation, because the data is correct, the configuration is correct, and the dates are still wrong.
Decide in advance which of these you accept. Options are to carry the original timestamp into a custom field that you control, to exclude auto-created Task records from reconciliation on date fidelity, or to tell users plainly which objects carry true historical dates and which do not. Any of the three is defensible. Silence is not.
Bulk loading manufactures ownership skew, which is a role hierarchy decision
Migrations load records as a service account or a single admin, and that is where this problem originates.
Salesforce’s platform sharing guidance defines ownership skew as a single user, role or group owning many records for an object, notes it can cause long running transactions and performance degradation when change occurs, and puts the recommended ratio of owner to records at 1:10,000.
The remedy is specific and it is a configuration decision, not a data decision. Where a single user owns more than 10,000 records, Salesforce’s stated best practice is that the owner’s user record should not hold a role in the role hierarchy, and if it must hold a role, that role goes at the top of the hierarchy in its own branch.
Two things follow for the plan. Your load user needs deciding before the first row goes in, with its role position set deliberately. And if records are meant to end up owned by real users, reassignment is a separate planned operation with its own duration, not a tidy-up task someone runs on the Monday after go-live.
| Decision | Made before loading | Cost of making it late |
|---|---|---|
| Audit field strategy | Yes, insert-only | Export, delete and reload |
| Load user and its role position | Yes | Sharing recalculation across the loaded volume |
| Owner assignment approach | Yes | A second bulk operation with its own window |
| External ID on every object | Yes | Reruns create duplicates instead of updating |
| Validation and automation bypass | Yes | Partial loads with inconsistent state |
Sharing recalculation decides your cutover window, and Support controls the switch
Volume turns a correct load into a long one, and sharing is usually the reason.
Salesforce is direct about the thresholds. Above two million accounts, with Teams or Enterprise Territory Management implemented, performance needs particular attention, because these are complex sharing model components that can produce a very large volume of share records and therefore long running transactions.
For bulk changes at that scale there is a feature called Defer Sharing Calculations, which lets you make the change and then have sharing calculations happen all at once, an approach Salesforce describes as typically more efficient and better performing for bulk changes. The part that belongs in your project plan rather than your technical design: it is a feature that Salesforce Customer Support enables. You cannot turn it on yourself at 2am on cutover weekend, so if your volumes warrant it, the request is a lead-time dependency with an owner and a date.
Load order follows the object graph, and external IDs make a rerun safe
With the one-way doors identified, ordering is mechanical.
Parents load before children, because a child row needs its parent’s identifier. Users and record types exist before anything that references them. Accounts precede Contacts and Opportunities, Opportunities precede their line items, and anything with a lookup waits for its target. Self-referencing hierarchies load in two passes, the records first and the parent references second.
The piece teams skip is making every pass repeatable, and it needs stating carefully because of the audit field rule above. Put an external ID field on every migrated object holding the source system’s primary key, populated on the way in.
On passes where you are not setting audit dates, that field lets you upsert, so a rerun updates the row it loaded last time instead of creating a second one. On the historical pass you cannot upsert, so the external ID earns its place differently: it identifies exactly the set you just inserted, which makes the rollback a targeted delete of known keys rather than a guess at which records were yours. Either way the failed-load path is a retry rather than an investigation, and without the field your only rollback is a broad delete, which carries its own consequences for related records.
Automation needs an explicit position too. Validation rules, record-triggered flows, duplicate rules, assignment rules and triggers will all fire against migrated rows unless something stops them. Decide which are suspended, which stay on because they are doing work you want, and how you prove the suspended ones came back. Where the design recorded those automation choices is covered in the document that fixes the data model before loading.
Rehearsal in a sandbox produces the only honest duration estimate
Every migration plan contains a duration. Most are guesses dressed as estimates.
Salesforce’s own advice on sharing volumes makes the point for the whole exercise: test the change in a sandbox before production, because that testing gives you a baseline for how long the change will take. Nothing else does. Row counts do not predict load time, because the variables are sharing recalculation, automation, indexes and your own transformation step.
Rehearse at production-representative volume, not at a sample. Timings taken from a few thousand rows do not scale linearly to millions, because the costs that dominate at volume, sharing recalculation and automation among them, barely register on a small set. Which sandbox type can actually hold that volume is a planning constraint in itself, covered in how environments and their data get planned, and the rehearsal result is what makes the cutover timings in the go-live sequence it feeds defensible rather than optimistic.
Record two numbers from the rehearsal: elapsed time per object, and error rate per object. The second predicts your cutover more accurately than the first, because errors are what turn a timed sequence into an unplanned debugging session. Keeping both numbers against the objects named in the design, rather than in a separate tracker, is part of why GetGenerative.ai holds the migration sequence in the same project context as the data model.
Validation operates at three levels, and only one of them is a row count
Validation is where migration plans are thinnest, usually offering a single reconciliation count per object. Counts are necessary and they are the weakest of the three checks.
Counts. Source rows in, target rows created, rejects accounted for. A migration where rows in and rows out agree and nothing is explained is not reconciled, it is coincidental.
Relationships. Child records attached to the correct parent, lookups resolving to the intended target, orphan counts at zero or at a documented number. This catches the most damaging class of failure, because records that exist but hang off the wrong parent pass a count check and produce wrong reports forever.
Fidelity. A sampled field-level comparison on the fields the business actually uses, including the date fields above. Pick the sample deliberately: the oldest records, the largest accounts, anything with unusual characters, anything that went through a transformation rule.
Business sign-off attaches to the third level, because that is the only one users can judge. How that acceptance gets run and evidenced sits in the cycle that produces the sign-off evidence.
Agents read migrated records, which changes what a data defect costs
Worth adding to any plan written now, because it was not true a few years ago.
Migrated data no longer just populates reports and list views. It grounds agent responses. An agent retrieving customer history answers from whatever rows the migration produced, and it answers with the same confident phrasing whether those rows are right or wrong. A duplicate account that a human would notice and work around becomes, through an agent, two different confident answers to the same question.
This raises the value of the relationship and fidelity checks specifically. Duplicates, mis-parented children and empty fields that used to be mild annoyances now surface in natural language to someone outside your organization. Migration quality has become a customer-facing property, which is an argument for loading deliberately rather than loading everything. Legacy rows nobody will ever read are now legacy rows an agent might. The policies that govern what agents may ground on are covered in the rules that keep agent answers honest.
Where AI earns its place in a migration, and where it does not
Two tasks in this process suit a language model well, and one commonly proposed use does not.
Field mapping proposals. Given a source schema and a target object, a model produces a first-pass mapping with transformation suggestions far faster than a human, and every line is checkable against both schemas. Treat the output as a draft for review, which is also how you get mapping documentation written as a byproduct rather than afterwards.
Fuzzy matching and duplicate detection. This is the strongest fit, because it is a language problem. Deciding whether Acme Corp, ACME Corporation and Acme Corp. are one company is exactly the judgment that deterministic matching rules handle badly and natural language processing handles well, and the same applies to addresses, person names with varying transliteration, and free-text fields that need classifying into picklist values. Run it before the load, not after, since merging in Salesforce is more expensive than not creating the duplicate.
Worth naming where this sits in a delivery model rather than a toolchain. Migration planning suffers from a handoff problem, because the people who set the data model are rarely the people writing the load sequence, and the model usually reaches them as a diagram with its constraints stripped off. GetGenerative.ai removes that gap by holding one project context across its agents, so the Design Agent’s data model carries the relationships that dictate load order, the Metadata and Build Agents work against that same model, and the Testing Agent generates the reconciliation checks from it. Load order stops being reconstructed from a schema export. You can see how a migration gets scoped and staffed if that is the shape of help you need.
Not reconciliation. Do not ask a model whether the migration succeeded. Reconciliation is arithmetic against two systems of record, and it needs a query returning a number you can check, not an assessment. A model that summarizes a reconciliation report is useful; a model that produces the verdict has introduced a judgment where a count belongs.
A note on who is searching for this, since it shapes what the page owes them. Google Trends data for the year ending October 2026 puts the term salesforce data migration at its yearly high in early May. Its leading related queries are prepositional variants, data migration to salesforce and data migration in salesforce, and migration services draws more interest than migration tools. Readers arriving here are closer to needing the work done than to comparing utilities.
What a validated migration lets you sign
A plan that handles the above produces something a sponsor can approve rather than hope about.
Audit field strategy decided and exercised before the real load. Load user and ownership model set deliberately, with reassignment planned as its own operation. Sharing deferral requested early if volumes need it. Load order derived from the object graph, every object carrying an external ID so any pass can run twice. A rehearsal at representative volume giving real durations and real error rates. Three levels of validation with the fidelity sample chosen by someone who knows the business. Automation suspended and provably restored.
That package is what makes a migration a precondition for go-live rather than a risk to it, and it is the evidence set GetGenerative.ai’s Testing Agent is built to produce rather than assemble by hand afterwards. The sequencing of the wider cutover around it sits in the plan that carries the whole phase.
Questions worth settling before the first load
What are the steps in a Salesforce data migration plan?
Decide the audit field strategy, set the load user and its role position, request sharing deferral if volumes need it, derive load order from the object graph with parents before children, put an external ID on every object so passes are repeatable, suspend and later restore automation, rehearse at representative volume, load, then validate on counts, relationships and sampled field fidelity before sign-off.
Can you change CreatedDate in Salesforce after loading records?
No. Create Audit Fields allows CreatedById, CreatedDate, LastModifiedById and LastModifiedDate to be set on create only, not on update. Salesforce states that where records already exist, they have to be exported to CSV, deleted, and imported again as new records. The feature also works for insert rather than upsert, and setting these fields requires Modify All Data.
Why do migrated Salesforce activities show the wrong dates?
Two documented cases survive even with Create Audit Fields enabled. Tasks inserted with a WhoID association to a Contact or Lead, or a WhatID association to records such as Account, Opportunity or Campaign, do not reflect the supplied LastModifiedDate and LastModifiedBy. And with Enhanced Email enabled, Task records auto-created when EmailMessages are inserted carry the date of insertion rather than the EmailMessage CreatedDate.
How do you avoid ownership skew during a data migration?
Ownership skew is one user, role or group owning many records on an object, and Salesforce recommends an owner-to-record ratio of 1:10,000. Above 10,000 records for a single owner, the guidance is that the owner’s user record should not hold a role in the role hierarchy, or if it must, that the role sits at the top of the hierarchy in its own branch. Decide the load user before loading and plan owner reassignment as a separate operation.
How long does a Salesforce data migration take?
The only defensible answer comes from rehearsing at production-representative volume in a sandbox, which Salesforce recommends specifically because it gives a baseline for how long the change will take. Row counts alone do not predict duration, since sharing recalculation, automation and transformation dominate it. Record elapsed time and error rate per object from the rehearsal.
How do you validate a Salesforce data migration?
At three levels. Counts, with rows in, rows created and rejects all accounted for. Relationships, confirming children attach to the right parents and lookups resolve, since mis-parented records pass a count check. And sampled field-level fidelity on the fields the business uses, which is the level business sign-off should attach to.
ChatGPT
Claude
Perplexity