Yanlış markaya kayıt yapılmasını engellemek

Bir sekme eski markayı gösterirken sunucu başka markaya kayıt yapabiliyordu. Bu hatayı engelleyen koruma ve 11 yönetim ekranındaki iyileştirmeler.

Farklı markalara ait iki panel sekmesi, biri marka uyuşmazlığı penceresiyle kapalı, yanında boş durumları olan dashboard ızgarası

Biri admin platformundan marka değiştirdi, panele geçti ve çalışmaya başladı. Header yeni markayı gösteriyordu. Sayfadaki verinin çoğu ise eski markaya aitti.

Kaydettiği her şey iki markaya bölünebilirdi. Bu tür bir hata, bir marka kendi kampanyasında başka bir markanın içeriğini bulana kadar sessiz kalır.

Eylül 2026'da bunun önüne bir koruma koydum. Hikâyenin geri kalanı, Ekim 2023'ten beri 847 commit (kod deposuna kaydedilen değişiklik paketi) attığım Revotas pazarlama panelinde, büyük ve ortak bir kod tabanında gündelik çalışmanın neye benzediği.

"Hangi marka" sorusunun iki cevabı

Panel yılların geçmişi olan bir React 18 uygulaması: 2022 sonundan beri bir düzineden fazla kişiden 6.000'i aşkın commit. Backend (uygulamanın sunucuda çalışan bölümü) benim değil ve eski endpoint'lerin (bir serviste istek gönderilen belirli adres) sizi nasıl tanıdığını değiştiremiyorum.

Sorun da tam buydu. Ekrandaki marka localStorage içindeki bir Customer kaydından geliyor. Veri endpoint'lerinin çoğu bunu okumuyor. Markayı sunucu oturum çerezinden çözüyorlar.

Genelde ikisi aynı şeyi söylüyor. İki durumda ayrışıyorlar:

  • Başka bir sekme. Ekip çoğu zaman birkaç markayı aynı anda açık tutuyor. B sekmesinde markayı değiştirince ortak localStorage kaydı değişiyor, ama A sekmesi açıldığı markayı göstermeye devam ediyor.
  • Admin köprüsü. Mayıs'ta yeni admin platformundan panele bir çalışma alanı aktaran köprüyü yazdım. URL'deki marka bağlamını çözüyor, gerekli alanların olup olmadığını kontrol ediyor, Customer kaydını yazıyor ve bağlamı URL'den silerek ana sayfaya yönlendiriyor. Ekranı güncelliyordu. Sunucu oturumuna dokunmuyordu; oturum önceki markada kalıyordu.

O yılın başında müşteri kayıtlarını admin platformuna taşırken, taşımanın yanlış tenant'a (aynı sistemde verileri ayrı tutulan müşteri alanı) girişlere yol açmadığını SQL ile kontrol etmiştim. Bu, benzer bir belirtiye giden başka bir yoldu ve benim kendi köprümdeydi.

Önce köprüyü düzelt, sonra her şeyi koru

Köprü tarafı iki satırdı. Yeni markayı yazmadan önce localStorage ve sessionStorage temizleniyor; önceki markadan kalan hiçbir cache (yeniden kullanmak için saklanan geçici veri, yani önbellek) geçişten sağ çıkmıyor.

Bu sunucu oturumunu düzeltmiyor ve iki açık sekme için de hiçbir şey yapmıyor. Asıl düzeltme panelin her sayfasında çalışan bir koruma: bir hook (belirli bir olayda çalışan bağlantı işlevi) ve tek bir diyalog bileşeni, toplamda yaklaşık 180 satır.

Hook, sekmenin hangi markayla açıldığını hatırlıyor ve iki kontrol yapıyor:

  1. Sekme kontrolü. Kayıtlı marka, bu sekmenin açıldığı markayla hâlâ aynı mı? Bir sekme localStorage'a yazdığında diğer bütün sekmelerde tetiklenen storage event'ini (bir işlemin gerçekleştiğini bildiren olay) dinliyor. Yani B sekmesindeki değişiklik A'ya anında ulaşıyor.
  2. Oturum kontrolü. Sunucu da aynı fikirde mi? Var olan oturum kontrol endpoint'ini çağırıp döndürdüğü marka id'sini kayıtlı olanla karşılaştırıyor. Sayfa yüklenince ve sekme her görünür olduğunda tekrar çalışıyor.

