<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Koray Yalçın</title>
    <description>The latest articles on DEV Community by Koray Yalçın (@korayyalcin).</description>
    <link>https://dev.to/korayyalcin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4198093%2F9a712aee-d5b9-4e7a-9c95-3a50c8038d83.png</url>
      <title>DEV Community: Koray Yalçın</title>
      <link>https://dev.to/korayyalcin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/korayyalcin"/>
    <language>en</language>
    <item>
      <title>B2B portalından ERP'ye sipariş göndermek: Idempotency, outbox ve kredi limiti kontrolü üzerine geliştirici notları</title>
      <dc:creator>Koray Yalçın</dc:creator>
      <pubDate>Thu, 22 Oct 2026 15:23:06 +0000</pubDate>
      <link>https://dev.to/korayyalcin/b2b-portalindan-erpye-siparis-gondermek-idempotency-outbox-ve-kredi-limiti-kontrolu-uzerine-2eg7</link>
      <guid>https://dev.to/korayyalcin/b2b-portalindan-erpye-siparis-gondermek-idempotency-outbox-ve-kredi-limiti-kontrolu-uzerine-2eg7</guid>
      <description>&lt;p&gt;Bayilerin ve kurumsal müşterilerin kendi özel fiyatlarıyla sipariş verdiği bir B2B portalı dışarıdan bakınca basit bir e-ticaret sitesine benzer: ürün listesi, sepet, onay butonu. Ama arka tarafta durum çok farklıdır. Stok, fatura, cari hesap ve risk limiti ERP'dedir; iletişim geçmişi, fırsatlar ve upsell planı CRM'dedir; portal ise ikisinin arasında, her iki sistemin de doğru kabul ettiği tek bir gerçeği müşteriye göstermeye çalışır. Ben, Koray Yalçın olarak CRM mimarisi, lead yönetimi ve pazarlama otomasyonu üzerine çalışırken en çok zaman kaybettiren hataların arayüzde değil, tam bu entegrasyon katmanında ortaya çıktığını gördüm.&lt;/p&gt;

&lt;p&gt;Bu yazıda B2B portal projesini iş tarafından değil, siparişi ERP'ye ulaştıracak kodu yazan geliştiricinin gözünden ele alıyorum: neden portalın ERP veritabanına doğrudan bağlanmaması gerektiği, çift sipariş nasıl önlenir, outbox deseni ne işe yarar, kredi limiti kontrolü nerede yapılmalı ve fiyat listeleri nasıl önbelleğe alınır. Belirli bir ERP'yi (SAP, Logo, Mikro) ya da belirli bir mesaj kuyruğunu savunmuyorum; ilkeler hepsinde aynı.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. İlk kural: Portal ERP veritabanına SQL atmaz
&lt;/h2&gt;

&lt;p&gt;Küçük ekiplerde en hızlı çözüm gibi görünen şey, portal uygulamasının ERP'nin veritabanına bir okuma-yazma kullanıcısıyla bağlanmasıdır. İlk hafta çalışır. Sonra şu sorunlar birer birer gelir:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Şema bağımlılığı:&lt;/strong&gt; ERP güncellemesi bir tablo adını ya da alan tipini değiştirdiğinde portal sessizce bozulur.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;İş kurallarının atlanması:&lt;/strong&gt; ERP'nin kendi arayüzünden girilen siparişte çalışan doğrulamalar, tetikleyiciler ve numaralandırma mantığı doğrudan &lt;code&gt;INSERT&lt;/code&gt; ile tamamen devre dışı kalır.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performans:&lt;/strong&gt; Yoğun saatte portaldan gelen fiyat sorguları, muhasebe ekibinin gün sonu raporlarıyla aynı veritabanı kaynaklarını paylaşır.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Güvenlik:&lt;/strong&gt; İnternete açık bir uygulamanın, şirketin finansal çekirdeğine geniş yetkili bir bağlantısı olur.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Doğru yapı, portal ile ERP arasına bir entegrasyon katmanı koymaktır. Bu katman ERP'nin resmi API'sini, web servisini ya da onaylı içe aktarma mekanizmasını kullanır; portal ise yalnızca bu katmanla konuşur. Arada bir mesaj kuyruğu (RabbitMQ, Kafka ya da bulut sağlayıcınızın yönetilen kuyruğu) bulunması, ERP yavaşladığında veya bakımdayken siparişlerin kaybolmadan beklemesini sağlar.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Siparişin yaşam döngüsünü baştan tanımlayın
&lt;/h2&gt;

&lt;p&gt;Kod yazmadan önce bir siparişin hangi durumlardan geçebileceğini netleştirmek, sonraki bütün tartışmaları kısaltır. Tipik bir durum makinesi şöyle görünür:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TASLAK -&amp;gt; GONDERILDI -&amp;gt; ERP_KUYRUGUNDA -&amp;gt; ERP_ONAYLADI -&amp;gt; SEVK_EDILDI -&amp;gt; FATURALANDI
                     \-&amp;gt; FINANS_ONAYI_BEKLIYOR -&amp;gt; (ONAY) -&amp;gt; ERP_KUYRUGUNDA
                     \-&amp;gt; REDDEDILDI
