Heisenbug: nedir, neden oluşur ve yakalama yöntemleri

Yazar: IT Sectr Yayınlanma: 2026-07-29 Okuma süresi: 10 dk

Heisenbug — hata ayıklamaya çalıştığınızda kaybolan bir hata. Terim, Heisenberg'in belirsizlik ilkesinden gelir: gözlem, sistemin davranışını etkiler. Mobil geliştirmede Heisenbug en zor sorunlardan biridir çünkü standart hata ayıklama yöntemleri (loglar, kesme noktaları, ek kod) program durumunu değiştirir ve hatayı gizler. Nedenlerini ve yakalanması zor hatalarla başa çıkma yöntemlerini inceleyelim.

Anahtar Noktalar

  • Race condition — Heisenbug'un ana nedeni: hata ayıklama sırasında zamanlamanın değişmesi sorunu maskeler
  • Bohrbug — öngörülebilir bir hata, Heisenbug'un aksine kolayca tekrarlanabilir
  • Mandelbug — karmaşık neden-sonuç ilişkilerine sahip, başlangıç koşullarına duyarlı hata
  • ThreadSanitizer — zamanlamayı etkilemeden veri yarışlarını tespit eden araç
  • Deterministik testler — Heisenbug'u tekrarlamanın tek güvenilir yolu

Mobil geliştirmede Heisenbug nedir?

Heisenbug — üretimde veya normal çalışma sırasında ortaya çıkan, ancak hata ayıklama ortamında tekrarlanmaya çalışıldığında kaybolan bir hata sınıfı. Terim, 1980'lerde programcı Jim Gray tarafından dağıtık sistemler bağlamında ortaya atılmıştır, ancak bugün asenkron yapıları nedeniyle mobil uygulamalar için en alakalı olanıdır.

Ana neden: standart hata ayıklama araçları yürütme ortamını değiştirir. Bir kesme noktası iş parçacığını birkaç milisaniye duraklatır, günlükleme senkron G/Ç ekler, ek kontroller işlemlerin sırasını değiştirir. Çok iş parçacıklı bir ortamda, mikrosaniyelik bir gecikme bile iş parçacığı yürütme sırasını değiştirebilir ve bir veri yarışını gizleyebilir.

Microsoft Research'e (2022) göre, çok iş parçacıklı mobil uygulamalardaki tüm hataların yaklaşık %15-25'i Heisenbug olarak sınıflandırılır. Aynı zamanda, bir Heisenbug'u bulma ve düzeltme süresi, doğrudan tekrarlanamaması nedeniyle normal bir hatadan ortalama 5-10 kat daha uzundur.

Heisenbug örneği

Bir uygulama, listede hızlıca kaydırma yapıldığında üretimde çöker, ancak hata ayıklayıcıya bağlandığında veya log eklendiğinde — mükemmel çalışır. Nedeni: UI iş parçacığı (RecyclerView güncellemesi) ile arka plan iş parçacığı (adaptör veri güncellemesi) arasında veri yarışı. Loglar, iş parçacıklarını rastgele senkronize eden bir gecikme ekler.

Bohrbug, Mandelbug, Heisenbug: Hata sınıflandırması

Bohrbug — öngörülebilir, istikrarlı bir şekilde tekrarlanabilir hata. Bohr'un atom modeline benzetilerek adlandırılmıştır: bir atom gibi, hata her gözlemlendiğinde aynı şekilde davranır. Örnek: veriler yüklenmeden önce bir düğmeye tıklandığında NullPointerException. Standart birim testleri ile tedavi edilir.

Mandelbug — karmaşık, kaotik neden-sonuç ilişkisine sahip hata (Mandelbrot kümesine benzetilerek adlandırılmıştır). Yalnızca belirli koşulların bir kombinasyonu altında ortaya çıkar: OS sürümü, cihaz modeli, ağ durumu. Hata ayıklama sırasında kaybolmamasıyla Heisenbug'dan ayrılır — sorun tekrarlama zorluğudur, araçların davranışı değiştirmesi değil.

Heisenbug — tam olarak hata ayıklama araçları nedeniyle kaybolan hatadır. Bir log eklerseniz — hata kaybolur. Bir kesme noktası koyarsanız — hata ortaya çıkmaz. Her şeyi kaldırırsanız — hata geri döner. Ana neden: hata ayıklama sırasında değişen zamanlama.

TürTekrarlanabilirlikHata ayıklamaya tepkiÖrnek
Bohrbug%100DeğişmezBoş listede NPE
MandelbugKaotikDeğişmezAndroid 12, Samsung, düşük pilde çökme
HeisenbugSadece ayıklama olmadanKaybolurLoglarla kaybolan veri yarışı
SchrödinbugKodda ortaya çıkmazBakınca belirirKodda görünür ama asla tetiklenmez