Sekmeyi durdur ve sor

Akla gelen ilk çözüm sessizce senkronlamak: sunucu X markası diyorsa ekranı X'e çevir.

Kod tabanında bunun bir sürümü zaten vardı, layout (öğelerin ekrandaki yerleşimi) dosyasında yorum satırı olarak. İki markayı karşılaştırıp oturumdaki marka olarak sessizce yeniden giriş yapıyordu. Aynı commit'te sildim.

Sessiz bir geçiş hatanın kendisinden daha kötü. Biri A markası için bir kampanyanın yarısındayken sayfa altından B markasına dönerse, bir sonraki kaydı oturum için "doğru", o kişi için yanlış olur.

Bu yüzden uyuşmazlıkta koruma sekmeyi kapatılamayan bir diyalogla örtüyor: sabit arka plan, kapatma düğmesi yok, Escape kapalı. Sekmenin hangi markayı gösterdiğini, diğer tarafın hangi markayı tuttuğunu ve yanlış markaya hiçbir şey kaydedilmesin ya da gönderilmesin diye işlemlerin durdurulduğunu söylüyor. Sonra tam olarak anlamlı olan seçenekleri veriyor:

  • Sekme uyuşmazlığı: sayfayı yenile, güncel marka gelsin.
  • Oturum uyuşmazlığı: oturumdaki markayla devam et (kayıtlı kaydı yeniden yaz ve yenile) ya da tamamen çıkış yap.

Bütün metinler baştan İngilizce ve Türkçe anahtarlarla girdi.

Bilinçli bir boşluk var: oturum kontrolünün kendisi başarısız olursa (ağ hatası, timeout) koruma hiçbir şey yapmıyor ve hatayı logluyor. Kararsız bir istek yüzünden her sekmeyi kilitlemek, uyuşmazlık bile olmayan bir sorun için paneli kullanılamaz hale getirirdi.

Bir düzeltme de kozmetikti. İlk sürüm sabit genişlikli, tek satırlık bir bildirim bileşeni kullanıyordu ve uzun marka adları diyaloğu yatayda kaydırıyordu. Aynı gün bir mesaj bileşenine geçirdim.

Sekme ve oturum aynı markayı tuttuğu sürece kayıtlar geçiyor. Oturum başka bir markaya geçtiğinde koruma, biri seçene kadar sekmeyi durduruyor.

Dashboard turu

Koruma bir veri hatasıydı. Panel işimin diğer yarısı daha az dramatik: bir düzine insanın yıllardır dokunduğu kodu, onların ekranlarını bozmadan toparlamak.

Temmuz 2026'da 11 dashboard'un (verileri ve işlemleri bir arada gösteren yönetim ekranı) üstünden aynı gün altı commit'le geçtim. Her şey tek bir sorunla başladı. Bir grafiğe boş veri geldiğinde canvas sadece boş çiziliyordu. "Henüz veri yok" ile "bozuk" arasındaki farkı anlamanın yolu yoktu.

Bunun için bir ChartContainer ve bir NoDataPlaceholder yazdım. Container önce loading, sonra error, sonra empty, sonra grafiği, bu sırayla ve tek bir yerde çözüyor. "Boş", hiç dataset olmaması ya da her dataset'in sadece null, undefined veya NaN tutması demek. Bir yükseklik sınıfı alıyor; yüklenme iskeleti tam olarak yerini aldığı grafik kadar uzun oluyor ve hiçbir şey zıplamıyor. 23 grafik render (veriyi ekranda gösterilecek görünüme dönüştürme) noktasının hepsi artık bundan geçiyor.