ERP_KUYRUGUNDA -&amp;gt; ERP_HATASI -&amp;gt; (yeniden deneme) -&amp;gt; ERP_KUYRUGUNDA
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Burada önemli olan iki şey var. Birincisi, portal siparişi "gönderildi" diye işaretlediği anda ERP'de sipariş oluşmuş değildir; müşteriye gösterilen ifade de buna uygun olmalıdır ("Siparişiniz alındı, onay bekleniyor" gibi). İkincisi, her durum geçişi kaydedilmelidir. Bir bayi "siparişim nerede?" diye aradığında müşteri temsilcisinin bakacağı yer bu geçmiştir.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Çift siparişi önlemek: Idempotency anahtarı
&lt;/h2&gt;

&lt;p&gt;B2B'de çift sipariş, B2C'deki kadar masum değildir. Tek bir sipariş satırı birkaç palet hammadde olabilir. Çift kaydın en yaygın sebepleri bellidir: kullanıcı butona iki kez basar, mobil ağ yanıtı yarıda keser ve uygulama isteği tekrarlar, kuyruk tüketicisi mesajı işledikten sonra onay vermeden çöker ve mesaj yeniden teslim edilir.&lt;/p&gt;

&lt;p&gt;Çözüm, her sipariş için istemci tarafında üretilen bir &lt;strong&gt;idempotency anahtarı&lt;/strong&gt;dır. Sepet onay ekranı açıldığında bir UUID üretilir ve sipariş isteğiyle birlikte gönderilir. Sunucu tarafında bu anahtar benzersiz kısıtla saklanır:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;portal_orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;               &lt;span class="n"&gt;BIGSERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;idempotency_key&lt;/span&gt;  &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;customer_code&lt;/span&gt;    &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt;           &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;erp_order_no&lt;/span&gt;     &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt;       &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aynı anahtarla ikinci istek geldiğinde yeni sipariş oluşturulmaz; ilk siparişin durumu döndürülür. Aynı mantık entegrasyon katmanının ERP'ye yazdığı noktada da gerekir. ERP'nin sipariş kaydında harici referans alanı varsa (çoğunda "belge no", "harici referans" ya da benzeri bir alan bulunur) portal sipariş kimliği oraya yazılır ve ERP'ye göndermeden önce bu referansla kayıt olup olmadığı kontrol edilir. Böylece kuyruk mesajı iki kez işlense bile ERP'de tek sipariş oluşur.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Outbox deseni: Veritabanı ile kuyruk arasındaki boşluk
&lt;/h2&gt;

&lt;p&gt;Klasik hata şudur: uygulama siparişi kendi veritabanına yazar, ardından kuyruğa bir mesaj atar. Veritabanı işlemi başarılı olur ama kuyruk çağrısı ağ hatası verirse sipariş portalda görünür, ERP'ye hiç gitmez. Ters sırada yaparsanız bu kez ERP'ye giden ama portalda olmayan bir sipariş oluşabilir.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transactional outbox&lt;/strong&gt; deseni bu boşluğu kapatır. Sipariş kaydı ile "gönderilecek mesaj" kaydı aynı veritabanı işlemi içinde yazılır:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;submit_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idempotency_key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;insert_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idempotency_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GONDERILDI&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO outbox (aggregate_id, event_type, payload) VALUES (%s, %s, %s)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OrderSubmitted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ayrı bir yayıncı süreç &lt;code&gt;outbox&lt;/code&gt; tablosunu düzenli okur, mesajları kuyruğa iletir ve başarıyla iletilenleri işaretler. Yayıncı çökerse mesaj tabloda bekler; tekrar ayağa kalktığında kaldığı yerden devam eder. Bu yöntem "en az bir kez teslim" garantisi verir; bu yüzden bir önceki bölümdeki idempotency kontrolü zorunlu bir eştir. İkisi birlikte, pratikte "tam bir kez etki" sonucunu üretir.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Kredi limiti kontrolü nerede yapılmalı?
&lt;/h2&gt;

&lt;p&gt;B2B portalının perakende e-ticaretten en belirgin farkı, ödemenin çoğu zaman sipariş anında değil vadeli olarak yapılmasıdır. Bu da her siparişte müşterinin açık hesap riskini, vadesi geçmiş borcunu ve tanımlı kredi limitini kontrol etmeyi gerektirir.&lt;/p&gt;

&lt;p&gt;Bu kontrolün iki katmanda yapılması sağlıklıdır:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Portal tarafında ön kontrol:&lt;/strong&gt; Sepet onay ekranında entegrasyon katmanı üzerinden ERP'den güncel risk bilgisi alınır. Limit aşılıyorsa kullanıcıya baştan söylenir; sipariş "Finans Onayı Bekliyor" durumuna gideceği açıkça belirtilir.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ERP tarafında kesin kontrol:&lt;/strong&gt; Asıl karar ERP'ye yazılırken verilir, çünkü ön kontrol ile yazma arasında başka bir sipariş ya da tahsilat bakiyeyi değiştirmiş olabilir.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ön kontrol için ERP'ye her sepet güncellemesinde sorgu atmak gerekmez. Risk bilgisini kısa süreli (örneğin birkaç dakikalık) önbellekte tutmak ve yalnızca onay adımında taze veri çekmek hem ERP'yi korur hem kullanıcı deneyimini hızlandırır. Finans onayına düşen siparişler için CRM'de ilgili müşteri temsilcisine otomatik görev açmak da iyi bir pratiktir; temsilci müşteriyi arayıp tahsilatı konuşabilir ve sipariş beklemede çürümez.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Kademeli fiyat listeleri ve önbellek
&lt;/h2&gt;

