#The challenge
Two Salesforce orgs, both heavily customised, had to become one. The org being kept was described as barely built out; the scan found roughly 275 Apex triggers, 6,500 Apex classes, 190 active flows, 60 installed packages, and a customer-facing community with several thousand external users that could not be disturbed. The org being retired carried roughly 220 triggers, 3,500 classes, 125 flows and 45 packages of its own. Between them, around 100 installed packages and roughly 95 roles and 110 profiles needed rationalising. Downstream tooling could only bind to one CRM, so reporting, CS, chat and enrichment all had to be repointed. And the deadline was not negotiable: the legacy org's contract expired on a fixed date.
#The approach
Scan both orgs before promising anything
Deep metadata scans on both sides ran before scoping, and they immediately contradicted the client's own description of the target org. The complexity discovered in that scan is what moved the estimate from weeks to months. A cross-org dependency map followed, then a migration blueprint. The commercial lesson is in the numbers: the org merge was originally positioned as a small fraction of the engagement and was not even on the kickoff deck; it became the dominant workstream and ran multiples over its original hour estimate.
Put a permanent org-of-origin flag on every migrated record
Every record carried across gets a legacy source-ID field, and a formula boolean derived from it answers 'did this come from the old org?' on Account, Contact, Lead, Opportunity and Case. That formula field, not record type, is the source of truth for the question, precisely because record type only exists on some objects and only tells you about opportunities. Reports filter on the boolean. This single decision is what made post-merge reporting possible at all, and it was the first question the customer asked on cutover morning.
Resolve username and alias collisions as a deliberate step
Every user in the legacy org was renamed with a suffix so the original username was free to be re-created in the surviving org, which meant users log in with their normal username afterwards and nobody has to relearn anything. A handful of people already existed in the target org as community users, so their new internal usernames took a distinguishing suffix instead. Ten more had aliases that collided with existing aliases and got suffixed too. All of this was communicated in advance, by name, before the blackout started.
Reconcile the object model where the picklists actually collide
The clearest example: both orgs had an account-type value whose underlying API name was identical, and API names must be unique. One value had to be renamed, which is why downstream filters started returning nothing. Two values were collapsed into one; a third could not be, because the surviving org had a live use case for it, so the merged model carries both and every downstream filter was rewritten to accept either. Merged opportunity record types kept a legacy type alongside the new ones so in-flight renewals could still reach the old stage set until unified stages shipped. Lead status was merged by union and produced semantic duplicates. Case priority came out with nine values where only four were used, and the extras broke the issue-tracker integration.
Find the relabelled standard fields before the customer does
An opportunity quoted at one value in the old org displayed that same value in the new org under a field label that had nothing to do with it. The cause: the surviving org had relabelled the standard Amount field. The retiring org did not use standard Amount at all and reported off its own recurring-revenue fields. The fix was a record-type-scoped compact layout rather than a field rebuild, so it was low-risk and reversible. Relabelled standard fields are invisible in a field-name comparison and only surface when a number looks wrong.
Rebuild the role hierarchy from the org chart, not from either org
Rather than reconciling two legacy role trees, the unified hierarchy was built against the actual HR org chart, which is how it came out at roughly 50 unique roles instead of the ~95 in the combined legacy spec. The exercise surfaced that entire role families in both orgs corresponded to executives and divisions that no longer existed; more than twenty legacy role families were marked for deletion. Permission sets and profiles were restructured directly in sandbox so production got a clean copy rather than someone's local version.
Accept what the platform will not let you migrate, and say so
Standard history tables on Opportunity, Account and Contact cannot be moved; the platform blocks it. The replacement was a custom object holding the historical progression as flat records, and the customer was told plainly that it is a list, not a timeline view. Setting audit fields on creation worked in the UI but was failing over the API, so a custom field was designed to hold the historical created and converted dates as a fallback. Archived tasks older than a year were deliberately left behind, because migrating them would un-archive them. Storage headroom was a live constraint on how much history could come at all.
Freeze automation, and track what you froze
Every flow was switched off before the load. Because someone had previously reactivated deactivated flows in sandbox, the active/inactive state of every flow was exported before cutover so restoration could be exact. This is not paranoia: at least one workflow rule was left off after the migration and a category of notification emails silently stopped sending for days before anyone noticed. Validation rules had a related failure — they were ported without change-scoping, so they fired on every save instead of only when the validated field changed, which required a sweep rather than incident-by-incident fixes.
Sequence the cutover by object over several days, with two people watching
Users were migrated first, so people had logins. Core objects loaded through the weekend, activities and notes over the following two days, history tables and attachments after that, reports and personal list views last. Production data had diverged from the sandbox copy, so new duplicates were expected and fixed on the fly, with two people present for the entire live load. Integrations were paused before the blackout and reconnected in a defined order afterwards.
Treat every downstream integration as a reconfiguration, not a credential swap
Each tool got its own migration plan. The CS platform was an ID-remapping exercise, not a rebuild: the new org stores the legacy account IDs on a custom field and the CS platform maps its mirror field to it, so rules, health scores, plays and reports survive. The marketing platform's contact links survived the org change but company and deal links reset, so deals were deleted and re-imported. The conversational-marketing tool needed a new integration user, new permissions, field remapping and per-rep reconnection, because campaign IDs are different in a different org. Several integrations were not on any list and broke silently: automation-platform recipes, a native chat integration whose backend cached the old org's access token, and OAuth connected apps that had to be re-installed as trusted apps in the merged org before reps could log email activity.
Split deduplication into stopping new ones and cleaning old ones
Systematic dedupe was explicitly out of scope, with collisions handled case by case. Native duplicate rules were enabled across lead-to-contact, lead-to-lead, contact-to-contact and account-to-account to stop new duplicates forming, with a third-party dedupe tool intended for the existing ones already merged in. The harder finding: accounts that had been merged in the legacy org over years appeared to come back as separate records, and leads previously converted and merged reappeared as live leads, because the converted state did not carry across. Account merges are normally irreversible, so re-merging at scale reopens every question — master record selection, blank-overwrite policy, related-record reassignment — that was already answered once.
Give end users a standing process instead of a one-time cleanup
The durable answer to duplicates was not a tool, it was a process the customer defined and owned: if you find a duplicate, file a case to revenue operations, who review and merge or delete. Do not merge it yourself. That went into the post-merge FAQ alongside the org-of-origin filter, the forecast-category guidance and the answer to 'where did my report go'.
Expect the ISV tail to outlast the merge
The business also distributes software through the CRM vendor's partner ecosystem, which meant the merge had to carry a licence-management org and a package listing across as well. That piece cannot be scripted: it depends on the vendor's own support process, a formal migration mechanism, and rollback confidence the vendor has to supply. It was still in a holding period two months after cutover, gated partly on the customer's own engineering team, and it was the single largest unfinished piece of the merge.
#Outcomes
Cutover executed on schedule, ahead of the contract deadline
Roughly 625,000 records across ten core objects plus about 2,900 cases landed in the surviving org over a weekend blackout, with go-live on the Monday morning. Parent-child relationships, original record ownership and audit trails were preserved; where an original owner was inactive or not flagged for migration, the original owner name and ID were retained on custom fields.
Users, roles and profiles consolidated
About eighty users migrated in a single big-bang cut, with roughly 300 legacy usernames suffixed to free the originals. Username and alias collisions were resolved and communicated ahead of the window. The unified role hierarchy came out at roughly 50 roles from ~95 in the combined legacy spec, with more than twenty dead role families retired.
Downstream systems repointed rather than rebuilt
The CS platform came back to full usability through ID remapping, confirmed by its owner on the customer side. Single sign-on, contract and renewal logic on standard objects, record-type-driven page layouts, and the remaining contract files and content versions all shipped in the weeks after go-live.
The tail was long, and some of it did not close
Hypercare ran hot: one week logged close to a hundred user questions. The licence-management-org migration, the duplicate cleanup, the reappearing merged accounts and converted leads, missing qualification-framework data on migrated opportunities, and the consolidation of duplicated page-layout tabs were all still open when the engagement ended, and rolled to the customer's internal team. There is no post-cutover reconciliation report and no formal acceptance in the record; the evidence of completion is the go-live and a long issue tracker.
The internal post-mortem named migration QA as the gap
The customer caught most defects during user acceptance testing rather than the delivery team catching them first. That is the honest read, and it is why the object-model and field-mapping checks above are written as pre-cutover work rather than post-cutover fixes.