Ana içeriğe geç

Dead Code Elimination: Kullanılmayan JavaScript Kodlarını Kaldırma

Dead Code Elimination: Kullanılmayan JavaScript Kodlarını Kaldırma - Web Geliştirme Rehberi

Bir JavaScript projesi yavaşladığında gözler çoğu zaman büyük framework paketlerine, görsellere ya da ağ isteklerine döner. Oysa dosyanın içine biraz yakından bakıldığında asıl yükün bir kısmı kullanılmayan kodlardan gelebilir. Kapatılmış özellikler, artık çağrılmayan yardımcı fonksiyonlar, yanlış import alışkanlıkları ve zamanla biriken eski modüller paket içinde sessizce yer kaplar. Kullanıcı bu satırları görmez; ama tarayıcı onları yine de indirir, ayrıştırır ve bazı durumlarda çalıştırmak için hazırlık yapar.

Dead code elimination bu sessiz maliyeti azaltma işidir. Yalnızca dosyayı küçültmek için değil, kod tabanını daha anlaşılır kılmak için de önemlidir. Çünkü gereksiz kod biriktiğinde bakım zorlaşır, gerçekten kritik modül ile artık tarih olmuş akış aynı dosyada yan yana kalır. Paket boyutunu küçültme tarafında çalışıyorsanız, kod sıkıştırma ve ölü kod temizliği akışını birlikte kurmak özellikle derleme sürecinde ciddi fark yaratır.

Burada dikkat edilmesi gereken nokta, her görünmeyen satırın güvenle silinebilir olmadığını bilmektir. Dead code bazen gerçekten erişilemeyen bloktur; bazen de yalnız belirli senaryoda çalışan ama gözden kaçan güvenlik ağıdır. Sağlıklı yaklaşım, önce neyin gerçekten ölü olduğunu anlamak, sonra tree shaking, minify ve manuel temizlik kararlarını birbirine karıştırmadan uygulamaktır.

Dead code tam olarak nedir ve neden her fazlalık aynı şey değildir?

Dead code en basit haliyle uygulamanın işleyen akışında hiçbir değer üretmeyen kod parçalarıdır. Bu bazen asla çalışmayacak bir if (false) bloğu olur, bazen tanımlandığı halde çağrılmayan fonksiyon, bazen de import edilip hiç kullanılmayan yardımcı modüldür. Ancak mesele sadece “hiç çalışmıyor” demek kadar basit değildir. Bir modül kullanılmasa da içindeki yan etkiler nedeniyle paket dışında davranış üretiyor olabilir. Bu nedenle ölü kod ile düşük frekanslı kodu aynı sepete koymak yanlış sonuç doğurur.

Örneğin uygulama ilk açılışta yalnızca bir kez çalışan izleme kaydı, hata yakalama kurulumu ya da global stil kaydı normal kullanımda görünmez; ama yine de işlev taşır. Buna karşılık eski kampanya akışından kalan, artık hiçbir route tarafından çağrılmayan bileşen gerçekten dead code olabilir. Yani doğru teşhis, kullanım sıklığından değil, uygulama davranışındaki rolünden çıkar. Bu ayrım yapılmadığında temizlemeye çalışırken işleyen akışın bir parçasını da silme riski doğar.

Pratikte dead code üç ana biçimde karşınıza çıkar: erişilemeyen koşul blokları, kullanılmayan export/import zinciri ve artık bağlanmayan eski özellik kodu. Bunların her biri paketi farklı şekilde şişirir. Erişilemeyen koşullar derleme sırasında temizlenebilirken, yanlış import alışkanlıkları çoğu zaman bundle seviyesinde sessiz boyut artışı üretir.

Dead code projede en çok nasıl birikir?

En yaygın kaynak refactor yarım bırakmaktır. Bir ekip yeni bir servis katmanına geçtiğinde eski yardımcıları “şimdilik dursun” diye bıraktığında dosyalar temiz görünse bile paket genişler. Özellik bayrakları da benzer sorun üretir. Kampanya dönemi için yazılmış bileşen kapanır, koşul kaldırılır, ama destek dosyaları içeride kalır. Zamanla bunlar tek tek önemsiz görünür; birlikteyse fark edilir maliyet üretir.

İkinci büyük kaynak import alışkanlığıdır. Bir kütüphanenin yalnızca iki yardımcı fonksiyonuna ihtiyaç varken tüm paketi içe almak, ya da ortak bir index dosyasından her şeyi zincirleme taşımak paket boyutunu gereksiz büyütebilir. Özellikle değişken adı kısaltma, sıkıştırma ve paketleme kararlarının nasıl birlikte çalıştığını anlatan mangle odaklı optimizasyon mantığı burada yararlı çerçeve sunar; çünkü küçültme ile gerçekten gereksiz kodu silme aynı şey değildir.