&lt;p&gt;Her bayinin kendi iskonto grubu, hacim kademeleri ve bazen ürün bazlı özel fiyatları vardır. Portal her ürün listesinde bu fiyatları ERP'den anlık hesaplatırsa sayfa yavaşlar; tamamen önbellekten gösterirse yanlış fiyatla sipariş alma riski doğar.&lt;/p&gt;

&lt;p&gt;İşe yarayan yaklaşım şudur:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fiyat listeleri (price book) ve iskonto kuralları entegrasyon katmanı tarafından ERP'den periyodik olarak çekilip portalın okuma modeline yazılır. Değişiklik olayı yayınlayabilen bir ERP'de bu, olay tabanlı da yapılabilir.&lt;/li&gt;
&lt;li&gt;Listeleme ve arama ekranları bu okuma modelinden beslenir.&lt;/li&gt;
&lt;li&gt;Sepet onayı sırasında son fiyat ERP'den doğrulanır. Fark varsa kullanıcıya gösterilir ve onay yeniden istenir.&lt;/li&gt;
&lt;li&gt;Her fiyat kaydında "geçerlilik başlangıcı" ve "son senkron zamanı" tutulur. Senkron belirli bir süreden uzun gecikirse sistem uyarı üretir.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bu yapı, kampanya dönemlerinde fiyatların toplu değiştiği günlerde özellikle önem kazanır. Kullanıcı eski fiyatı görüp siparişi verdiğinde yaşanacak tartışma, birkaç saniyelik ek doğrulamadan çok daha pahalıdır.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. CRM'e ne gider, ne gitmez?
&lt;/h2&gt;

&lt;p&gt;Portal, ERP ve CRM aynı anda konuştuğunda en sık yapılan hata, her şeyi her yere kopyalamaktır. Pratikte iyi işleyen ayrım şöyledir:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ERP'de kalır:&lt;/strong&gt; stok, fatura satırları, cari hareketler, risk hesaplaması. Bunların ana kaynağı ERP'dir.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRM'e özet olarak gider:&lt;/strong&gt; sipariş toplamı, sipariş sıklığı, son sipariş tarihi, ürün kategorisi dağılımı, finans onayına düşen siparişler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRM'de doğar:&lt;/strong&gt; iletişim geçmişi, fırsatlar, upsell ve çapraz satış planları, müşteri memnuniyeti notları.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bu özet veriler CRM'de çok değerli tetikleyicilere dönüşür. Örneğin düzenli olarak ayda bir sipariş veren bir bayi kırk beş gündür sipariş vermediyse temsilciye görev açılabilir. Bu, müşteri kaybını erken yakalamanın en ucuz yollarından biridir ve verinin tamamını değil yalnızca doğru özeti taşımayı gerektirir.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Gözlemlenebilirlik: Kaybolan siparişi beş dakikada bulmak
&lt;/h2&gt;

&lt;p&gt;Entegrasyon katmanı kara kutu olursa her sorun bir suçlama oyununa döner: portal ekibi ERP'yi, ERP ekibi ağı, ağ ekibi kuyruğu suçlar. Bunu önlemek için baştan şunları koyun:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Her siparişe portal tarafında üretilen bir &lt;strong&gt;korelasyon kimliği&lt;/strong&gt; atayın ve bu kimliği portal logundan kuyruk mesajına, oradan ERP isteğine kadar taşıyın.&lt;/li&gt;
&lt;li&gt;Kuyrukta belirli sayıda başarısız denemeden sonra mesajı bir &lt;strong&gt;hata kuyruğuna&lt;/strong&gt; (dead letter queue) alın ve bunun için alarm kurun. Sessizce bekleyen bir hata kuyruğu, en tehlikeli kuyruktur.&lt;/li&gt;
&lt;li&gt;"Gönderildi ama belirli süredir ERP onayı yok" durumundaki siparişleri listeleyen basit bir panel hazırlayın. Operasyon ekibi sabah ilk iş bu listeye bakmalıdır.&lt;/li&gt;
&lt;li&gt;ERP hata mesajlarını ham haliyle değil, sınıflandırarak saklayın: geçici hata (zaman aşımı, kilit), veri hatası (tanımsız ürün kodu, birim uyumsuzluğu), iş kuralı hatası (limit, bloke cari). Yeniden deneme yalnızca geçici hatalar için anlamlıdır.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  9. Canlıya geçişten önce veri hijyeni
&lt;/h2&gt;

&lt;p&gt;Entegrasyonun kalitesi, ERP'deki ana verinin kalitesini geçemez. Portal açılmadan önce ürün kodlarının, barkodların, birim çevrimlerinin (koli, adet, paket) ve cari kodlarının standartlaştırılması gerekir. Aynı ürünün iki farklı kodla tutulduğu ya da birim tanımının eksik olduğu bir katalogda, en iyi tasarlanmış idempotency ve outbox yapısı bile yanlış siparişi hatasız şekilde ERP'ye taşır.&lt;/p&gt;

