Tree Shaking Nedir? Kullanılmayan Kod Nasıl Düşer?
Aynı projede hem modern bundler kullanıp hem gereksiz kod taşımak sandığınızdan daha yaygındır. Bunun nedeni çoğu zaman araç eksikliği değil, beklenti fazlalığıdır. Tree shaking tek başına tüm yükü temizlemez; yalnızca doğru koşullar oluştuğunda gerçekten kullanılmayan modül parçalarını build çıktısından çıkarır. Koşullar yanlışsa paket büyümeye devam eder ve ekip niye düşmediğini anlamakta zorlanır.
Tree shaking konusu geliştirme ekiplerinde teoride iyi bilinir, ama pratikte eksik uygulanır. "Kullanmıyorsak zaten derlenmiş çıktıya (build) yansımaz" cümlesi büyük bir efsanedir. Bundle raporu açıldığında, hiç çağırılmayan yardımcı parçaların, dil dosyalarının veya UI bileşenlerinin hâlâ ana pakette ağırlık yaptığı açıkça görülür. Problem derleyicinin (bundler) kör olması değil; kodun dışa aktarma (export) biçiminin ve modül mimarisinin tree shaking için elverişsiz kalmasıdır.
Bu başlık, bundle analizi kavramının bir adım sonrasıdır. Bundle analizi hangi modülün faturayı büyüttüğünü saptarken, tree shaking doğrudan o faturanın kesilmemesini sağlamaya çalışır. Kodlama mimarisindeki küçük sınır ihlalleri, modern build araçlarının ölü kodu silme (dead-code elimination) mekanizmasını tamamen felç edebilir.
Bundler'lar neden her projede aynı sonucu üretmez?
Tree shaking'in temeli, kullanılmayan parçaların güvenle silinebilmesidir. Bunun gerçekleşmesi için modül referanslarının statik olarak analiz edilebilmesi, yani kod henüz çalışmadan dahi neyin neyle bağlantılı olduğunun mutlak şekilde saptanabilmesi gerekir.
Burada ECMAScript modülleri (ESM) kritik rol oynar. import ve export kelimeleri sabit ve öngörülebilirse tree shaking harika çalışır. Ancak eski tip require() kurgusu (CommonJS), bir if-else bloğu içinde modül çağıran dinamik bağlantılar, veya global window objesine eklemeler yapan kütüphaneler bu statik zemin haritasını karartır. Ekip Vite veya Webpack gibi modern bir araca sahip olabilir; fakat modüller CommonJS tarzıyla içe alındığında beklenen kilobayt düşüşleri asla yaşanmaz.
Bir diğer yanılgı ise tree shaking'in code splitting yeteneğiyle aynı zannedilmesidir. Code splitting devasa bir kod yığınının geç (ihtiyaç anında) yüklenmesini sağlarken, tree shaking hiç gerek olmayan ölü kodun projeden kalıcı olarak atılmasıdır. İki konsept ağ performansını iyileştirse de tarayıcı donanımına sundukları bedeller tamamen farklıdır.
Export şeması yeterli mi? Yan Etkiler (Side Effects)
Aynı dosyanın içinde 10 farklı formatlayıcı fonksiyon bulunduğunu düşünün. Sadece iki tanesini içe aktardınız. Mantıken geri kalan 8 fonksiyonun çöpe gitmesi beklenir. Ancak dosyanın en üstünde doğrudan konsola log atan bir işlem varsa ya da arka planda çalışıp prototype zincirini değiştiren bir tanım bulunuyorsa, bundler risk almaz ve bu dosyayı olduğu gibi bırakır.
Modüllerin temiz kalması (pureness) esastır. Veri sabitleri ile olay işleyicilerinin (event handler), ya da iş mantıkları ile pollyfill entegrasyonlarının tek bir çatı altına sıkıştırılması statik ayrıştırma yeteneğini sınırlar.
// Temiz ve elemesi kolay sinyal
export function calculateTax(amount, rate) {
return amount + (amount * rate);
}
// Tehlikeli sinyal (Yan etki barındırır)
console.log('Math module initialized');
export function calculateTax(amount, rate) {
return amount + (amount * rate);
}
İkinci örnek derleyici için kırmızı alarmdır. Konsol logu çalıştırmak veya localStorage üzerine anlık veri basmak, o dosyayı riskli "side effect" listesine sokar. Kullanılmayacak satırlar oradadır fakat bundler güvenlik önlemi sebebiyle silgiyi elinden bırakmak zorunda kalır.
Ağır importların paket hacmine darbesi
Geliştiricilerin ikon veya util kütüphanelerinde en sık tetiğe bastığı hata, varlıkları "genel" bir şemsiyeden indirmektir. Bir paketi çağırırken destructuring (süslü parantez) kullanmak, arkaplanda her zaman sadece o fonksiyonun derlendiğini garantilemez.
Siz sadece bir adet takvim ikonuna ihtiyaç duyarken, kütüphanenin giriş dosyası (index.js) tüm ikon ailelerini (Barrel Export) kendi içine çağırıp sonradan içinden tek bir ikonu size sunuyor olabilir. Birçok modülü dev bir merkezi dosyada toplayıp oradan dağıtmak kod yazım hızını artırır, ancak bundler'ın ağaç dallarını budamasını kilitler.
Bu sebeple, büyük modülleri çağırırken import { IconCalendar } from 'library' yerine, hedefe varan spesifik derin yol ile import IconCalendar from 'library/icons/IconCalendar' tarzı içe almalar kullanmak, main chunk boyutunda büyük ferahlıklar yaratır. Unutmayın parçalama ya da ufaltma işlemi (dosya küçültme) kod satırlarındaki boşluğu alır; tree shaking ise neyin hiç yüklenmeyeceğine karar verir.
Paket seviyesindeki 'sideEffects' işareti
Ekipler kendi util paketini ürettiğinde derleyiciye doğrudan güvence sunabilir. İlgili klasörün package.json şemasına eklenecek olan "sideEffects": false beyanı, Webpack veya Rollup motoruna şu net garantiyi verir: "Bu paketin içindeki hiçbir dosyada gizli bir yan etki yoktur; eğer bir fonksiyon dışarıda çağrılmıyorsa o dosyayı çekinmeden komple silebilirsin."
Fakat CSS dosyalarını barındıran bir butonu tümüyle "side effects yok" diyerek etiketlerseniz, webpack kullanılmayan kısımla beraber CSS yapısını da çöpe atar. O an sitenin arayüzü kalıcı olarak kırılır. Bu yüzden tehlikeli CSS veya polyfill bileşenleri varsa "sideEffects": ["*.css", "*.scss"] olarak dizi formatında istisna tutulmalıdır. Tree shaking güçlü bir araçtır; ama mimari kuralları doğru okumadan güvenle çalışmaz.
Elde edilen düşüş gerçekten işe yarıyor mu?
Kurulum sırasında ayar değiştirip build çıktısının 60 KB azaldığını alkışlamak yanıltıcı olabilir. O 60 KB'lık verinin sitenin public (misafir) gezindiği ana sayfadan mı söküldüğü yoksa sadece nadir açılan bir profil ayar panelinden mi temizlendiği meselesinden söz ediyoruz.
Ölü kodun budandığı bölge UI çiziminin ana damarıysa, tarayıcı parsing süresi ve etkileşim performansı doğrudan iyileşir. Aksi takdirde metrikler test raporlarında düşer ama sahadaki hız hissi belirgin biçimde artmaz. Yeni bir üçüncü taraf kütüphanesi entegre edilecekse, sadece ne kadar fonksiyonel olduğuna değil, modern ESM destekli olup tree shaking ile uyum sağlayıp sağlamadığına da bakmak gerekir.
Derleme zinciri ESM yapısını bozuyorsa budama erken durur
Birçok ekip modülleri düzgün yazdığı halde beklediği düşüşü göremez; çünkü sorun uygulama kodunda değil, aradaki dönüştürme katmanındadır. TypeScript derlemesi, Babel eklentileri veya eski test yapılandırmaları import ve export ifadelerini erken aşamada CommonJS formatına çeviriyorsa bundler eline artık statik ve güvenli bir ağaç yerine daha bulanık bir bağımlılık grafiği alır. Kod dışarıdan modern görünür; fakat üretim hattının ortasında modül yapısı bozulduğu için tree shaking ancak yüzeysel çalışır.
Bu durum özellikle şirket içi paylaşılan paketlerde sık görülür. Uygulama Vite ile çalışıyordur, ama iç kütüphane tsconfig tarafında module: commonjs ile yayımlanmıştır. Siz modern projeye modern paket eklediğinizi sanırsınız; derleyici ise gerçekte eski modül sistemiyle paketlenmiş bir bağımlılığı çözmeye uğraşır. Benzer biçimde merkezi index.ts dosyalarından yapılan yoğun yeniden dışa aktarmalar da ağacı gereksiz sislendirir. Kod okunabilirliği artar, ama paketin nihai çıktısı çoğu zaman kalınlaşır.
Bir başka kırılma noktası da stil ve çalışma zamanı karışımıdır. CSS-in-JS çözümleri, tema kayıtları veya bileşen yüklendiği anda çalışan kayıt mekanizmaları bazı dosyaları fiilen yan etkili hale getirir. Bu durumda fonksiyonun kendisi kullanılmasa bile dosyanın başlatma mantığı korunur. Dolayısıyla tree shaking yalnızca "kullanılmayan export" meselesi değildir; modülün yüklenir yüklenmez bir şey yapıp yapmadığı meselesidir. Paket tasarımını saf hesaplayıcılar, görsel kayıtlar ve global başlangıçlar olarak ayırmak bu yüzden önemlidir.
Pratik doğrulama için yalnızca kaynak koda bakmak yetmez. Üretim çıktısında oluşan paket raporuna, bağımlılık ağacına ve gerekiyorsa yayımladığınız iç paketin package.json alanlarına bakın. module, exports ve sideEffects tanımları tutarlıysa; ayrıca derleme zinciri ESM yapısını koruyorsa, tree shaking beklenen seviyede sonuç verir. Aksi halde ekip haftalarca import satırlarını düzeltir, ama asıl sorun paketleme katmanında kaldığı için kazanım sınırlı olur.