- npm ekosisteminde bağımlılık ağacının şişmesi önemli bir sorun olarak gösteriliyor; bunun başlıca nedenleri eski runtime desteği, atomik paket yapısı ve eski ponyfill kullanımı
- Eski motor uyumluluğu ve cross-realm güvenliği gerekçesiyle korunan küçük yardımcı paketler, modern ortamlarda da gereksiz yere varlığını sürdürüyor
- Atomik mimari, yeniden kullanılabilirliği artırmayı hedefledi ancak pratikte tekrar, güvenlik ve bakım maliyetlerini artıran verimsiz bir yapıya dönüştü
- Tüm motorların zaten desteklediği özellikler için kullanılan eski ponyfill paketleri kaldırılmadığı için gereksiz indirme ve yönetim yükü oluşturuyor
- Topluluk, e18e, knip ve module-replacements gibi araçlarla gereksiz bağımlılıkları temizleme ve yerel özelliklere geçiş sürecini ilerletiyor
JavaScript bağımlılık şişkinliğinin üç ekseni
- e18e topluluğu büyüdükçe performans odaklı katkılar artıyor ve gereksiz ya da bakımsız paketleri ayıklamaya yönelik cleanup çalışmaları yürütülüyor
- npm ekosisteminde bağımlılık ağacının şişmesi (dependency bloat) başlıca sorunlardan biri olarak öne çıkıyor; bunun temel nedenleri eski runtime desteği, atomik paket yapıları ve eski ponyfill kullanımı
1. Eski runtime desteği (güvenlik ve realm dahil)
- npm ağacında
is-string, hasown gibi birçok küçük yardımcı paket bulunuyor ve bunlar şu üç nedenle varlığını sürdürüyor
- Çok eski motorları desteklemek (ES3, IE6/7, erken dönem Node.js gibi)
-
Global namespace’in değiştirilmesine karşı koruma
-
Eski motor desteği
- ES3 ortamlarında
Array.prototype.forEach, Object.keys, Object.defineProperty gibi ES5 özellikleri yoktur
- Bu ortamlarda özellikler doğrudan uygulanmalı ya da polyfill kullanılmalıdır
- En iyi çözüm yükseltme yapmak olsa da bazı kullanıcılar hâlâ eski sürümlerde kalıyor
-
Global namespace’in değiştirilmesine karşı koruma
- Node, global nesneleri ilk anda sarıp değişikliklere karşı korumak için dahili olarak primordials kavramını kullanır
- Örneğin
Map yeniden tanımlanırsa Node’un kendisi bozulabilir; bu yüzden Node özgün referansı korur
- Bazı paket bakımcıları bu yaklaşımı genel paketlerde de uygulayarak
math-intrinsics gibi güvenlik odaklı paketler kullanır
-
Cross-realm değerler
- iframe’ler arasında nesne aktarılırken
instanceof denetiminin başarısız olması sorunu ortaya çıkar
- Örnek:
window.RegExp !== iframeWindow.RegExp
chai gibi test framework’leri realm’ler arası tür denetimi için Object.prototype.toString.call(val) yaklaşımını kullanır
is-string gibi paketler bu cross-realm uyumluluğu için vardır
-
Sorunlar
- Geliştiricilerin büyük çoğunluğu modern Node veya evergreen tarayıcılar kullanır; bu tür uyumluluklara ihtiyaç duymaz
- Buna rağmen bu paketler genel bağımlılık ağacının “hot path” içinde yer alır ve maliyeti herkes öder
2. Atomik (Atomic) mimari
- Bazı geliştiriciler, paketlerin olabildiğince küçük parçalara ayrılması ve yeniden kullanılabilir yapı taşları olarak düzenlenmesi gerektiğini savunur
- Sonuç olarak
shebang-regex, arrify, slash, path-key, onetime, is-wsl gibi aşırı derecede parçalanmış paketler ortaya çıkmıştır
- Örnek:
shebang-regex yalnızca tek satırlık bir regex içerir (/^#!(.*)/)
-
Sorunlar
- Atomik paketlerin çoğu yeniden kullanılmaz ya da yalnızca tek bir tüketiciye sahiptir
- Örnekler:
shebang-regex → yalnızca shebang-command tarafından kullanılır
cli-boxes → yalnızca boxen, ink tarafından kullanılır
onetime → yalnızca restore-cursor tarafından kullanılır
- Bu durumda paket, satır içi kodla aynı işi yapar ama npm isteği, arşiv açma, bant genişliği gibi ek maliyetler doğurur
-
Tekrarlama sorunu
- Örnek:
nuxt@4.4.2 bağımlılık ağacında is-docker, is-stream, is-wsl, path-key gibi paketlerin ikişer sürümü bulunur
- Satır içi kodla değiştirildiğinde sürüm çakışması ya da çözümleme maliyeti ortadan kalkar; böylece tekrar maliyeti neredeyse sıfırlanır
-
Tedarik zinciri riskinin büyümesi
- Paket sayısı arttıkça güvenlik ve bakım riskleri de artar
- Gerçekte bir bakımcının çok sayıda küçük paketi yönettiği ve hesabı ele geçirilince yüzlerce paketin aynı anda bozulduğu bir olay yaşanmıştır
- Basit bir kod (
Array.isArray(val) ? val : [val]) ayrı bir pakete gerek olmadan doğrudan satır içi kullanılabilir
-
Sonuç
- Atomik mimari, niyetin aksine verimsiz ve riskli bir yapıya dönüşmüştür
- Çoğu kullanıcı için somut fayda sağlamadan tüm ekosistem maliyeti üstlenir
3. Eski ponyfill’ler
- Polyfill, motorun desteklemediği bir özelliği ortama ekleyen koddur;
Ponyfill ise ortamı değiştirmeden doğrudan import edilerek kullanılan alternatif bir uygulamadır
- Örnek:
@fastly/performance-observer-polyfill hem polyfill hem ponyfill sağlar
-
Sorunlar
- Ponyfill’ler geçmişte faydalıydı, ancak hedef özellik tüm motorlarda zaten desteklense bile kaldırılmıyorlar
- Örnekler:
globalthis (2019’dan beri destekleniyor, haftalık 49 milyon indirme)
indexof (2010’dan beri destekleniyor, haftalık 2,3 milyon indirme)
object.entries (2017’den beri destekleniyor, haftalık 35 milyon indirme)
- Bu paketler çoğunlukla yalnızca kaldırılmadıkları için varlığını sürdürüyor
- Tüm LTS motorlar bir özelliği destekliyorsa ponyfill kaldırılmalıdır
Şişkinliği azaltma yolları
- Bağımlılık ağacındaki derin iç içe geçme nedeniyle temizlik zor olsa da topluluk iş birliğiyle iyileştirme mümkündür
- Her geliştirici “Bu paket gerçekten gerekli mi?” diye sormalı; gereksizse issue açmalı ya da alternatif paket aramalıdır
- module-replacements projesi, yerel özelliklerle değiştirilebilen paketlerin listesini sunar
-
knip kullanımı
- knip, kullanılmayan bağımlılıkları ve ölü kodu tespit eden bir araçtır
- Doğrudan çözüm olmasa da temizliğe başlamak için yararlıdır
-
e18e CLI kullanımı
@e18e/cli analyze komutuyla değiştirilebilir bağımlılıklar tespit edilebilir
- Örnek:
chalk → picocolors için otomatik geçiş
- İleride ortama göre Node’un
styleText gibi yerel özellikleri de önerilecek
-
npmgraph kullanımı
- npmgraph.js.org, bağımlılık ağacını görselleştiren bir araçtır
- Örnek:
eslint@10.1.0 ağacında find-up dalı izole durumdadır
- Basit dosya arama işlevi için 6 pakete ihtiyaç olmadığından
empathic gibi daha küçük alternatifler kullanılabilir
-
module replacements projesi
- Topluluk, değiştirilebilir paketler ve yerel özellikler arasındaki eşleme veri kümesini sürdürüyor
- codemods projesi üzerinden otomatik geçişler de destekleniyor
Sonuç
- Mevcut şişkinlik, eski uyumluluk ya da sıra dışı yapıları korumak isteyen küçük bir kesim yüzünden maliyetin herkes tarafından ödendiği bir yapıya dönüşmüş durumda
- Geçmişte bu kaçınılmazdı, ancak modern motorlar ve API’ler artık yeterince geliştiği için bu yük gereksiz hale geldi
- Bundan sonra bu küçük kesimin ayrı bir yığın sürdürmesi, geri kalanların ise hafif ve modern bir kod tabanına yönelmesi gerekiyor
- e18e ve npmx gibi projeler bunu dokümantasyon ve araçlarla destekliyor;
geliştiricilerin de kendi bağımlılıklarını gözden geçirip “Neden gerekli?” sorusunu sorması gerekiyor
- Hep birlikte temizlik yapılabilir
2 yorum
Ben de kütüphane yaparken hâlâ cjs build’i sunuyorum ama
2026’da esm örneği bile olmayan ve tamamen
requiretabanlı kütüphanelerin biraz güncellenmesini isterdim.Hacker News görüşleri
Bugünlerde bağımlılıksız JavaScript ile geliştirme yapmanın en iyi yön olduğunu düşünüyorum
JS/CSS standart kütüphaneleri de harika, statik analiz (TypeScript'in JSDoc kontrolleri), ES modülleri, web bileşenleri gibi araçlar da fazlasıyla güçlü
İnsanlar bu yaklaşımın ölçeklenebilirlik ya da bakım açısından dezavantajlı olduğunu söylüyor ama benim deneyimimde tam tersine daha basit ve değiştirmenin kolay olduğu bir yapı korunabildi
Framework'lerin ya da build araçlarının yaptığı işlerin çoğu, tarayıcıya gömülü özellikler ve vanilla pattern'lerle değiştirilebilir
Ancak bu yaklaşım hâlâ pek tanıdık bir alan değil; sorun da eğitim ekosisteminin büyük ölçüde büyük framework'ler etrafında dönmesi
Nitekim React kodunu tamamen vanilla'ya taşısanız bile modülerlik korunuyor ve kod uzunluğu yalnızca yaklaşık 1.5 kat artıyor; buna karşılık bağımlılık olmadığı için performans daha iyi oluyor
Elbette bağımlılıkların kötü olduğunu söylemiyorum. Sadece birçok geliştirici, “mutlaka kullanılmaları gerekir” gibi bir sabit fikre takılı kalmış durumda
Örneğin ben yoğun harita işlevleri olan siteler yapıyorum ve mapbox/maplibre/openlayers gibi alternatifsiz kütüphaneleri kullanmak zorundayım
Müşteri de geçiş maliyeti için tek kuruş ödemedi
Şu yazıda anlatıldığı gibi model güncellemelerini nasıl ele aldığını merak ediyorum
Hatta büyük kod tabanlarını az sayıda kişiyle sürdürmek daha kolay hâle geldi
Bugünkü araçlar sayesinde pek çok şeyi elle yapmak eskisine göre çok daha kolay ve bu yaklaşım agentic engineering ile de iyi örtüşüyor
Yazı iyi kaleme alınmış; duygusal değil ama sorunu net biçimde anlatıyor
JS'nin düzgün bir “standart kütüphaneye” sahip olmamasının bu durumun nedenlerinden biri olduğunu düşünüyorum
Güzel bir yazı ama sorunun kökünde bence gereksiz eklemeler (=bloat) var
Saint-Exupéry'nin “Mükemmellik, eklenecek bir şey kalmadığında değil, çıkarılacak bir şey kalmadığında elde edilir” sözünü alıntılamak isterim
Yazılımların çoğu “Bunu nasıl daha zarif yaparız?” diye değil, “Buna nasıl daha kolay bir şey ekleriz?” diye sorularak yazılıyor
Cevap da her zaman
npm i more-stuffoluyorDemosthenes ile Cicero karşıtlığındaki gibi, iyi kod çıkarılabilecek hiçbir şey kalmamış koddur
JS hem geçmiş hem de gelecekteki tarayıcı uyumluluğunu düşünmek zorunda ve UI merkezli bir dil olduğu için erişilebilirlik, uluslararasılaştırma, mobil destek gibi nedenlerle şişiyor
Çoğu durumda bu bana gizli bir teknik borç sorunu gibi görünüyor
Derleme hedefini ESx'e yükseltmemek, paketleri ya da uygulamaları güncellememek bunun nedeni
ES5 zaten 13 yıldır tüm tarayıcılarda destekleniyor (caniuse.com/es5)
İki grup da yaptığını bir özellik olarak görüyor ve çok kullanılan paketleri sürdürüyor
Bu yüzden değişmesi zor. Topluluk zaman zaman eleştiriyor ama onların da kendince bir mantığı var
Babel ile eski sürümlere transpile edince kod şişiyor ve yavaşlıyor; üstelik eski tarayıcılarda CSS ya da JS özellik sınırları yüzünden yine çalışmıyor
Hatta polyfill'lerin sorun çıkardığı da oldu (BigInt'i işleyemeyen üs operatörü polyfill'i gibi)
Konsollar, TV'ler, eski Android cihazlar, iPod touch, Facebook'un gömülü tarayıcısı gibi pek çok ortam var
Bu yüzden tek bir dış modül kullanıp geri kalanını transpiler ayarlarıyla çözüyorum
Eskiden asenkron takibi için
setTimeoutgibi şeyler override ediliyordu ama artık signals ile çok daha basit şekilde çözülebiliyorBazı paket yazarlarının indirme sayılarını artırmak için bağımlılık ağacını yapay biçimde parçaladığını düşünüyorum
7 satırlık paketlerin var olması saçma. lockfile metaverisi kodun kendisinden büyük
Eskiden create-react-app bağımlılıklarının %5'i tek bir yazarın mini paketleriydi
has-symbols, is-string, ljharb gibi örnekler var
Mesela Anthropic, npm indirme sayısı yüksek açık kaynak bakımcılarına ücretsiz Claude veriyor
İndirme sayısı yarışı riski büyütüyor
Ama başka kültürlerde bu tam tersine iyi bir şey sayılabiliyor
JS ekosistemini eleştirmeden önce 30 years of br tags yazısını okumakta fayda var
JS'nin ve araçlarının evrim sürecini anlamaya yardımcı oluyor
Sadece “Sorun JS geliştiricileri” demek mühendislik düşüncesinin eksikliğini gösterir
Her zaman daha iyi teorileri ve pratikleri düşünmeliyiz
Yazılım dünyası çok hızlı değiştiği için, bazen kendi “sahte cenazemizi” yapıp eski alışkanlıkları terk etmemiz gerekir
9 yıllık bir Node.js kod tabanını yönetiyorum; sadece 8 bağımlılığı var ve bunların hiçbirinin alt bağımlılığı yok
Önce Node'un yerleşik özelliklerini kullanıyor, sadece gereken parçaları kendim yazıyorum
Eskisine göre çok daha istikrarlı ve çok daha az stresli
Deno'nun standart kütüphanesi de harika; runtime'ın varsayılan özellikleriyle birleşince birkaç paketle rahatça uygulama yapılabiliyor
JS, dikkatli yaklaşıldığında oldukça iyi bir dil
is-stringgibi paketlerin cross-realm güvenliği iddiasını anlıyorum ama pratikte böyle durumlar nadirnpm'in yayınlamayı fazla kolaylaştırmasıyla “modülü böl, ayrı paket olarak yayımla” felsefesi gereğinden fazla büyüdü
Tüketiciler bağımlılık ağacını denetlemeden doğrudan kurulum yaptığı için, isteğe bağlı maliyet varsayılan maliyete dönüşüyor
ponyfill sorunu otomasyonla çözülebilir
Örneğin Node LTS sürümlerinde zaten desteklenen özellikleri otomatik tespit edip kaldıran Renovate tarzı bir bot faydalı olabilir
Şirket içi PWA için tek bir ilkemiz var:
“Chrome'un en güncel sürümüne yükseltin. Hâlâ sorun varsa o zaman bakarız”
Safari'nin daha az bellek kullanmasını anlıyorum ama politika olarak tekilleştirmek daha verimli
“ES3'e (IE6/7 seviyesi) kadar destek vermeliyiz” sözü gerçekten anlaşılmaz geliyor
Güvenlik açısından banka sitelerinin bile böyle antika tarayıcıları engellemesi gerekir
Webpack, Babel, polyfill yığınını yükseltmek büyük iş olduğu için olduğu gibi bırakıyorlar
“Bozulmadıysa dokunma” kültürü hâkim