&lt;p&gt;Geçişi de kademeli yapmak mantıklıdır: önce teknolojiye yatkın, sadık küçük bir bayi grubuyla pilot yapılır, durum geçişleri ve hata kuyruğu birkaç hafta izlenir, sonra tüm ağa açılır. Pilot dönemde toplanan hata sınıfları, entegrasyonun gerçekte nerede zorlandığını gösteren en dürüst veridir.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sık Sorulan Sorular (SSS)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mesaj kuyruğu küçük bir portal için gereksiz karmaşıklık değil mi?&lt;/strong&gt;&lt;br&gt;
Günde birkaç sipariş alan bir yapıda kuyruk yerine outbox tablosunu doğrudan okuyan bir arka plan işi yeterli olabilir. Önemli olan kuyruğun kendisi değil, siparişin ERP geçici olarak erişilemezken kaybolmamasıdır.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Idempotency anahtarını sunucu mu üretmeli, istemci mi?&lt;/strong&gt;&lt;br&gt;
İstemci üretmelidir; çünkü amaç, aynı kullanıcı eyleminin tekrarlanan isteklerini tanımaktır. Sunucu yalnızca anahtarı benzersiz kısıtla saklar ve tekrar eden istekte ilk sonucu döndürür.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ERP harici referans alanı desteklemiyorsa ne yapılır?&lt;/strong&gt;&lt;br&gt;
Entegrasyon katmanı kendi eşleme tablosunu tutar: portal sipariş kimliği ile ERP belge numarası. ERP'ye yazmadan önce bu tabloya bakılır, yazdıktan hemen sonra tablo güncellenir. Bu durumda yazma ile eşleme arasındaki kısa boşluk için ERP tarafında periyodik bir mutabakat kontrolü eklemek iyi olur.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fiyatları tamamen gerçek zamanlı göstermek mümkün değil mi?&lt;/strong&gt;&lt;br&gt;
Mümkün, ama ERP'nin hesaplama yükünü ve yanıt süresini portalın her sayfa açılışına bağlar. Okuma modeli ile listeleme, onayda kesin doğrulama yaklaşımı çoğu durumda hız ve doğruluk arasındaki en sağlıklı dengedir.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kredi limiti aşıldığında sipariş tamamen reddedilmeli mi?&lt;/strong&gt;&lt;br&gt;
Çoğu B2B ilişkisinde hayır. Siparişi "Finans Onayı Bekliyor" durumuna almak ve ilgili temsilciye görev açmak, hem satışı kaybetmez hem riski kontrol altında tutar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bu yapıyı CRM tarafında nasıl değerlendirmeliyim?&lt;/strong&gt;&lt;br&gt;
Sipariş özetlerini CRM'e taşıyıp sipariş sıklığındaki düşüşleri, finans onayına düşen siparişleri ve kategori değişimlerini tetikleyici olarak kullanın. Portal yalnızca sipariş kanalı değil, müşteri davranışını ölçen bir veri kaynağı haline gelir.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Koray Yalçın; lead yönetimi, CRM mimarisi, dijital pazarlama ve yapay zekâ entegrasyonları üzerine çalışıyor. B2B ve gayrimenkul satış ekipleri için reklamdan CRM'e, CRM'den operasyon sistemlerine uzanan veri akışları kuruyor.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>HubSpot'tan Salesforce'a veri taşırken External ID, upsert ve ilişki sırası: Geliştirici notları</title>
      <dc:creator>Koray Yalçın</dc:creator>
      <pubDate>Thu, 22 Oct 2026 07:26:20 +0000</pubDate>
      <link>https://dev.to/korayyalcin/hubspottan-salesforcea-veri-tasirken-external-id-upsert-ve-iliski-sirasi-gelistirici-notlari-67</link>
      <guid>https://dev.to/korayyalcin/hubspottan-salesforcea-veri-tasirken-external-id-upsert-ve-iliski-sirasi-gelistirici-notlari-67</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Sorun neden "Excel'i dışa aktar, içe aktar" kadar basit değil?
&lt;/h2&gt;

&lt;p&gt;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 &lt;code&gt;__c&lt;/code&gt; sonekli özel alanlara dönüşür.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Temel taş: External ID alanı
&lt;/h2&gt;

&lt;p&gt;Göçün omurgası, kaynak sistemdeki benzersiz kimliğin hedef sisteme taşınmasıdır. HubSpot'ta her nesnenin bir &lt;code&gt;hs_object_id&lt;/code&gt; değeri vardır. Salesforce tarafında her hedef nesneye, örneğin &lt;code&gt;HubSpot_ID__c&lt;/code&gt; adında, &lt;strong&gt;External ID&lt;/strong&gt; ve &lt;strong&gt;Unique&lt;/strong&gt; olarak işaretlenmiş bir metin alanı açarsınız.&lt;/p&gt;