Heisenbug'un ana nedenleri

Race condition — Heisenbug'un bir numaralı nedeni. İki iş parçacığı senkronizasyon olmadan paylaşılan verilere erişir. Hata ayıklayıcı bir gecikme getirir ve iş parçacıklarının doğal olarak senkronize olmasına neden olur. Hata ayıklayıcı olmadan, yürütme sırası tahmin edilemez.

Zamanlamaya bağlı hatalar — yalnızca belirli bir yürütme hızında ortaya çıkan hatalar. Örneğin, bir sonraki işlem başlamadan önce tamamlanması gereken bir animasyon. Hata ayıklayıcıda animasyon daha yavaş çalışır ve işlem animasyon bittikten sonra başlamak için zaman bulur. Üretimde — tam tersi.

kotlin
// Example race condition — typical Heisenbug
class ListViewModel : ViewModel() {
    private var items = mutableListOf<String>()

    fun loadFromNetwork() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = api.fetchItems()
            items.addAll(result) // ❌ Not thread-safe
        }
    }

    fun getItems(): List<String> = items.toList()
    // Race condition: getItems read may overlap with loadFromNetwork write
}

Derleyici optimizasyonu — derleyici (JIT, ART, Kotlin/Native) optimizasyon için talimatları yeniden sıralayabilir. Hata ayıklama yapısında (debug build) optimizasyonlar devre dışıdır ve kod «yazıldığı gibi» yürütülür. Sürüm yapısında (release build) derleyici işlem sırasını değiştirir, bu da kodda gizli varsayımları ortaya çıkarabilir.

  • ThreadLocal — diğer iş parçacıkları tarafından görülemeyen iş parçacığı yerel değişkenlerinin yanlış kullanımı
  • Başlatılmamış değişkenler — sınıf alanlarının varsayılan değerlerine güvenen kod
  • GCD/dispatch kuyrukları — iOS'ta, eşzamanlı kuyruklarda blok yürütme sırasının tanımsız olması
  • Arabellekli G/Ç — arabellek dolana kadar veriler diske yazılmaz

Yakalanması zor hataları yakalama stratejileri

ThreadSanitizer (TSan) — C/C++ ve Kotlin/Native'de veri yarışlarını tespit etmek için bir Google aracı. Yapıya gömülür ve senkronizasyon olmadan herhangi bir paylaşılan bellek erişimini tespit eder. Logların aksine TSan, G/Ç üzerinden değil, enstrümante kod aracılığıyla çalıştığı için zamanlamayı etkilemez.

Deterministik testler — gerçek eşzamansızlığı kontrollü eşzamansızlıkla değiştirin. Yürütme sırası üzerinde tam kontrol için TestDispatcher (Kotlin), RxJava Plugins veya GCD test kuyrukları (iOS) kullanın. Belirli senaryolar belirleyin: iş parçacığı A çalışır, sonra B, sonra tekrar A.

Döngüsel günlükleme — bellekteki halka tampona günlükleme (disk değil). Hata oluştuğunda, tampon bir dosyaya kaydedilir. Belleğe yazma nanosaniye sürdüğünden (disk G/Ç için milisaniye yerine), bu tür günlükleme zamanlamayı etkilemez ve Heisenbug'u maskelemez.

kotlin
class CyclicBuffer(val capacity: Int = 1000) {
    private val buffer = ArrayDeque<String>(capacity)
    private val lock = Any()

    fun log(message: String) {
        synchronized(lock) {
            if (buffer.size >= capacity) buffer.removeFirst()
            buffer.addLast(message)
        }
    }

    fun flush() {
        synchronized(lock) { buffer.forEach { fileWriter.write(it) } }
    }
}

Üretimde günlükleme — hata yerel olarak tekrarlanamıyorsa, üretimde veri toplayın. Firebase Crashlytics logları, Sentry Breadcrumbs veya özel bir döngüsel kaydedici kullanın. Önemli: günlükleme eşzamansız olmalı ve performans üzerinde minimum etkiye sahip olmalıdır.

Mimari düzeyde Heisenbug önleme

Durum izolasyonu — paylaşılan değişebilir durumu en aza indirin. Her bileşen, diğer bileşenlerden doğrudan yazılamayan kendi izole durumuna sahip olmalıdır. Unidirectional Data Flow (UDF) kullanın — durum tek yönde akar: Olay → Azaltıcı → Durum → UI.

Fonksiyonel yaklaşım — yan etkileri olmayan saf işlevlerin test edilmesi ve hata ayıklaması daha kolaydır. Yan etkileri (ağ, DB, dosyalar) kesin olarak tanımlanmış katmanlarda (depo, veri kaynağı) izole edin. Fonksiyonel kodda iş parçacığı hataları neredeyse imkansızdır.

