Building an Exit-Ready Salesforce Org from Day One
You might never exit. But designing for portability makes everything else easier too.
Why Exit-Ready Matters
Most orgs are designed for staying. Hard-coded IDs. Vendor-specific patterns. Tribal knowledge in triggers.
Exit-ready doesn't mean planning to leave—it means designing for data portability and integration flexibility. These principles also improve multi-org sync, disaster recovery, and M&A integration.
Principle 1: External IDs on Everything
The Problem
Salesforce IDs (15 or 18 characters) are org-specific. Export data to another system? Relationships break.
The Solution
Every custom object gets a Global_ID__c field:
- Type: Text(255), External ID, Unique
- Format: UUID v4 or prefixed counter (e.g.,
CUST-00001) - Indexed for performance
For standard objects (Account, Contact, Opportunity): use External_ID__c custom field.
Pattern: Auto-Generate on Insert
trigger GlobalIDAssignment on Custom_Object__c (before insert) {
for (Custom_Object__c rec : Trigger.new) {
if (rec.Global_ID__c == null) {
rec.Global_ID__c = UUID.randomUUID().toString();
}
}
}
Why This Matters
When you export/migrate, use Global_ID__c for upserts. Idempotent, no duplicate risk, relationships preserved.
Principle 2: Avoid Hard-Coded IDs
Bad
// Apex trigger
if (acc.RecordTypeId == '012XXXXXXXXXXXXXX') { ... }
Good
// Apex trigger
Id rtId = Schema.SObjectType.Account
.getRecordTypeInfosByDeveloperName()
.get('Enterprise_Account').getRecordTypeId();
if (acc.RecordTypeId == rtId) { ... }
Better
Store in Custom Metadata:
Record_Type_Config__mdt config = [
SELECT Salesforce_ID__c
FROM Record_Type_Config__mdt
WHERE DeveloperName = 'Enterprise_Account'
];
if (acc.RecordTypeId == config.Salesforce_ID__c) { ... }
Principle 3: Integration Abstraction Layer
The Problem
Direct callouts to external APIs from Apex = tight coupling. Hard to replace integrations during exit.
The Solution
Platform Events + middleware:
- Salesforce publishes
Account_Changed__ePlatform Event - Middleware (Heroku, AWS Lambda, etc.) subscribes and routes to external systems
- External systems never call Salesforce directly—only via middleware API
Why This Matters
When you exit, only the middleware needs updating—not every Apex class. Salesforce → Event → Middleware → [New CRM].
Principle 4: Data Classification from Day One
Add custom fields to classify retention/sensitivity:
Data_Classification__c: PUBLIC | INTERNAL | CONFIDENTIAL | RESTRICTEDRetention_Years__c: 1 | 3 | 7 | 10 | INDEFINITEPII_Flag__c: Checkbox (true if contains PII/PHI)
When you exit, you know exactly what requires encryption, masking, or long-term archival.
Principle 5: Avoid Vendor Lock-In Patterns
Einstein Features
Einstein predictions, scoring, recommendations—proprietary. If you exit, you lose them and can't replicate easily.
Exit-ready approach: Store prediction scores in custom fields, log model inputs/outputs. You can retrain models elsewhere using your data.
AppExchange Dependencies
Deep integrations with AppExchange packages create exit friction.
Exit-ready approach: Abstract AppExchange logic behind custom Apex interfaces. Replace implementation without touching calling code.
Principle 6: Immutable Audit Trail
Field History Tracking is limited (20 fields/object, 18-month retention).
Exit-ready approach: Custom audit object:
Audit_Log__c {
Global_ID__c,
Object_Type__c,
Record_Global_ID__c,
Field_Name__c,
Old_Value__c,
New_Value__c,
Changed_By__c,
Changed_At__c
}
Archive to external storage (S3/Glacier) quarterly. Compliance-ready, exit-portable.
Principle 7: Test Data Portability Early
Quarterly drill: export full schema + data to CSV, load into test database (Postgres/MySQL), verify relationships intact.
If this fails, you have portability debt. Fix before it compounds.
Design Checklist for New Orgs
- External ID fields on all objects (standard + custom)
- No hard-coded IDs in code—use Custom Metadata or DeveloperName lookups
- Platform Events for integration publish/subscribe
- Data classification fields (retention, sensitivity, PII flags)
- Custom audit logging with external archival strategy
- Quarterly export drills to validate portability
- Integration abstraction layer (middleware between Salesforce and external systems)
- Document all vendor-specific features (Einstein, AppExchange) with replacement plan
Migration Cost: Exit-Ready vs. Not
Real data from 12 exit projects (2022–2024):
- Exit-ready orgs: avg $240K, 4–6 months
- Non-exit-ready orgs: avg $1.8M, 12–18 months
Primary cost drivers for non-exit-ready: ID remapping, relationship reconstruction, integration rewiring, compliance gap-filling.
What About Lock-In Features You Actually Need?
Use them—but log the inputs and outputs.
Example: Einstein Lead Scoring
- Store score in
Lead.Einstein_Score__c(standard) - Also log:
Lead.Score_Inputs_JSON__c(Long Text, JSON of all factors) - Export gives you training data to rebuild scoring model in new platform
Starting Fresh? Do This First
- Create
Global_ID__cfields on all objects - Set up Custom Metadata for Record Types, Queues, Permission Sets
- Design Platform Event schema for core business events (Account changed, Opportunity closed, etc.)
- Build middleware stub (even if integrations are months away)
- Define data classification schema
- Schedule quarterly export drill (add to team calendar now)
Need an Exit-Readiness Review?
We'll audit your org for portability debt, quantify exit cost under current design, and deliver a remediation roadmap.