Key takeaways
- A CRM migration is three separate problems: schema mapping, data migration, and workflow reconstruction. Treating them as one project is why migrations overrun.
- Run a parallel period. For at least two weeks, both CRMs are source-of-truth for different things. Yes it’s painful. Yes it’s the only way to catch the edge cases.
- Validate by count, then by sample, then by field. Row-count mismatches surface structural bugs. Sampling surfaces data-shape bugs. Field-level diffs surface mapping bugs. Do them in that order.
- Custom fields are where migrations go to die. Plan two days to map them, not two hours.
- Freeze the old system the moment you cut over. Do not leave it writable as a “safety net” — you’ll end up with two divergent sources of truth and a week-long reconciliation.
Migrations off legacy CRMs fail the same way every time. Someone exports a CSV, someone else imports it into the new system, the founder flips the switch at 5pm on a Friday, and by Monday morning the sales team is rebuilding workflows from memory while invoices are getting sent to deleted contacts.
This isn’t because CRM migrations are mysterious. It’s because they’re three problems pretending to be one. This post is the playbook we’d recommend teams run. None of it is Conduyt-specific — if you end up picking a different CRM, this still applies.
1. The three-problem framing
A CRM migration looks like “moving data from system A to system B.” It isn’t. It’s three independent problems:
- Schema mapping — figuring out how Contacts in A correspond to Contacts in B, where each custom field goes, what the status taxonomy looks like on the other side.
- Data migration — actually moving the rows. Relatively mechanical once schema is settled.
- Workflow reconstruction — rebuilding every automation, integration, report, and dashboard your team relied on. This is 60% of the effort and usually gets 10% of the planning.
Treating these as one project produces surprises. Budget for them separately, staff them separately, ship them in order. Schema first, data second, workflows last — and workflows can be iterative while the team is already live on the new system.
2. Before you export: the inventory
Before anyone touches an export button, write down what you actually have. Every legacy CRM accumulates junk — dead custom fields from a 2019 campaign, user accounts for people who left, test records from the initial setup.
The inventory you need:
- Contact count by owner. Contacts with no owner, contacts with owners who’ve left the company, contacts with duplicate email addresses. These are three different data-hygiene problems you don’t want to import.
- Pipeline and stage list, with counts. Pipelines that haven’t had a new deal in 6+ months are candidates to archive, not migrate. Stages that contain 80% of deals are candidates to split.
- Custom field list. Every custom field, what type it is, what percent of records actually have a value for it, and which of your team members uses it. Be ruthless — migrations are a clean-slate opportunity.
- Active automations. Every workflow that’s currently firing, with last-fire timestamps. Anything that hasn’t fired in 3 months is a candidate to retire.
- Integration endpoints. Every webhook, every Zapier zap, every API client. This is the list you’ll rebuild against in step 8.
Two to three days for inventory. It pays for itself immediately in reduced migration scope.
3. Schema mapping: the spreadsheet that saves your project
Open a spreadsheet. Columns: Source object, Source field, Source type, Target object, Target field, Target type, Transform, Notes.
Fill in every row. Every custom field, every system field, every relationship. This is boring. Do it anyway — the spreadsheet is what your migration script will execute against, and it’s what you reference six months later when someone asks “why is this field named this way?”
Common transforms that look innocent and aren’t:
- Enum values — “Qualified” in system A might be “Marketing Qualified Lead” in system B. Your transform column needs the exact mapping.
- Phone numbers — formats vary. Normalize to E.164 before you import.
- Timestamps — pay attention to timezones. CRMs store in UTC but display local; a careless import puts your western customers’ last-contact-date a day off.
- Owner references — if user accounts don’t exist yet in the target system, decide whether to import owner-less records (re-assignable later) or to provision all users first.
The mapping spreadsheet takes a week if you’re methodical. Skipping it takes a month of debugging.
4. Custom fields will hurt
Reserve the most time for custom fields. They look simple in the spreadsheet — Text maps to Text, right? — but they carry the most business logic and the most edge cases.
Three patterns that burn migrations:
- Semi-structured fields. A “Notes” text field that, over the years, your team started using in a specific format —
Contract value: $X | Renewal: date | Owner note: .... The target system probably has dedicated fields for two of those, and your migration should parse them out. Don’t import the raw text and assume you’ll clean it later. - Multi-select fields. Legacy systems implement these differently. Some store them as comma-separated strings in one column; others as relations. The target system will also have its own approach. Confirm how each side serializes before you export.
- Dropdown fields with orphaned values. At least one of your dropdown fields has a value that exists on records but isn’t in the current dropdown configuration — because someone deleted it without cleaning up the old records. Decide now: recreate the value in the target, remap to an active value, or null it.
Assume two full days of work just for the custom-field pass.
5. Shadow writes and parallel run
Before you cut over, run both systems in parallel for at least two weeks. The cost is real — someone has to maintain both — but the cost of cutting over without a parallel period is worse.
Two shapes of parallel run, depending on your appetite:
- Shadow writes (one-way). New records go into the new system automatically; someone manually mirrors into the legacy system for a while. Cleaner data on the new side, higher manual cost.
- Dual writes (bi-directional sync). Events in either system propagate to the other via a sync process. Lower manual cost, higher engineering cost — and the bi-directional case has subtle conflict-resolution problems you’ll hit.
For most teams, one-way shadow writes plus a bi-directional sync on just the Contact object is the sweet spot. You get the “no lost records” property for the highest-stakes object, without the full complexity of dual-write for everything.
Use the parallel period to:
- Validate that your schema mapping handles real-world data, not just test records.
- Let the sales team click around the new system without pressure to commit to it.
- Run your automations in the new system with production data to catch edge cases.
End the parallel period when everyone can answer “which system do I check?” the same way for a given question. If the answer is still “it depends,” you’re not ready.
6. Validation in three passes
Don’t validate by eyeballing. Do it in three structured passes:
Pass 1 — Count validation. For every object, row count on source should equal row count on target. If it doesn’t, you have a structural bug (filter misconfigured, relationship dropped, etc.). Do not proceed to pass 2 until counts match.
-- Count validation SQL — run on both sides, compare
SELECT
'contacts' AS object, COUNT(*) AS row_count FROM contacts
UNION ALL
SELECT 'deals', COUNT(*) FROM deals
UNION ALL
SELECT 'companies', COUNT(*) FROM companies
UNION ALL
SELECT 'activities', COUNT(*) FROM activities;
Pass 2 — Sample validation. Randomly pick 50 records of each object. For each, compare side by side. If any of the 50 diverge, expand the sample or diff at field level to find the pattern.
Pass 3 — Field-level diff for high-stakes records. Every record of strategic value — your largest accounts, your top-of-pipeline opportunities, your named customers — gets diffed field-by-field. This catches the custom-field mapping bugs that slip through count + sample.
A migration script that passes all three passes on a sample probably isn’t broken. A script that passes count but fails sample has a data-shape bug. A script that passes sample but fails field-level for big accounts has a mapping bug for a rarely-set field.
7. Workflows and automations
This is the part teams underestimate. You rebuild every automation from scratch on the new system — there’s no automated way to translate, say, a GHL trigger into a Conduyt workflow. The triggers, conditions, and actions don’t map 1:1.
Structure the rebuild:
- Start with the highest-volume automations. If your lead-capture → auto-response workflow fires 500 times a day, rebuild that first and run it in the parallel period. Side-by-side, you’ll see divergences within a week.
- Don’t rebuild everything. The inventory step surfaced the automations that haven’t fired in 3 months. Ask whether they need to exist at all. Migration is the cheapest moment to delete.
- Version what you build. Every new automation should include a note on what it replaces:
"Replaces legacy: GHL workflow 42 'Lead Intake'". Makes debugging weeks later much easier.
A rough scaling rule: if you had 20 active automations on the legacy system, assume you’ll end up with 12 on the new one after the cleanup. The 8 missing are drift.
8. Integrations: cut deliberately, not all at once
Integrations are the most dangerous part of a cutover because they’re mostly invisible. Something stops firing, you don’t notice until the monthly report is wrong.
Do not move all integrations at once. Do this instead:
- Inventory them (from step 2).
- Classify each by criticality: lead-capture (high), internal Slack pings (medium), reporting dashboards (low).
- Cut over one at a time, starting from lowest criticality. That way if something breaks, the impact is a stale dashboard, not lost leads.
- Monitor each for 48 hours after cut. Then move the next one.
If the integration owner isn’t on your team — a Zap that your CMO built, say — include them in the cutover conversation. They need to know what broke and where the new endpoint lives.
9. The cutover day
Cutover is the easy part if the prior steps went well. A short playbook for the day itself:
- Freeze the legacy system first. Switch it to read-only, or revoke write API keys, or just announce a hard cutoff time and trust the team. Do not leave it writable “just in case” — that’s how you end up with two divergent sources of truth.
- Run a final delta sync. Anything that’s been written to the legacy system in the last 24 hours since your parallel-period sync gets one more pass.
- Run validation pass 1 (count) one more time. If it passes, you’re in good shape.
- Notify the team. Post a Slack message with: the new URL, where to find legacy data if they need to reference it (read-only portal), the list of known-not-yet-migrated automations, and who to escalate to if they find something broken.
- Archive, don’t delete, the legacy system. Keep read-only access for 60 days minimum. You will need to look something up.
Do not treat cutover as “project over.” The first week on the new system is when you find the last 5% of migration issues that didn’t surface during parallel run.
10. Patterns that come up most in CRM migrations
A couple of patterns worth calling out specifically, because they come up in most CRM migrations:
- GoHighLevel has a lot of per-location complexity. If you’re running an agency on GHL, each location has its own schema, custom fields, and automations. Plan to treat each location as a separate migration with its own spreadsheet — don’t try to unify on day one.
- HubSpot’s per-contact pricing model changes your import strategy. There’s no reason to import inactive contacts on day one if you’re leaving HubSpot partially because of the contact-count bill. Migration is a good moment to decide which contacts deserve to exist on the new platform.
- Reports and dashboards rarely translate. Budget extra time to rebuild reporting. The “top-of-funnel this quarter” dashboard your sales lead looks at every morning is exactly the wrong thing to be rebuilding under time pressure during the first week post-cutover.
Closing
A good CRM migration ends with your team not noticing they moved. That’s an achievable bar if you spend more time in the spreadsheet than in the export button, run a real parallel period, and validate in structured passes. Most of the work happens before anyone clicks “import.” Most of the mistakes happen when that order gets reversed.
If you’re evaluating Conduyt as your migration destination, start the 20-day free trial and we’ll walk through your migration plan together.