Sıkı mod — hata ayıklama yapısında Android StrictMode'u etkinleştirin. İş parçacığı politikası ihlallerini (ana iş parçacığında ağ, ana iş parçacığında disk G/Ç) tespit eder ve bir istisna fırlatır. Bu, potansiyel bir Heisenbug'u hemen görülebilen deterministik bir Bohrbug'a dönüştürür.

kotlin
class DebugApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            StrictMode.setThreadPolicy(
                StrictMode.ThreadPolicy.Builder()
                    .detectDiskReads()
                    .detectDiskWrites()
                    .detectNetwork()
                    .penaltyLog()
                    .build()
            )
        }
    }
}

Eşzamansızlığa odaklanan kod incelemesi — sürecin zorunlu bir parçasıdır. Her çekme isteği, paylaşılan değişebilir durum, iş parçacığı güvenli olmayan koleksiyonlar ve senkronizasyon eksikliği açısından kontrol edilmelidir. Belirli kalıpları (örneğin, synchronized olmadan MutableList'e erişmek) otomatik olarak yasaklamak için lint kuralları kullanın.

Sıkça Sorulan Sorular

Heisenbug'u bulmak neden bu kadar zor?

Çünkü standart yöntemler — kesme noktaları, loglar, print — yürütme ortamını o kadar değiştirir ki hata ortaya çıkmaz olur. Hata ayıklayıcı tüm iş parçacıklarını onlarca milisaniye duraklatır. Bu süre boyunca, hataya neden olan veri yarışı doğal olarak çözülür. Yürütme zamanlamasını etkilemeyen araçlar gerekir.

Heisenbug, Mandelbug'dan nasıl farklıdır?

Mandelbug koşulların karmaşıklığı nedeniyle tekrarlanması zordur, ancak hata ayıklama araçları onun ortaya çıkışını etkilemez. Heisenbug ise tam olarak hata ayıklama araçları nedeniyle kaybolur. Mandelbug örneği: yalnızca Android 11, 3 GB RAM ve %15'in altında pil seviyesine sahip cihazlarda çökme. Heisenbug örneği: Log.d() eklendiğinde kaybolan veri yarışı.

CI/CD'de Heisenbug nasıl test edilir?

Flaky test tespiti kullanın — bazen başarısız olan, bazen geçen testler. Android'de test izolasyonu için Android Test Orchestrator kullanın. Hata ayıklama testlerine StrictMode ekleyin. Yapıyı ThreadSanitizer ile enstrümante edin. Bir test çalıştırmaların >%5'inde flaky ise — onu potansiyel bir Heisenbug olarak kabul edin ve birleştirmeden önce araştırın.

Flow/Coroutines Heisenbug'u önlemeye yardımcı olur mu?

Kısmen. Kotlin'de Flow ve yapılandırılmış eşzamanlılık, paylaşılan değişebilir durum miktarını azaltır ve iş parçacığı yönetimini basitleştirir. Ancak coroutine'ler iş parçacığı güvenliğini garanti etmez: iki coroutine durumu paylaşıyorsa, veri yarışı hala mümkündür. Paylaşılan durumu korumak için Mutex veya coroutine'ler arasında veri geçirmek için Channel kullanın.

Heisenbug yalnızca üretimde ortaya çıkarsa ne yapmalı?

Hata durumunda otomatik olarak boşaltılan bellekte döngüsel log tamponu kullanın. Özel breadcrumbs ile Crashlytics veya Sentry aracılığıyla ayrıntılı izleme ekleyin. Android için ANR tespitini etkinleştirin ve izleri kontrol edin. Hata bir veri yarışıysa, üretim benzeri yük ile hata ayıklama yapısında ThreadSanitizer sorunu ortaya çıkarabilir.

Özet

  • Heisenbug — hata ayıklamaya çalışırken kaybolan hata; ana neden geliştirici araçlarının zamanlama değişiklikleridir
  • Race condition — mobil uygulamalarda, özellikle eşzamansız kodda Heisenbug'un ana nedeni
  • Bohrbug (%100 tekrarlanabilir) ve Mandelbug (kaotik) — Heisenbug ile karıştırılmaması gereken diğer hata türleri
  • ThreadSanitizer — yürütme zamanlamasını etkilemeden veri yarışlarını tespit etmek için en iyi araç
  • Disk yerine bellekte döngüsel günlükleme — Heisenbug'u maskelemeden veri toplama yöntemi
  • Unidirectional Data Flow ve paylaşılan değişebilir durumu en aza indirme — tüm bir hata sınıfının mimari önlemesi
  • Hata ayıklama yapısında StrictMode, potansiyel Heisenbug'u hemen görülebilen deterministik bir Bohrbug'a dönüştürür

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun