CRM göçü dendiğinde çoğu ekip önce lisans fiyatlarını, sonra arayüzü konuşur. Oysa projeyi gerçekten zorlayan kısım, yıllardır biriken kayıtların ve aralarındaki ilişkilerin bir sistemden ötekine bozulmadan taşınmasıdır. Ben, Koray Yalçın olarak lead yönetimi, CRM mimarisi ve pazarlama otomasyonu üzerine çalışırken bu işin büyük ölçüde bir veri mühendisliği problemi olduğunu defalarca gördüm. Bu yazıda HubSpot'tan Salesforce'a geçişi iş tarafından değil, kodu yazacak geliştiricinin gözünden ele alıyorum: kimlik eşleştirme, upsert mantığı, yükleme sırası, delta aktarımı ve doğrulama.
Yazı belirli bir aracı satmıyor. Data Loader, Bulk API, bir ETL aracı ya da kendi yazdığınız Python betiği; hangisini seçerseniz seçin, aşağıdaki ilkeler değişmiyor.
1. Sorun neden "Excel'i dışa aktar, içe aktar" kadar basit değil?
HubSpot ile Salesforce aynı kavramları farklı modellerle tutar. HubSpot'ta tek bir Contact nesnesi vardır; Salesforce ise ham adayı (Lead) ve nitelikli kişiyi (Contact) ayırır. HubSpot'taki Company, Salesforce'ta Account olur ve Account hiyerarşisi (ana şirket ile alt şirketler) çok daha katı işler. Deal, Opportunity'ye; Ticket, Case'e; özel property'ler ise __c sonekli özel alanlara dönüşür.
Bu farklar yüzünden tablo kopyalamak yetmez. Asıl taşınması gereken şey kayıtlar arasındaki bağlardır: bir fırsatın hangi şirkete ait olduğu, hangi kişiyle konuşulduğu, iki yıllık e-posta ve not geçmişinin kime bağlı olduğu. İlişkiler koparsa veri "taşınmış" görünür ama satış ekibi için işe yaramaz hale gelir.
2. Temel taş: External ID alanı
Göçün omurgası, kaynak sistemdeki benzersiz kimliğin hedef sisteme taşınmasıdır. HubSpot'ta her nesnenin bir hs_object_id değeri vardır. Salesforce tarafında her hedef nesneye, örneğin HubSpot_ID__c adında, External ID ve Unique olarak işaretlenmiş bir metin alanı açarsınız.
Bu alan üç işe yarar:
- Upsert: Aynı kaydı ikinci kez gönderdiğinizde yeni kayıt oluşmaz, mevcut kayıt güncellenir. Göçü defalarca tekrar çalıştırabilmenin anahtarı budur.
-
İlişki kurma: Bir Opportunity yüklerken Salesforce'un kendi
AccountIddeğerini bilmenize gerek kalmaz; ilişkiyi Account üzerindeki External ID ile kurabilirsiniz. - Denetim izi: Göçten aylar sonra bile bir kaydın HubSpot'taki karşılığını bulabilirsiniz.
Pratik bir uyarı: External ID alanını metin tipinde tutun. Kimlikler sayısal görünse bile, sayısal alan baştaki sıfırları ve büyük değerleri sorunlu işleyebilir.
3. Yükleme sırası: Önce ebeveyn, sonra çocuk
İlişkili nesneler bağımlılık sırasıyla yüklenmelidir. Tipik sıra şöyledir:
- Kullanıcılar (User) ve sahiplik eşlemesi
- Account (önce ana şirketler, sonra alt şirketler)
- Contact (ve gerekiyorsa Lead)
- Opportunity ve OpportunityContactRole
- Case
- Aktiviteler: Task, Event, notlar ve e-posta kayıtları
Account hiyerarşisini tek geçişte yüklemeye çalışmak sık yapılan bir hatadır. Alt şirket, henüz oluşmamış bir ana şirkete bağlanamaz. Güvenli yol iki geçiştir: önce tüm Account'ları ParentId boş olarak upsert edin, ardından ikinci bir geçişte yalnızca ebeveyn ilişkisini External ID üzerinden güncelleyin.
Sahiplik (OwnerId) de ayrı bir eşleme tablosu ister. HubSpot'taki owner kimliklerini Salesforce kullanıcılarına bağlayan küçük bir sözlük tutun ve işten ayrılmış kişilerin kayıtlarını kime devredeceğinize göçten önce karar verin.
4. Upsert ile ilişki kurmanın örneği
Aşağıdaki örnek, simple-salesforce kütüphanesiyle bir Opportunity'yi kendi External ID'si üzerinden upsert ediyor ve Account ilişkisini Account'un External ID'siyle kuruyor. Kimlik bilgileri ortam değişkenlerinden okunuyor; anahtarları koda yazmayın.
import os
from simple_salesforce import Salesforce
sf = Salesforce(
username=os.environ["SF_USER"],
password=os.environ["SF_PASS"],
security_token=os.environ["SF_TOKEN"],
domain="test", # önce sandbox
)
def upsert_opportunity(deal: dict) -> None:
payload = {
"Name": deal["dealname"],
"StageName": STAGE_MAP[deal["dealstage"]],
"CloseDate": deal["closedate"][:10],
"Amount": deal.get("amount"),
# İlişkiyi Salesforce Id yerine Account'un External ID'si ile kur
"Account": {"HubSpot_ID__c": deal["company_id"]},
}
sf.Opportunity.upsert(f"HubSpot_ID__c/{deal['hs_object_id']}", payload)
Burada iki şey önemli. Birincisi STAGE_MAP: HubSpot pipeline aşamaları ile Salesforce StageName picklist değerleri birebir eşleştirilmeli ve eşleşmeyen her değer yükleme başlamadan raporlanmalı. İkincisi tarih ve para birimi: çok para birimli bir yapıdaysanız CurrencyIsoCode alanını ve kur tablosunu göçten önce kurun.
Büyük hacimlerde kayıt kayıt çağrı yapmak yerine Bulk API 2.0 kullanın. Mantık aynıdır; CSV içinde ilişki sütununu Account.HubSpot_ID__c gibi bir başlıkla verirsiniz ve Salesforce eşleştirmeyi kendisi yapar.
5. Önce temizlik: Tekrarlı kayıtlar taşınmaz, çözülür
Kirli veriyi yeni sisteme taşımak, sorunu pahalı bir sisteme kopyalamaktır. Göç öncesi temizlik adımında en azından şunları yapın:
- E-posta adreslerini küçük harfe çevirip boşlukları temizleyin, telefonları tek bir biçime (örneğin E.164) getirin.
- Aynı e-postaya sahip kişileri ve aynı alan adına sahip şirketleri gruplayın, hangisinin "ana kayıt" olacağına kural ile karar verin (en son aktivite, en dolu kayıt gibi).
- Uzun süredir hareketsiz kayıtları taşımak yerine arşivlemeyi değerlendirin.
- Birleştirilen kayıtların eski kimliklerini bir eşleme tablosunda saklayın; aksi halde bu kimliklere bağlı aktiviteler sahipsiz kalır.
Salesforce tarafında duplicate ve matching kurallarını göç sırasında geçici olarak gevşetmeniz gerekebilir; ama bunu bilinçli yapın ve göç bitince yeniden açın.
6. Kuru çalıştırma (dry run) ve sandbox
Üretime dokunmadan önce tam bir prova yapın. Kaynak sistem rehberinde de önerilen yaklaşım, kayıtların rastgele bir örneklemini sandbox'a aktarıp doğrulamaktır. Geliştirici açısından bu provanın çıktısı bir rapor olmalı:
- Her nesne için kaynak kayıt sayısı, başarılı ve başarısız yükleme sayısı
- Hata kodlarına göre gruplanmış başarısızlıklar (zorunlu alan eksik, picklist uyuşmazlığı, doğrulama kuralı gibi)
- Rastgele seçilmiş kayıtlarda alan alan karşılaştırma
Bu raporu her provada yeniden üretebiliyorsanız, göçünüz tekrarlanabilir demektir. Upsert ve External ID sayesinde aynı betiği ikinci, üçüncü kez çalıştırmak yeni kopya üretmez; sadece farkları günceller.
Bir de otomasyonlara dikkat: Salesforce'ta tetikleyiciler, Flow'lar ve doğrulama kuralları yükleme sırasında devreye girerek binlerce gereksiz e-posta ya da görev üretebilir. Göç kullanıcısı için bunları atlayan bir mekanizma (örneğin özel bir izin ya da ayar ile kontrol edilen bir bayrak) kurmak işinizi çok kolaylaştırır.
7. Kesin geçiş ve delta aktarımı
Tam veri aktarımı saatler sürebilir; bu sırada satış ekibi HubSpot'ta çalışmaya devam eder. Bu yüzden geçiş iki parçalı planlanır:
- Ana yükleme: Geçişten günler önce tüm geçmiş veri upsert edilir.
- Delta: Geçiş anında HubSpot salt okunur hale getirilir ve ana yüklemeden sonra değişen kayıtlar (son değişiklik tarihine göre filtrelenerek) tekrar gönderilir.
Delta için ana yüklemenin başladığı zaman damgasını kaydedin ve küçük bir güvenlik payı bırakın. Upsert kullandığınız için çakışan kayıtlar sorun çıkarmaz; aynı kayıt iki kez gelirse sadece güncellenir.
Geçişten sonra web formları, reklam platformlarının sunucu taraflı dönüşüm entegrasyonları ve diğer uç noktalar Salesforce'a yönlendirilir. Bu adım çoğu zaman unutulur ve ilk gün gelen leadler eski sisteme düşer. Geçiş kontrol listesine her entegrasyonu tek tek yazın.
8. Doğrulama: "Yüklendi" ile "doğru" aynı şey değil
Göçten sonra şu kontrolleri otomatikleştirin:
- Sayım eşitliği: Nesne bazında kaynak ve hedef kayıt sayıları (arşivlenenler ve birleştirilenler düşüldükten sonra) tutuyor mu?
- İlişki bütünlüğü: Account'u boş kalan Opportunity var mı? Sahipsiz aktivite var mı?
- Toplam değerler: Aşama bazında toplam fırsat tutarı iki sistemde aynı mı?
- Örneklem karşılaştırması: Rastgele kayıtlarda alan değerleri birebir eşleşiyor mu?
Bu kontroller bir kez değil, ilk haftalarda düzenli çalışmalı. Satış ekibinin "bu müşterinin geçmişi kaybolmuş" şikâyeti geldiğinde External ID sayesinde sorunu dakikalar içinde izleyebilirsiniz.
9. Kod kadar önemli: Kullanıcı benimsemesi
Teknik olarak kusursuz bir göç bile satış temsilcileri yeni sistemi kullanmazsa başarısız sayılır. Geliştirici olarak katkınız burada da büyük: sayfa düzenlerini sadeleştirip temsilcinin günlük işinde gerçekten kullandığı az sayıda alanı öne çıkarmak, e-posta ve takvim kayıtlarının otomatik işlenmesini sağlamak ve rol bazlı (SDR, AE, yönetici) farklı görünümler hazırlamak benimsemeyi doğrudan etkiler.
Kısa kontrol listesi
- Her hedef nesnede
HubSpot_ID__cExternal ID ve Unique alanı - Aşama, owner ve picklist eşleme tabloları, eşleşmeyen değer raporu
- Temizlik ve birleştirme kuralları, eski kimlik eşleme tablosu
- Bağımlılık sırasına göre yükleme, Account hiyerarşisi için iki geçiş
- Sandbox provası ve tekrarlanabilir hata raporu
- Göç sırasında otomasyonları atlayan bayrak
- Ana yükleme, zaman damgası ve delta
- Entegrasyon uç noktalarının yeniden yönlendirilmesi
- Sayım, ilişki ve toplam değer doğrulamaları
SSS
External ID kullanmadan göç yapılabilir mi?
Yapılabilir ama önerilmez. External ID olmadan her ilişki için Salesforce'un ürettiği kimlikleri bulup eşlemeniz gerekir ve göçü güvenle tekrar çalıştıramazsınız. Her tekrar yeni kopyalar üretir.
Lead mi Contact mı? HubSpot kişilerini nereye taşımalıyım?
Genel yaklaşım, henüz nitelendirilmemiş adayları Lead, bir şirketle ilişkilendirilmiş ve satış sürecine girmiş kişileri Contact olarak taşımaktır. Kuralı yaşam döngüsü aşaması gibi açık bir alana bağlayın ki karar kişiden kişiye değişmesin.
Aktivite geçmişini taşımak şart mı?
Satış ekibi için çoğu zaman en değerli veri budur. Hacim büyükse son belirli bir dönemi tam taşıyıp daha eskisini arşivde erişilebilir tutmak makul bir orta yoldur.
Göç ne kadar sürer?
Bu tamamen veri hacmine, özel nesne sayısına ve entegrasyonlara bağlıdır. Süreyi belirleyen şey genellikle yükleme değil, temizlik ve eşleme kararlarıdır.
Bulk API mi, kayıt kayıt REST çağrısı mı?
Binlerce kaydın üzerindeki her şey için Bulk API daha uygundur. REST çağrıları küçük düzeltmeler ve delta aktarımı için pratiktir.
Göç sırasında satış ekibi çalışmaya devam edebilir mi?
Evet, ana yükleme sırasında edebilir. Yalnızca kesin geçiş penceresinde kaynak sistem salt okunur yapılır; bu yüzden pencereyi hafta sonu gibi düşük yoğunluklu bir zamana planlamak yaygındır.
Koray Yalçın; lead yönetimi, CRM mimarisi, dijital pazarlama ve yapay zekâ entegrasyonları üzerine çalışıyor. Gayrimenkul ve B2B satış ekipleri için reklamdan CRM'e uzanan veri akışları kuruyor.