Bunu düzgün yapmak her dashboard'un yüklenme mantığını okumak demekti ve asıl hatalar oradaydı:

  • Her dashboard'da loading adlı bir bayrak vardı ve veri geldiğinde true yapılıyordu. JSX spinner'ı göstermek için loading === false kontrol ediyordu. İki ters çevirme, yani tesadüfen doğru render ediyordu. Adını isLoading yaptım, anlamını adına uydurdum ve bir finally bloğundan set ettim ki her zaman sonuçlansın.
  • İki izin kaydı (İYS) dashboard'u callback'ini ortak API (yazılımların birbirine istek göndermesini sağlayan arayüz) yardımcısının yanlış argüman yerine veriyordu. Callback hiç çalışmıyordu ve iki sayfa da sonsuza kadar spinner'da kalıyordu.
  • Push (uygulama açık olmasa da cihaza gönderilebilen bildirim) dashboard'u hata yolunda exception atıyordu; o da spinner'ı sonsuza kadar açık bırakıyordu.
  • Bir dashboard var olmayan bir setter'ı çağırıyordu, üstelik tek işi o setter'ı beslemek olan bir isteğin içinde. Ölü isteği kaldırdım.

Grafik yükseklikleri inline style'lardan CSS (sayfanın renklerini, boyutlarını ve yerleşimini belirleyen stil kuralları) sınıflarına taşındı. Bu, 450px'lik bir minHeight altında 430px'lik bir maxHeight gibi çelişkileri ortaya çıkardı. Span'ler, bir h1 ve stillendirilmiş etiketlerden oluşan bölüm başlıkları her yerde aynı görünen boyutta h3 oldu.

Bir de her dilde 114 çeviri anahtarı ekledim. Öncesinde yalnızca e-posta dashboard'u metnini çeviri dosyalarından okuyordu. Admin dashboard'u bilerek dışarıda kaldı: sadece iç kullanım için ve içinde çeviri kullanan hiçbir şey yok.

İncelemeyi kolay tutan küçük alışkanlıklar

Daha önceki iki iş, bu kod tabanına nasıl yaklaştığımı belirledi.

Ocak 2026'da tek parça route dosyasını e-posta, kişisel arama, öneriler, smart widget'lar (sayfaya eklenen küçük işlevsel bileşen) ve web push için feature modüllerine böldüm. Ana uygulama dosyası yaklaşık 650 satır küçüldü. Sonraki özellikler, herkesin dokunduğu bir dosyada değişiklik yerine tek bir modülün içinde küçük diff'ler (iki sürüm arasındaki değişiklikleri gösteren karşılaştırma) oldu.

Hata izlemeden gelen crash'leri de olabildiğince dar düzeltiyorum. Ağustos'ta üç crash raporu, yalnızca ilgili altı dosyaya dokunan tek bir commit oldu.

Farklı ne yapardım

Koruma uyuşmazlığı sayfa yüklenince, sekme görünür olunca ve storage değişince yakalıyor. Sekme önde dururken oturum başka bir yoldan değişirse, bu ancak bir sonraki kontrolde yakalanır. Dürüst çözüm backend'de: markayı açıkça alan ve uyuşmazlığı reddeden endpoint'ler. UI koruması benim müdahale edebildiğim ve hatanın göründüğü yerdi.

Bir de app push dashboard'u. Temmuz 2025'te çizgi ve pasta grafikleri, istatistik kartları, bir kampanya tablosu ve 7 × 24'lük bir gönderim saati ısı haritasıyla kurdum. Statik örnek veriyle kurulmuştu ve bir yıl sonra hâlâ öyleydi; boş durum turunda dışarıda bırakmamın sebebi de bu. Bir dahaki sefere layout'u cilalamadan önce en az bir gerçek veri kaynağını bağlardım.

Etkileşimli demoyu deneyin. Bir sekmede markayı değiştirip diğerinde kaydetmeyi deneyin. Diğer işlerim Revotas sayfasında.

Bu yazı İngilizce aslından Türkçeye çevrilmiştir.

Çözmeye değer bir problemin mi var?Birlikte çözelim

Konuşmaya başla

Full-stack ve uygulamalı AI rollerine açığım, uzaktan veya hibrit.

Rolü, ekibi ve problemi anlat.