Üçüncü kaynak da ekip süreçleridir. Kod incelemesinde “çalışıyor” eşiği aşılınca kullanılmayan fonksiyonlar, yorumda bırakılmış eski örnekler ve yalnız test etmek için eklenmiş yardımcılar kolayca repoda kalır. CI tarafında kullanılmayan import ve export kontrolü yoksa bu birikim daha da hızlanır. Dead code çoğu projede tek büyük hata yüzünden değil, küçük ihmal zinciri yüzünden oluşur.

Özellikle monorepo yapılarında bu daha görünür hale gelir. Bir pakette kaldırılan yardımcı fonksiyon, başka paket “ileride lazım olur” diye tutulduğunda kimse gerçek sahipliği üstlenmez. Sonra eski API yüzeyi korunur, yeni yüzey eklenir ve paket yavaş yavaş iki farklı dönemin izini taşımaya başlar. Dead code çoğu zaman kötü niyet değil, net sahiplik eksikliğinin doğal sonucu olur.

Dead code nasıl tespit edilir?

İlk katman statik analizdir. Linter kuralları kullanılmayan değişkenleri, import'ları ve bazı erişilemeyen blokları daha kod derlenmeden işaretleyebilir. Bu hızlıdır; ancak tek başına yeterli değildir. Çünkü linter bir fonksiyonun kullanılmadığını söyleyebilir, fakat dinamik olarak çağrılan veya başka paket tarafından tüketilen export'ları her zaman doğru yorumlayamayabilir.

İkinci katman bundle analizidir. Üretim paketinde hangi modülün ne kadar yer kapladığını görmek, özellikle “görünüşte küçük ama içeride ağır” bağımlılıkları buldurur. Burada yalnız dosya boyutuna bakmak yerine, ilk yükleme paketine gereksizce giren modülleri okumak gerekir. Paket kararlarının kullanıcı tarafında neye dönüştüğünü görmek için yükleme ve çalışma maliyetini performans raporunda izlemek tespiti daha anlamlı hale getirir.

Üçüncü katman coverage ve gerçek akış kontrolüdür. Testler ya da tarayıcı coverage görünümü, hangi satırların gerçekten yürüdüğünü gösterir. Bu veri çok değerlidir ama yine de kör güvenle kullanılmaz. Çünkü test edilmeyen ama üretimde çalışan bir yönetim akışı coverage tablosunda ölü gibi görünebilir. Sağlıklı tespit, statik analiz, bundle görünümü ve uygulama bilgisinin birlikte okunmasıyla yapılır.

Bu aşamada karşılaştırmalı ölçüm almak da önemlidir. Bir modülü kaldırmadan önce ve sonra paket boyutuna, parse süresine ve etkileşim gecikmesine bakarsanız gerçekten değer üreten temizliği ayırt edersiniz. Aksi halde saatlerce uğraştığınız temizlik, kullanıcı tarafında hissedilmeyen birkaç kilobayt kazanca dönüşebilir. Önceliği yüksek alanı doğru seçmek, temizliğin kendisi kadar kritiktir.

Tree shaking, minify ve dead code elimination aynı şey mi?

Hayır. Bu kavramlar birbirini destekler ama aynı işlemi yapmaz. Tree shaking, modül yapısını okuyarak kullanılmayan export'ların pakete hiç girmemesini hedefler. Minify ise pakete giren kodu daha kısa yazımla teslim eder. Dead code elimination ise hem derleme sırasında erişilemeyen blokları temizleme hem de kullanılmayan parçaları düşürme mantığını kapsar. Yani biri seçme, biri sıkıştırma, biri de gereksizi ayıklama işidir.

import { formatPrice, debugHelper } from "./utils.js";

if (process.env.NODE_ENV !== "production") {
  debugHelper();
}

console.log(formatPrice(total));

Bu örnekte üretim derlemesinde çevre değişkeni doğru çözümleniyorsa koşul içindeki blok silinebilir. Eğer debugHelper başka yerde kullanılmıyorsa tree shaking ile paketin dışına itilebilir. Ardından minify kalan kodu daha kısa hale getirir. Aynı akış içinde üç ayrı optimizasyon mantığı vardır. Sorunlardan biri eksikse, diğer ikisi tek başına beklenen sonucu vermez.

Özellikle CommonJS alışkanlığı, yan etkili modüller ve merkezi barrel dosyaları tree shaking verimini zayıflatabilir. Bu yüzden “minify yaptık, problem çözüldü” demek çoğu zaman yüzeysel kalır. Önce paket mimarisinin temizlenmesi, sonra sıkıştırma kararının gelmesi daha sağlıklı sıradır.

Hangi kodu silmek risklidir?

Yan etkili modüller en riskli gruptur. Bir dosya sadece import edildiğinde global ayar değiştiriyor, event listener bağlıyor ya da polyfill yüklüyorsa, kullanılmıyor gibi görünse bile davranış taşıyor olabilir. Benzer biçimde hata izleme, ölçümleme, uluslararasılaştırma kayıtları ve uygulama açılışındaki bootstrap kodu da düşük görünürlük yüzünden yanlışlıkla ölü kabul edilebilir.