&lt;p&gt;Bu alan üç işe yarar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Upsert:&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;İlişki kurma:&lt;/strong&gt; Bir Opportunity yüklerken Salesforce'un kendi &lt;code&gt;AccountId&lt;/code&gt; değerini bilmenize gerek kalmaz; ilişkiyi Account üzerindeki External ID ile kurabilirsiniz.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Denetim izi:&lt;/strong&gt; Göçten aylar sonra bile bir kaydın HubSpot'taki karşılığını bulabilirsiniz.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Yükleme sırası: Önce ebeveyn, sonra çocuk
&lt;/h2&gt;

&lt;p&gt;İlişkili nesneler bağımlılık sırasıyla yüklenmelidir. Tipik sıra şöyledir:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Kullanıcılar (User) ve sahiplik eşlemesi&lt;/li&gt;
&lt;li&gt;Account (önce ana şirketler, sonra alt şirketler)&lt;/li&gt;
&lt;li&gt;Contact (ve gerekiyorsa Lead)&lt;/li&gt;
&lt;li&gt;Opportunity ve OpportunityContactRole&lt;/li&gt;
&lt;li&gt;Case&lt;/li&gt;
&lt;li&gt;Aktiviteler: Task, Event, notlar ve e-posta kayıtları&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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ı &lt;code&gt;ParentId&lt;/code&gt; boş olarak upsert edin, ardından ikinci bir geçişte yalnızca ebeveyn ilişkisini External ID üzerinden güncelleyin.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Upsert ile ilişki kurmanın örneği
&lt;/h2&gt;

&lt;p&gt;Aşağıdaki örnek, &lt;code&gt;simple-salesforce&lt;/code&gt; 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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;simple_salesforce&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Salesforce&lt;/span&gt;

&lt;span class="n"&gt;sf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Salesforce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SF_USER&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SF_PASS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;security_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SF_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# önce sandbox
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upsert_opportunity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dealname&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;StageName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;STAGE_MAP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dealstage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CloseDate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;closedate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="c1"&gt;# İlişkiyi Salesforce Id yerine Account'un External ID'si ile kur
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Account&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HubSpot_ID__c&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;sf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Opportunity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HubSpot_ID__c/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hs_object_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Burada iki şey önemli. Birincisi &lt;code&gt;STAGE_MAP&lt;/code&gt;: HubSpot pipeline aşamaları ile Salesforce &lt;code&gt;StageName&lt;/code&gt; 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 &lt;code&gt;CurrencyIsoCode&lt;/code&gt; alanını ve kur tablosunu göçten önce kurun.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;Account.HubSpot_ID__c&lt;/code&gt; gibi bir başlıkla verirsiniz ve Salesforce eşleştirmeyi kendisi yapar.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Önce temizlik: Tekrarlı kayıtlar taşınmaz, çözülür
&lt;/h2&gt;

&lt;p&gt;Kirli veriyi yeni sisteme taşımak, sorunu pahalı bir sisteme kopyalamaktır. Göç öncesi temizlik adımında en azından şunları yapın:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;E-posta adreslerini küçük harfe çevirip boşlukları temizleyin, telefonları tek bir biçime (örneğin E.164) getirin.&lt;/li&gt;
&lt;li&gt;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).&lt;/li&gt;
&lt;li&gt;Uzun süredir hareketsiz kayıtları taşımak yerine arşivlemeyi değerlendirin.&lt;/li&gt;
&lt;li&gt;Birleştirilen kayıtların eski kimliklerini bir eşleme tablosunda saklayın; aksi halde bu kimliklere bağlı aktiviteler sahipsiz kalır.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Kuru çalıştırma (dry run) ve sandbox
&lt;/h2&gt;

&lt;p&gt;Ü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ı:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Her nesne için kaynak kayıt sayısı, başarılı ve başarısız yükleme sayısı&lt;/li&gt;
&lt;li&gt;Hata kodlarına göre gruplanmış başarısızlıklar (zorunlu alan eksik, picklist uyuşmazlığı, doğrulama kuralı gibi)&lt;/li&gt;
&lt;li&gt;Rastgele seçilmiş kayıtlarda alan alan karşılaştırma&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Kesin geçiş ve delta aktarımı
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ana yükleme:&lt;/strong&gt; Geçişten günler önce tüm geçmiş veri upsert edilir.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delta:&lt;/strong&gt; 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.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Doğrulama: "Yüklendi" ile "doğru" aynı şey değil
&lt;/h2&gt;

