Hey folks, Joe here. We just finished a massive migration from Salesforce to HubSpot at my company, and let me tell you, the most painful part wasn't the data transfer itself—it was getting a handle on all the customizations that had sprouted over the years. Our old CRM was like an attic full of forgotten boxes labeled "do not touch."
The key to our survival was building a "source of truth" document *before* we even talked to the new vendor. We didn't just list fields; we documented the entire lifecycle of a piece of data. For every custom field, we answered:
* **Origin:** Which API event, webform, or internal process creates it?
* **Dependencies:** Does it trigger a workflow? Is it a picklist that references another object?
* **Destination:** Which teams and reports consume it? Is it synced to our data lake or a reverse ETL tool like Hightouch?
* **Quality Flag:** Is this data reliable, or is it known to be garbage?
This turned out to be crucial. For example, we found a "Customer_Tier" field that was populated by a nightly batch job from our data warehouse. In the new CRM, we had to rebuild that pipeline, and knowing the exact source SQL saved us weeks.
We used a simple markdown table in a shared doc to start, but it got messy fast. I ended up writing a Python script to pull Salesforce metadata and generate a structured JSON catalog. This became our bible. Here's a snippet of the output schema we used:
```json
{
"object_name": "Contact",
"custom_field": "integration_score__c",
"api_name": "Integration_Score__c",
"data_type": "Number",
"description": "Calculated score from product usage events.",
"source_process": "Airbyte sync from Segment -> Snowflake -> nightly dbt model",
"downstream_consumers": ["Marketing_Automation_Workflow_7", "Sales_Dashboard_2"],
"migration_notes": "Map to HubSpot property 'hs_integration_score'. Requires new Airbyte connection to Snowflake model."
}
```
Having this forced us to have hard conversations early. We retired over 100 unused fields and simplified a dozen redundant workflows before the migration, which made the actual cutover so much smoother.
The biggest lesson? Document for the *future* team, not just the migration. We handed this catalog to the new operations team, and they could actually understand the data flow. It turned a terrifying project into a manageable one.
Hope this helps someone staring down a similar mountain of custom objects! What's your go-to method for untangling CRM spaghetti before a move?
ship it
ship it
Oh, absolutely. That "source of truth" document is a lifesaver. The bit about mapping the > entire lifecycle of a piece of data< is so much more important than a simple field inventory.
We learned the hard way that not doing this causes chaos later. For instance, we once had a "Lead_Source_Detail" field that marketing populated. It seemed simple until we migrated and realized our sales team's compensation dashboard pulled from it via a hidden, forgotten Zapier zap. That dashboard broke for a month because no one documented that destination.
What tool did you end up using for the documentation itself? We tried a shared spreadsheet first, but it became unmanageable. We eventually moved everything into a Notion database, which made linking dependencies and flagging quality issues way easier.
Exactly! Mapping the lifecycle is the secret sauce. We found it also exposed weird timing issues. We had a "Last_Engagement_Score" field updated by a daily cron job, but a sales report ran at 8 AM local time for each rep. That mismatch meant the report was always using yesterday's stale data.
Totally agree on moving beyond spreadsheets. We started in Google Sheets too, but once you need to track the "health" of a data pipeline - like when that nightly batch job for `Customer_Tier` fails - you need something that can integrate alerts. We hooked our Notion database up to a simple CI job that checks pipeline status and updates a field color. Red if the source job failed, green if it ran in the last 24 hours. Makes the doc a living dashboard.
Pipeline Pilot
That point about the Quality Flag is something we totally missed at first. We documented origins and destinations, but we assumed all the data coming in was good. Then we found an "Email_Opt_In_Date" field that was getting populated even when the consent was for SMS, not email. We spent ages trying to figure out why our email campaigns looked off before tracing it back to that.
So our document got a fourth column pretty quickly: "Known Data Gaps." It's a lifesaver for the new team - they know not to build a critical report on that field without cleaning it first. What did you use to track that quality info, was it just a column in your doc?