Dinamik import ve eklenti sistemleri de dikkat ister. Kod tabanında doğrudan çağrı görünmeyen ama isimle çözülen bileşenler, dosya sistemi üzerinden yüklenen modüller veya CMS konfigürasyonundan tetiklenen parçalar basit aramayla bulunamayabilir. Bu yüzden dead code temizliği yalnız metin aramasıyla yapılmaz. Akışa dair bilgi yoksa agresif temizlik, bakım kazancı yerine üretim hatası getirir.

Bu nedenle en güvenli yaklaşım, silme kararından önce iki soruyu sormaktır: Bu modülün yan etkisi var mı? Bu modül yalnız doğrudan import zinciriyle mi çağrılıyor? Cevap belirsizse önce izleme, coverage ya da sınırlı kaldırma deneyi yapılmalıdır. Dead code temizliği cesaret işi değildir; kontrollü risk yönetimi işidir.

Özellikle kullanıcıya görünmeyen ama iş açısından önemli akışlar burada öne çıkar. Ödeme sonrası izleme pikseli, hata raporlama istemcisi, performans ölçüm kancası ya da güvenlik başlatma kodu nadiren görünür ama kaldırıldığında etkisi büyüktür. Bu yüzden kodun “ekranda bir şey değiştirmemesi” onun gereksiz olduğu anlamına gelmez. Dead code kararını her zaman işlev haritasıyla birlikte vermek gerekir.

Sağlıklı temizleme akışı nasıl kurulur?

İyi akış küçükten başlar. Önce linter seviyesinde kullanılmayan import ve değişkenleri hata seviyesine çıkarın. Sonra paket analizinde ilk yüklemeye gereksiz giren modülleri işaretleyin. Ardından modül yapısını tree shaking dostu hale getirin: doğrudan gerekli export'ları kullanın, yan etkili dosyaları açık biçimde ayırın, barrel dosyalarını ölçüsüz büyütmeyin. Bu temel katman kurulmadan Terser ayarı eklemek genellikle makyaj etkisi üretir.

İkinci adım ekip disiplini kurmaktır. Özellik kaldırıldığında yalnız görünür bileşen değil, test verisi, config, eski import ve route bağlantısı da temizlenmeli. PR incelemesinde “çalışıyor mu” kadar “eski akıştan ne kaldı” sorusu da sorulmalı. Düzenli paket boyutu eşiği belirlemek ve ani büyümeleri görünür kılmak bu yüzden değerlidir. Küçük kazanımların toplamı ilk boyama, parse süresi ve etkileşim hissinde gerçek fark üretir.

Bundle budget yaklaşımı burada faydalı olur. Paket belirli eşiği geçtiğinde uyarı veren ya da derlemeyi kıran kurallar, dead code'u “bir gün temizleriz” seviyesinden çıkarıp günlük disipline taşır. Aynı şekilde yeni bağımlılık eklenirken bunun ilk yüklemeye mi, ayrı chunk'a mı, yoksa yalnız yönetim ekranına mı girdiğini sormak gerekir. Dead code elimination yalnız temizlik değil, yeni yüklerin kontrollü kabulü anlamına da gelir.

Küçük ama sürekli temizlik yaklaşımı çoğu zaman büyük tek seferlik refactor'dan daha verimlidir. Her sprintte kaldırılan bir eski helper, sadeleştirilen bir import zinciri ve kapatılan bir özellik bayrağı, birkaç ay içinde dikkat çekici hafifleme üretir. Üstelik bu yöntem ekip hafızasını da güçlendirir; insanlar hangi modüllerin artık aktif olmadığını unutmadan sistemi temiz tutar. Dead code elimination kalıcı sonuç verdiğinde bunu genellikle büyük bir sihirle değil, düzenli küçük kararlarla yapar.

Dead code elimination tek seferlik temizlik kampanyası gibi düşünülmemeli. Sağlıklı projelerde bu, derleme ayarları, kod inceleme alışkanlığı ve mimari tercihlerin birleşiminden doğar. Gereksiz kodu sistemli biçimde dışarıda tuttuğunuzda yalnız daha hafif paket üretmezsiniz; aynı zamanda ekibin hangi modülün gerçekten iş yaptığını daha net görmesini sağlarsınız. Uzun ömürlü hız kazanımı çoğu zaman tam burada başlar.

En iyi sonuç, hız ekibinin tek başına yaptığı gizli temizlikten değil, bütün ekibin “kalan kodun gerçekten görevi var mı” sorusunu düzenli sormasından çıkar. Bu soru canlı kaldığında ölü kod yeniden birikse bile uzun süre görünmez halde kalamaz.