&lt;p&gt;Göçten sonra şu kontrolleri otomatikleştirin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sayım eşitliği:&lt;/strong&gt; Nesne bazında kaynak ve hedef kayıt sayıları (arşivlenenler ve birleştirilenler düşüldükten sonra) tutuyor mu?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;İlişki bütünlüğü:&lt;/strong&gt; Account'u boş kalan Opportunity var mı? Sahipsiz aktivite var mı?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Toplam değerler:&lt;/strong&gt; Aşama bazında toplam fırsat tutarı iki sistemde aynı mı?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Örneklem karşılaştırması:&lt;/strong&gt; Rastgele kayıtlarda alan değerleri birebir eşleşiyor mu?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Kod kadar önemli: Kullanıcı benimsemesi
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kısa kontrol listesi
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Her hedef nesnede &lt;code&gt;HubSpot_ID__c&lt;/code&gt; External ID ve Unique alanı&lt;/li&gt;
&lt;li&gt;Aşama, owner ve picklist eşleme tabloları, eşleşmeyen değer raporu&lt;/li&gt;
&lt;li&gt;Temizlik ve birleştirme kuralları, eski kimlik eşleme tablosu&lt;/li&gt;
&lt;li&gt;Bağımlılık sırasına göre yükleme, Account hiyerarşisi için iki geçiş&lt;/li&gt;
&lt;li&gt;Sandbox provası ve tekrarlanabilir hata raporu&lt;/li&gt;
&lt;li&gt;Göç sırasında otomasyonları atlayan bayrak&lt;/li&gt;
&lt;li&gt;Ana yükleme, zaman damgası ve delta&lt;/li&gt;
&lt;li&gt;Entegrasyon uç noktalarının yeniden yönlendirilmesi&lt;/li&gt;
&lt;li&gt;Sayım, ilişki ve toplam değer doğrulamaları&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SSS
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;External ID kullanmadan göç yapılabilir mi?&lt;/strong&gt;&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lead mi Contact mı? HubSpot kişilerini nereye taşımalıyım?&lt;/strong&gt;&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aktivite geçmişini taşımak şart mı?&lt;/strong&gt;&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Göç ne kadar sürer?&lt;/strong&gt;&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bulk API mi, kayıt kayıt REST çağrısı mı?&lt;/strong&gt;&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Göç sırasında satış ekibi çalışmaya devam edebilir mi?&lt;/strong&gt;&lt;br&gt;
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.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;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.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crm</category>
      <category>salesforce</category>
      <category>dataengineering</category>
      <category>api</category>
    </item>
    <item>
      <title>Pazarlama ekibi için RAG kurarken parçalama ve metadata: Sahada öğrenilen dersler</title>
      <dc:creator>Koray Yalçın</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:09:55 +0000</pubDate>
      <link>https://dev.to/korayyalcin/pazarlama-ekibi-icin-rag-kurarken-parcalama-ve-metadata-sahada-ogrenilen-dersler-37eo</link>
      <guid>https://dev.to/korayyalcin/pazarlama-ekibi-icin-rag-kurarken-parcalama-ve-metadata-sahada-ogrenilen-dersler-37eo</guid>
      <description>&lt;p&gt;Pazarlama ekiplerine yapay zekâ asistanı kurma talebi genelde aynı cümleyle gelir: "ChatGPT gibi olsun ama bizim ürünlerimizi bilsin." İlk prototip birkaç saatte çıkar, sonra sorun başlar. Asistan geçen yılın fiyat listesinden teklif yazar, sunulmayan bir özelliği vaat eder ya da iki farklı ürünün şartlarını tek cevapta karıştırır.&lt;/p&gt;

&lt;p&gt;Koray Yalçın'ın pazarlama ekipleri için RAG mimarisini anlattığı yazısı, bu riskin neden ticari bir risk olduğunu ve RAG'ın (Retrieval-Augmented Generation, yani modelin cevap üretmeden önce şirketin doğrulanmış dokümanlarından ilgili pasajları getirmesi) bunu nasıl azalttığını üst seviyeden anlatıyor. Yazıdaki en önemli tespitlerden biri şu: sistemin başarısı modelin büyüklüğünden çok bilginin nasıl parçalandığına bağlı. Ben bu yazıda o cümleyi geliştirici tarafından açmak istiyorum.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sabit boyutlu parçalama neden pazarlama dokümanında kırılır
&lt;/h2&gt;

&lt;p&gt;Metni 500 kelimelik bloklara bölmek kolaydır ve teknik dokümantasyonda çoğu zaman yeterlidir. Pazarlama dokümanları ise farklıdır: bir kampanya koşulları sayfasında "geçerlilik tarihi", "kapsam dışı ürünler" ve "fiyat" birbirine bağlı üç küçük bölümdür. Bölme noktası tam ortaya denk gelirse, getirilen pasajda fiyat vardır ama hangi döneme ait olduğu yoktur. Model de boşluğu kendinden emin bir dille doldurur.&lt;/p&gt;

&lt;p&gt;Bu yüzden başlıkları, madde listelerini ve tabloları bütün tutan anlamsal parçalamayı varsayılan yapmak daha güvenlidir.&lt;/p&gt;

&lt;h2&gt;
  
  
  Her parçaya kimliğini taşıtın
&lt;/h2&gt;

&lt;p&gt;Parçalamanın ikinci yarısı metadata'dır. Bir pasajın tek başına anlamlı olması için hangi dokümandan, hangi başlıktan, hangi tarihte geldiğini bilmesi gerekir. Basit bir yapı şöyle olabilir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Chunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;doc_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;section_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# ["Fiyatlandırma", "Kurumsal Paket"]
&lt;/span&gt;    &lt;span class="n"&gt;doc_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;             &lt;span class="c1"&gt;# "fiyat_listesi", "vaka_analizi", "marka_kilavuzu"
&lt;/span&gt;    &lt;span class="n"&gt;valid_from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;
    &lt;span class="n"&gt;valid_until&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;                &lt;span class="c1"&gt;# dokümandan sorumlu ekip
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;embed_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Başlık yolunu metnin başına eklemek, kısa pasajların
&lt;/span&gt;    &lt;span class="c1"&gt;# bağlamını embedding'e de taşır.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &amp;gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;section_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Burada iki küçük karar büyük fark yaratıyor. Birincisi, başlık yolunu embedding girdisine eklemek: "Kurumsal Paket" altındaki iki cümlelik bir pasaj, bağlamı olmadan aranınca neredeyse hiçbir soruya eşleşmez. İkincisi, geçerlilik tarihleri: eski fiyat listesini silmek yerine &lt;code&gt;valid_until&lt;/code&gt; ile işaretleyip sorgu anında filtrelemek hem geçmişe dönük soruları cevaplamayı sağlar hem de güncel cevaplarda eski bilgiyi dışarıda bırakır.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getirmeden önce filtreleyin
&lt;/h2&gt;

