Veri kalitesi
dataLayer sözleşmesi: her sürümde bozulan ölçümlemeyi nasıl durdurursunuz?
Release sonrası sessizce bozulan GA4 ölçümlemesini durdurmak için geliştirici ve analist arasında yazılı, versiyonlu ve otomatik test edilen bir dataLayer sözleşmesi.
Her release sonrası bozulan ölçümlemeyi durdurmanın yolu, dataLayer’ı bir yan ürün değil, geliştirici ile analist arasında yazılı bir sözleşme olarak ele almaktır. Bu sözleşme hangi olayın hangi parametrelerle, hangi tiplerde ve hangi koşulda gönderileceğini tanımlar, versiyonlanır ve hem QA aşamasında hem de BigQuery üzerinde otomatik olarak test edilir. Sözleşme yoksa ölçümleme her deploy’da şansa kalır; sözleşme varsa kırılma, veri raporlara ulaşmadan yakalanır.
Ölçümleme neden sessizce bozulur?
Bir ön yüz ekibi sepet bileşenini yeniden yazar. Butonun sınıf adı değişir, fiyat artık string olarak gelir, items dizisindeki item_id alanı ürün kodu yerine veritabanı kimliğini taşımaya başlar. Hiçbir test kırılmaz, çünkü uygulamanın kendisi çalışıyordur. Kullanıcı ürünü sepete ekleyebilir, ödeme yapabilir. Bozulan tek şey analitiktir ve bunu kimse o gün fark etmez.
Sorunu iki hafta sonra pazarlama ekibi fark eder: kampanya raporunda gelir düşmüş görünür, ürün bazlı dönüşüm oranları anlamsızlaşmıştır. Analist GTM önizlemesini açar, olayların hâlâ geldiğini görür ve bir süre yanlış yerde arar. Sonunda fiyatın "1.299,90" biçiminde bir metin olarak geldiğini, GA4’ün bunu sayıya çeviremediğini bulur. O iki haftanın verisi geri gelmez.
Bu senaryonun kökü teknik değil, organizasyoneldir. Geliştirici için dataLayer, bir analistin bir zamanlar istediği ve Jira’da kaybolmuş bir görevdir. Analist içinse sitenin içinden gelen, üzerinde kontrolü olmayan bir veri akışıdır. Arada kimsenin sahiplenmediği bir arayüz vardır ve sahipsiz arayüzler her değişiklikte kırılır.
Yazılım dünyası bu problemi API’ler için çoktan çözdü. Bir backend ekibi mobil uygulamanın kullandığı bir endpoint’in yanıt yapısını habersiz değiştirmez, çünkü arada OpenAPI tanımı, sözleşme testleri ve versiyon numarası vardır. dataLayer da aynı şeydir: site ile ölçüm katmanı arasındaki bir API. Ona da aynı disiplini uygulamak gerekir.
Sözleşmenin içinde ne olmalı?
Sözleşme, herkesin okuyabildiği tek bir kaynaktır. Confluence sayfası, Google Sheets tablosu ya da repoda duran bir YAML dosyası olabilir; biçim ikincildir. Önemli olan, olayların ve parametrelerin makine tarafından doğrulanabilecek kadar kesin tanımlanmasıdır. Her olay için en az şu bilgiler yazılı olmalıdır:
- Olay adı ve tetiklenme koşulu: tam olarak hangi kullanıcı eylemi, hangi sayfada, kaç kez.
- Parametre adları, veri tipleri ve izin verilen değer aralıkları ya da kalıpları.
- Zorunlu ve opsiyonel alanların ayrımı, zorunlu alan eksikse ne olacağı.
itemsdizisinin yapısı ve dizideki her öğe için zorunlu alanlar.- Olayın hangi GA4 raporunu, hangi BigQuery tablosunu ya da hangi reklam dönüşümünü beslediği.
- Sözleşme versiyonu ve bu olayın en son hangi versiyonda değiştiği.
- Olayın sahibi: soruları kime soracağınızı gösteren tek bir isim.
Son madde genellikle atlanır ve en pahalıya patlayan madde odur. Bir olayın sahibi yoksa, o olay bozulduğunda kimse sorumlu değildir.
İsimlendirme ve tipler
GA4 önerilen olayları kullanıyorsanız isimlendirme kararının büyük bölümü sizin yerinize verilmiştir. add_to_cart, begin_checkout, purchase gibi olay adlarını ve currency, value, transaction_id gibi parametreleri Google’ın tanımladığı biçimde kullanın. Bunları Türkçeleştirmek ya da kendi varyantlarınızı üretmek, hazır raporlardan ve reklam entegrasyonlarından vazgeçmek demektir. Referans olarak GA4 e-ticaret ölçümleme dokümantasyonu sözleşmenin omurgası olmalıdır.
Özel olaylar için tek bir kural koyun ve istisnasız uygulayın: küçük harf, alt çizgi ile ayrılmış kelimeler, fiil ile başlayan yapı. filter_apply, store_locator_search gibi. FilterApplied, filter-apply ve filterApply aynı sitede yan yana yaşamaya başladığında GA4 bunları üç farklı olay olarak sayar.
Tipler konusunda en sık hata sayıların metin olarak gönderilmesidir. value ve price her zaman ondalık ayırıcısı nokta olan bir sayı olmalı, para birimi sembolü ya da binlik ayırıcı içermemelidir. quantity tam sayıdır. currency üç harfli ISO 4217 kodudur, yani TRY, tl değil. Türkçe yerelleştirme kullanan sitelerde fiyat genellikle ekrandaki biçimlendirilmiş metinden okunduğu için bu hata beklenenden çok daha yaygındır. Değeri ekrandan değil, uygulamanın state’inden alın.
E-ticaret items dizisi
items dizisi sözleşmenin en kırılgan parçasıdır, çünkü birden fazla ekip tarafından birden fazla yerde doldurulur: ürün listesi, ürün detay sayfası, sepet, ödeme adımları. Her biri aynı ürünü farklı bir kaynaktan okuyabilir. Sözleşmede item_id alanının neyi temsil ettiğini açıkça yazın: SKU mu, varyant kodu mu, ürün grubu mu? Bu karar bir kez verilmeli ve tüm olaylarda aynı olmalıdır; aksi halde ürün listesinden sepete geçiş hunisi hiçbir zaman eşleşmez.
Aşağıdaki örnek, sözleşmeye uygun bir add_to_cart gönderimini gösteriyor. Önceki e-ticaret nesnesini temizlemek için yapılan ecommerce: null itmesi, Google’ın dataLayer rehberinde de önerilen bir uygulamadır.
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: 'add_to_cart',
schema_version: '2.3.0',
ecommerce: {
currency: 'TRY',
value: 1299.9,
items: [
{ item_id: 'SKU-48213', item_name: 'Koşu Ayakkabısı', price: 1299.9, quantity: 1 }
]
}
});
schema_version alanı GA4 için zorunlu değildir, ama sizin için çok değerlidir. Bir olayın hangi sözleşme versiyonuna göre üretildiğini veri içinde taşımak, bir kırılmayı geriye doğru izlemeyi dakikalara indirir.
Versiyonlama: değişiklik yasak değil, habersiz değişiklik yasak
Sözleşme bir kez yazılıp dondurulan bir belge değildir. Ürün değişir, yeni olaylar gerekir, eski parametreler anlamını yitirir. Amaç değişikliği engellemek değil, görünür kılmaktır. Bunun için semantik versiyonlama iyi çalışır.
Yeni bir opsiyonel parametre eklemek küçük sürümdür: 2.3.0’dan 2.4.0’a. Mevcut tüketiciler etkilenmez. Bir parametrenin adını değiştirmek, tipini değiştirmek, zorunlu bir alanı kaldırmak ya da item_id alanının anlamını değiştirmek ise büyük sürümdür: 2.x’ten 3.0.0’a. Büyük sürüm, GTM tarafında değişken ve tetikleyici güncellemesi, BigQuery tarafında sorgu ve dbt modeli güncellemesi, bazen de reklam platformlarındaki dönüşüm tanımlarının gözden geçirilmesi anlamına gelir.
Pratik kural şudur: büyük sürüm değişikliği, analist onayı olmadan birleştirilemez. Sözleşme dosyası repodaysa bunu CODEOWNERS ile zorunlu kılmak birkaç dakikalık iştir. Sözleşme bir tabloda duruyorsa, en azından değişiklik geçmişinin bir changelog bölümünde tutulması gerekir.
Ölçümleme bir özellik değil, bir arayüzdür. Arayüzü değiştiren ekip, onu kullanan ekibe haber vermekle yükümlüdür.
QA aşamasında otomatik kontrol
Sözleşmenin asıl faydası, makine tarafından okunabilir olduğunda ortaya çıkar. Olay tanımlarını JSON Schema olarak yazdığınızda, aynı dosyayı hem belge olarak hem de test girdisi olarak kullanabilirsiniz.
En basit kurulum şöyledir: uçtan uca testlerinizde, örneğin Playwright ya da Cypress ile, kritik akışlar zaten test ediliyordur. Ürün sayfasına git, sepete ekle, ödemeye geç. Bu testlere window.dataLayer dizisini okuyan bir adım ekleyin. Test sonunda dizideki her olayı, olay adına karşılık gelen şemaya göre doğrulayın. Eksik zorunlu alan, yanlış tip ya da tanımsız bir olay adı bulunursa test kırılsın.
Bu yaklaşımın güzelliği, analistin QA sürecine manuel olarak dahil olmasını gerektirmemesidir. Geliştirici fiyatı string’e çevirdiği anda pipeline kırmızıya döner ve hata mesajı neyin yanlış olduğunu açıkça söyler: ecommerce.items[0].price beklenen tip number, gelen tip string. Bu mesajı okuyan geliştiricinin analistle toplantı yapmasına gerek yoktur.
Uçtan uca testlerin kapsamadığı akışlar için staging ortamında GTM önizleme modu ve tarayıcı eklentileriyle yapılan manuel kontrol hâlâ gereklidir. Ama manuel kontrolü yalnızca yeni ya da değişen olaylar için yapın. Mevcut olayların regresyonunu otomatik testlere bırakın; insan gözü tekrarlanan kontrollerde yorulur.
Consent Mode ve server-side tagging kullanıyorsanız bir katman daha ekleyin. dataLayer doğru olsa bile, sunucu tarafındaki container bir parametreyi dönüştürürken bozabilir. Server-side container’ın çıktısını da, en azından kritik olaylar için, aynı şemayla doğrulamak iyi bir yatırımdır.
Canlıda BigQuery ile doğrulama
QA ne kadar iyi olursa olsun, bazı hatalar yalnızca canlı trafikte ortaya çıkar: belirli bir tarayıcı, belirli bir ödeme yöntemi, belirli bir kampanya sayfası. Bu yüzden ikinci savunma hattı, GA4’ün BigQuery export’u üzerinde çalışan günlük bir kalite sorgusudur.
Aşağıdaki sorgu son on dört günün purchase olaylarını tarar ve her gün için zorunlu alanları eksik ya da hatalı olan olayları sayar.
WITH purchases AS (
SELECT
PARSE_DATE('%Y%m%d', event_date) AS gun,
ecommerce.transaction_id AS transaction_id,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'currency') AS currency,
ecommerce.purchase_revenue AS revenue,
items
FROM `project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 14 DAY))
AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
AND event_name = 'purchase'
)
SELECT
gun,
COUNT(*) AS purchase_sayisi,
COUNTIF(transaction_id IS NULL OR transaction_id IN ('', '(not set)')) AS eksik_transaction_id,
COUNTIF(currency IS NULL OR NOT REGEXP_CONTAINS(currency, r'^[A-Z]{3}$')) AS hatali_currency,
COUNTIF(revenue IS NULL OR revenue <= 0) AS hatali_revenue,
COUNTIF(ARRAY_LENGTH(items) = 0) AS bos_items,
COUNTIF(EXISTS(
SELECT 1 FROM UNNEST(items) AS i
WHERE i.item_id IS NULL OR i.price IS NULL OR i.quantity IS NULL OR i.quantity <= 0
)) AS hatali_item
FROM purchases
GROUP BY gun
ORDER BY gun DESC;
Bu sorguyu zamanlanmış sorgu olarak her sabah çalıştırın ve sonucu bir tabloya yazın. Hatalı oranı belirlediğiniz eşiği aştığında, örneğin günlük satın almaların yüzde ikisini geçtiğinde, Slack ya da e-posta ile uyarı gönderin. Eşiği sıfır yapmayın; her sitede reklam engelleyiciler, yarım kalan sayfa yüklemeleri ve botlar yüzünden küçük bir gürültü vardır. Önemli olan ani sıçramalardır.
Sorguya bir kırılım daha eklemek çok işe yarar: schema_version parametresine göre gruplamak. Hatalı olayların tamamı yeni versiyonla geliyorsa, sorunun son release ile geldiğini soru sormadan bilirsiniz. Aynı mantıkla add_to_cart ve begin_checkout için de benzer sorgular yazın, ama önce gelire doğrudan dokunan olayla başlayın.
Sahiplik ve release kontrol listesi
Otomasyon, sahipliğin yerini tutmaz. Testler kırıldığında düzeltmeyi kimin yapacağı, uyarı geldiğinde kimin bakacağı önceden belli olmalıdır. İşleyen bir model şöyledir: sözleşme dosyasının sahibi analitik ekibinden bir kişidir, uygulamanın sahibi ise olayı tetikleyen bileşeni geliştiren ürün ekibidir. BigQuery uyarıları analitik ekibine gider, analitik ekibi kök nedeni bulup ilgili ürün ekibine iletir.
Her release öncesinde aşağıdaki listeyi pull request şablonuna ekleyin. Liste kısa olmalı ki gerçekten okunsun.
- Bu değişiklik dataLayer’a gönderilen herhangi bir olayı, parametreyi ya da tetiklenme koşulunu etkiliyor mu?
- Etkiliyorsa sözleşme dosyası aynı pull request içinde güncellendi mi ve versiyon numarası doğru artırıldı mı?
- Büyük sürüm değişikliğiyse analitik ekibinden onay alındı mı?
- Uçtan uca testlerdeki dataLayer şema doğrulaması başarılı mı?
- GTM tarafında gerekli değişken, tetikleyici ya da etiket güncellemesi hazırlandı mı ve yayınlanma zamanı release ile eşleşiyor mu?
- Release sonrası ilk günün BigQuery kalite sorgusu sonuçlarını kim kontrol edecek?
Beşinci madde özellikle önemlidir. GTM container’ı site kodundan bağımsız yayınlandığı için, sitenin yeni versiyonu canlıya çıktığında GTM hâlâ eski yapıyı bekliyor olabilir. İkisinin aynı saat içinde yayınlanması planlanmalıdır.
Küçük başlayın, ama yazılı başlayın
Dağınık bir kurulumda bütün olayları bir haftada sözleşmeye bağlamaya çalışmak, sürdürülemez bir belgeyle sonuçlanır. Gelire dokunan üç olayla başlayın, bunların bugünkü halini dürüstçe belgeleyin, BigQuery sorgusunu kurun ve ilk uyarının gelmesini bekleyin. İlk uyarı, sözleşmenin neden gerekli olduğunu ekibe herhangi bir sunumdan daha iyi anlatır.
Sonraki adımlar doğal olarak gelir: şemayı JSON Schema’ya dönüştürmek, uçtan uca testlere bağlamak, pull request şablonunu güncellemek. Her adım bir öncekinin üzerine kurulur ve hiçbiri büyük bir bütçe gerektirmez. Gerektirdiği şey, dataLayer’ın bir kod parçası değil, iki ekip arasında verilmiş bir söz olduğunu kabul etmektir.
Sık sorulan sorular
- dataLayer sözleşmesi için özel bir araç satın almak gerekir mi?
- Hayır. Başlangıç için repoda duran bir JSON Schema dosyası, CI içinde çalışan bir doğrulama betiği ve BigQuery üzerinde zamanlanmış bir sorgu yeterlidir. Ücretli araçlar ölçek büyüdüğünde işi kolaylaştırır ama sözleşmenin yerini tutmaz.
- Sözleşmeyi kim yazmalı, analist mi geliştirici mi?
- İlk taslağı ölçüm ihtiyacını bilen analist yazar, geliştirici teknik olarak uygulanabilirliğini onaylar. Dosyanın sahibi tek bir kişi olmalı, değişiklikler ise her iki tarafın onayıyla birleştirilmelidir.
- Mevcut ve dağınık bir kurulumda sözleşmeye nereden başlanır?
- Önce gelire dokunan olaylarla başlayın: purchase, begin_checkout ve add_to_cart. Bu üç olayın bugünkü halini belgeleyin, BigQuery kontrolünü kurun, sonra diğer olayları sprint sprint ekleyin.