&lt;p&gt;Vektör araması "en benzer" olanı bulur, "en doğru" olanı değil. Fiyat sorulan bir soruda vaka analizinden gelen bir pasaj semantik olarak çok benzer olabilir. Pratikte iyi çalışan yaklaşım, soruyu önce basit bir sınıflandırmadan geçirip &lt;code&gt;doc_type&lt;/code&gt; ve tarih filtresini aramaya eklemek, sonra kalan adaylar içinde benzerlik sıralaması yapmaktır. Pinecone, Qdrant ve Milvus gibi vektör veritabanlarının hepsi metadata filtresini destekliyor; bu özelliği kullanmamak en sık gördüğüm hatalardan biri.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kaynak göstermeyen cevabı reddedin
&lt;/h2&gt;

&lt;p&gt;Son katman üretim adımı. Modele "yalnızca verilen pasajlardan cevap ver, her iddianın yanına &lt;code&gt;doc_id&lt;/code&gt; yaz, pasajlarda yoksa bilmediğini söyle" talimatını vermek yetmez; bunu kodda da doğrulamak gerekir. Cevapta geçen kaynak kimlikleri getirilen parçalar arasında değilse ya da hiç kaynak yoksa cevap kullanıcıya gitmeden işaretlenmeli. Satış ekibinin müşteri karşısında kullandığı bir asistanda "bilmiyorum" demek, yanlış bir taahhütten her zaman ucuzdur.&lt;/p&gt;

&lt;h2&gt;
  
  
  Küçük bir kontrol listesi
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Parçalama başlık ve tablo sınırlarına saygı duyuyor mu?&lt;/li&gt;
&lt;li&gt;Her parçada doküman türü, başlık yolu ve geçerlilik tarihi var mı?&lt;/li&gt;
&lt;li&gt;Arama öncesi metadata filtresi uygulanıyor mu?&lt;/li&gt;
&lt;li&gt;Cevaptaki her iddia getirilen bir parçaya bağlanabiliyor mu?&lt;/li&gt;
&lt;li&gt;Dokümanın sahibi belli mi, güncellenince yeniden indeksleniyor mu?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mimarinin bütününü, doküman havuzundan üretim adımına kadar akışı ve pazarlama tarafındaki kullanım alanlarını (teklif şartnamesi yanıtlama, marka dili kontrolü, satış temsilcisi asistanı) merak edenler için kaynak yazı: &lt;a href="https://www.korayyalcin.org/yayinlar-arastirmalar/pazarlama-ekipleri-icin-rag-ve-kurumsal-bilgi-tabani-mimarisi/" rel="noopener noreferrer"&gt;Pazarlama Ekipleri İçin RAG ve Kurumsal Bilgi Tabanı Mimarisi&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>llm</category>
      <category>python</category>
    </item>
    <item>
      <title>Pazarlama olaylarını sunucuda toplamak: Bir geliştiricinin kontrol listesi</title>
      <dc:creator>Koray Yalçın</dc:creator>
      <pubDate>Wed, 21 Oct 2026 11:02:12 +0000</pubDate>
      <link>https://dev.to/korayyalcin/pazarlama-olaylarini-sunucuda-toplamak-bir-gelistiricinin-kontrol-listesi-3831</link>
      <guid>https://dev.to/korayyalcin/pazarlama-olaylarini-sunucuda-toplamak-bir-gelistiricinin-kontrol-listesi-3831</guid>
      <description>&lt;p&gt;Pazarlama ekibi "dönüşümler panelde düşük görünüyor" dediğinde, sorun çoğu zaman kampanyada değil, verinin yolculuğundadır. Tarayıcıda çalışan izleme kodları reklam engelleyicilere, Safari'nin çerez kısıtlamalarına ve onay bannerlarına takılır. Sonuçta reklam panelindeki sayı ile CRM'deki gerçek satış birbirinden uzaklaşır.&lt;/p&gt;

&lt;p&gt;Koray Yalçın'ın GA4, PostHog ve CDP üzerine yazdığı mimari yazısı bu tabloyu üst seviyeden anlatıyor: birinci taraf veri toplama, sunucu taraflı etiketleme, merkezi veri ambarı ve Reverse ETL ile verinin CRM'e ve reklam platformlarına geri yazılması. Ben bu yazıda aynı mimariye geliştirici koltuğundan bakmak ve işi yaparken sık atlanan teknik noktaları bir kontrol listesine dökmek istiyorum.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Olayı bir kez tanımla, her yere aynı adla gönder
&lt;/h2&gt;

&lt;p&gt;Sunucu taraflı kurulumda en büyük kazanç, tek bir olay şemasından GA4'e, ürün analitiğine ve reklam platformlarına dağıtım yapabilmektir. Bunun çalışması için olay adlarının ve alanlarının baştan sabitlenmesi gerekir. &lt;code&gt;form_submit&lt;/code&gt;, &lt;code&gt;formSubmitted&lt;/code&gt; ve &lt;code&gt;lead&lt;/code&gt; aynı şeyi anlatıyorsa, üç ayrı rapor ve üç ayrı tartışma doğar.&lt;/p&gt;

&lt;p&gt;Pratik öneri: olay sözlüğünü kodla birlikte versiyonlayın. Basit bir JSON şeması ya da TypeScript tipi bile, yeni bir alan eklendiğinde kimin neyi bozduğunu görmenizi sağlar.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Kişisel veriyi tarayıcıdan dışarı çıkarmayın
&lt;/h2&gt;

&lt;p&gt;E-posta ve telefon gibi kişisel verileri doğrudan üçüncü taraf betiklere vermek yerine, kendi sunucunuza alın, normalize edin ve hash'leyin. Burada en sık hata normalizasyonu atlamaktır: büyük harf, baştaki boşluk ya da ülke kodu farkı, aynı kişiyi farklı bir hash'e dönüştürür ve eşleşme oranı sessizce düşer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normalize_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normalize_phone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_cc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;90&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;digits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isdigit&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;digits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;default_cc&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;digits&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu adım aynı zamanda KVKK ve GDPR açısından da savunulabilir bir sınır çizer: ham veri sizin altyapınızda kalır, dışarıya yalnızca gerekli ve dönüştürülmüş alanlar çıkar. Hangi alanın hangi hedefe gittiğini bir tabloda belgelemek, denetim günü işinizi çok kolaylaştırır.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Çift sayımı event_id ile önleyin
&lt;/h2&gt;

&lt;p&gt;Geçiş döneminde hem tarayıcı pikseli hem sunucu çağrısı aynı dönüşümü gönderir. Reklam platformlarının çoğu bu ikisini ortak bir olay kimliğiyle eşleştirip tekilleştirir. Kimliği istemcide üretip hem piksele hem sunucu isteğine aynı değeri taşımazsanız, dönüşümler bir anda iki katına çıkar ve kimse bunun bir hata olduğunu hemen fark etmez.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Alt alan adını ve gecikmeyi ciddiye alın
&lt;/h2&gt;

&lt;p&gt;Toplayıcıyı &lt;code&gt;data.alanadiniz.com&lt;/code&gt; gibi kendi alt alan adınızda çalıştırmak, isteklerin birinci taraf olarak işlenmesine yardım eder. Ama bu sunucu artık sizin üretim altyapınızın parçasıdır: izleme, alarm ve hata günlüğü ister. Toplayıcı çöktüğünde veri sessizce kaybolur; sağlık kontrolü ve kuyruk (retry) mekanizması olmadan kurulan bir sunucu taraflı izleme, tarayıcı tarafındakinden daha kırılgan olabilir.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Ürün olaylarını pazarlama verisiyle aynı kimlikte buluşturun
&lt;/h2&gt;

&lt;p&gt;GA4 "ziyaretçi nereden geldi" sorusuna, PostHog gibi ürün analitiği araçları ise "geldikten sonra ne yaptı" sorusuna cevap verir. İkisini anlamlı kılan şey ortak bir kullanıcı ya da hesap kimliğidir. Kayıt anında anonim kimliği gerçek kullanıcı kimliğiyle birleştirmeyi (identify) unutmak, huniyi ortadan ikiye böler.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Veri ambarı tek doğruluk kaynağı olsun
&lt;/h2&gt;

&lt;p&gt;BigQuery ya da Snowflake gibi bir ambarda modellenen veri, Reverse ETL araçlarıyla CRM'e ve reklam kitlelerine geri yazılabilir. Geliştirici açısından kritik nokta, bu senkronizasyonların da kod gibi yönetilmesidir: SQL modelleri versiyon kontrolünde, test edilmiş ve sahibi belli olmalı.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kapanış
&lt;/h2&gt;

&lt;p&gt;Sunucu taraflı izleme bir etiket yöneticisi ayarı değil, küçük bir veri ürünüdür. Olay sözlüğü, hash normalizasyonu, tekilleştirme ve gözlemlenebilirlik doğru kurulduğunda pazarlama ile finansın gördüğü sayılar birbirine yaklaşır.&lt;/p&gt;

&lt;p&gt;Mimarinin tamamını, GA4 ve PostHog karşılaştırmasını ve Composable CDP yaklaşımını kaynak yazıda okuyabilirsiniz: &lt;a href="https://www.korayyalcin.org/yayinlar-arastirmalar/modern-pazarlama-ve-buyume-analitigi-ga4-posthog-cdp-veri-mimarisi/" rel="noopener noreferrer"&gt;Modern Pazarlama ve Büyüme Analitiği: GA4, PostHog ve CDP Veri Mimarisi&lt;/a&gt;&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>webdev</category>
      <category>dataengineering</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
