- Pembengkakan pohon dependensi di ekosistem npm disebut sebagai masalah utama, yang berasal dari dukungan runtime lama, struktur paket atomik, dan penggunaan ponyfill usang
- Paket utilitas kecil yang dipertahankan karena kompatibilitas engine lama dan keamanan lintas realm masih tetap tersisa secara tidak perlu di lingkungan modern
- Arsitektur atomik dimaksudkan untuk meningkatkan reusabilitas, tetapi dalam praktiknya justru menjadi struktur yang tidak efisien karena menambah biaya duplikasi, keamanan, dan pemeliharaan
- Paket ponyfill usang untuk fitur yang sudah didukung semua engine tidak dihapus, sehingga memicu unduhan yang tidak perlu dan beban pengelolaan tambahan
- Komunitas mendorong pembersihan dependensi yang tidak perlu dan peralihan ke fitur native lewat alat seperti e18e, knip, dan module-replacements
Tiga Pilar Pembengkakan Dependensi JavaScript
- Seiring pertumbuhan komunitas e18e, kontribusi yang berfokus pada performa makin meningkat, dan aktivitas cleanup untuk merapikan paket yang tidak perlu atau tidak terawat sedang berlangsung
- Pembengkakan pohon dependensi (dependency bloat) di ekosistem npm disebut sebagai masalah utama, dengan dukungan runtime lama, struktur paket atomik, dan penggunaan ponyfill usang sebagai penyebab inti
1. Dukungan Runtime Lama (termasuk keamanan dan realm)
- Di pohon npm ada banyak paket utilitas kecil seperti
is-string, hasown, yang dipertahankan karena tiga alasan berikut
- Dukungan untuk engine yang sangat tua, misalnya ES3, IE6/7, Node.js awal
-
Pencegahan modifikasi namespace global
- Penanganan nilai cross-realm
-
Dukungan engine lama
- Di lingkungan ES3, fitur ES5 seperti
Array.prototype.forEach, Object.keys, Object.defineProperty tidak tersedia
- Dalam lingkungan seperti ini, implementasi manual atau polyfill harus digunakan
- Solusi terbaik adalah upgrade, tetapi sebagian pengguna masih tetap memakai versi lama
-
Pencegahan modifikasi namespace global
- Node secara internal menggunakan konsep primordials untuk membungkus objek global pada tahap awal agar terlindung dari modifikasi
- Misalnya, jika
Map didefinisikan ulang, Node sendiri bisa rusak, sehingga Node menyimpan referensi aslinya
- Sejumlah maintainer paket menerapkan pendekatan ini juga pada paket umum dan memakai paket berfokus keamanan seperti
math-intrinsics
-
Nilai cross-realm
- Saat objek dikirim antar iframe, pemeriksaan
instanceof bisa gagal
- Contoh:
window.RegExp !== iframeWindow.RegExp
- Framework pengujian seperti
chai melakukan pemeriksaan tipe antar realm dengan pendekatan Object.prototype.toString.call(val)
- Paket seperti
is-string ada demi kompatibilitas cross-realm seperti ini
-
Masalahnya
- Sebagian besar developer menggunakan Node modern atau browser evergreen, sehingga kompatibilitas seperti ini tidak lagi diperlukan
- Namun paket-paket ini masuk ke “hot path” pohon dependensi umum sehingga semua orang ikut menanggung biayanya
2. Arsitektur Atomik
- Sebagian developer berpendapat bahwa paket harus dipisah menjadi unit sekecil mungkin agar tersusun dari building block yang dapat dipakai ulang
- Akibatnya, muncul banyak paket yang terlampau terfragmentasi seperti
shebang-regex, arrify, slash, path-key, onetime, is-wsl
- Contoh:
shebang-regex hanya berisi satu baris regex (/^#!(.*)/)
-
Masalahnya
- Sebagian besar paket atomik tidak dipakai ulang atau hanya punya satu konsumen
- Contoh:
shebang-regex → hanya dipakai shebang-command
cli-boxes → hanya dipakai boxen, ink
onetime → hanya dipakai restore-cursor
- Dalam kasus seperti ini, hasilnya sama saja dengan kode inline, tetapi tetap menambah biaya seperti permintaan npm, ekstraksi arsip, dan bandwidth
-
Masalah duplikasi
- Contoh: dalam pohon dependensi
nuxt@4.4.2, is-docker, is-stream, is-wsl, path-key masing-masing muncul ganda dalam 2 versi
- Jika diganti dengan kode inline, konflik versi dan biaya resolusi hilang sehingga biaya duplikasi nyaris tidak ada
-
Perluasan risiko supply chain
- Makin banyak jumlah paket, makin besar risiko keamanan dan pemeliharaan
- Pernah ada kasus ketika satu maintainer mengelola banyak paket kecil, akunnya diretas, dan ratusan paket rusak sekaligus
- Kode sederhana seperti
Array.isArray(val) ? val : [val] bisa langsung ditulis inline tanpa perlu paket terpisah
-
Kesimpulan
- Arsitektur atomik, alih-alih memenuhi niat awalnya, justru berubah menjadi struktur yang tidak efisien dan berisiko
- Tanpa manfaat nyata bagi kebanyakan pengguna, seluruh ekosistem menanggung biayanya
3. Ponyfill Usang
- Polyfill adalah kode yang menambahkan fitur ke lingkungan saat engine belum mendukungnya,
sedangkan Ponyfill adalah implementasi pengganti yang digunakan dengan di-import secara langsung tanpa memodifikasi lingkungan
- Contoh:
@fastly/performance-observer-polyfill menyediakan polyfill sekaligus ponyfill
-
Masalahnya
- Ponyfill dulu berguna, tetapi tidak dihapus meski fitur targetnya sudah didukung semua engine
- Contoh:
globalthis (didukung sejak 2019, 49 juta unduhan mingguan)
indexof (didukung sejak 2010, 2,3 juta unduhan mingguan)
object.entries (didukung sejak 2017, 35 juta unduhan mingguan)
- Paket seperti ini sebagian besar tetap ada hanya karena belum pernah dibersihkan
- Jika semua engine LTS sudah mendukung fiturnya, ponyfill seharusnya dihapus
Cara Mengurangi Pembengkakan
- Karena kedalaman nested pada pohon dependensi yang besar, pekerjaan pembersihan memang sulit, tetapi bisa diperbaiki lewat kolaborasi komunitas
- Setiap developer perlu bertanya, “apakah paket ini benar-benar perlu?”, dan jika tidak, buat issue atau cari paket pengganti
- Proyek module-replacements menyediakan daftar paket yang bisa diganti dengan fitur native
-
Menggunakan knip
- knip adalah alat untuk mendeteksi dependensi tak terpakai dan dead code
- Ini bukan solusi langsung, tetapi berguna sebagai titik awal untuk cleanup
-
Memanfaatkan e18e CLI
- Dengan perintah
@e18e/cli analyze, kita bisa mendeteksi dependensi yang dapat diganti
- Contoh: migrasi otomatis dari
chalk ke picocolors
- Di masa depan, alat ini juga akan merekomendasikan fitur native seperti
styleText milik Node sesuai lingkungan
-
Memanfaatkan npmgraph
- npmgraph.js.org adalah alat visualisasi pohon dependensi
- Contoh: di pohon
eslint@10.1.0, cabang find-up tampak terisolasi
- Fungsi pencarian file yang sederhana tidak membutuhkan 6 paket, sehingga alternatif yang lebih kecil seperti
empathic bisa digunakan
-
Proyek module replacements
- Komunitas memelihara dataset pemetaan paket yang bisa diganti dan fitur native
- Lewat proyek codemods, migrasi otomatis juga didukung
Kesimpulan
- Pembengkakan saat ini adalah struktur di mana seluruh ekosistem menanggung biaya karena sebagian kecil pengguna masih ingin mempertahankan kompatibilitas lama atau struktur yang tidak umum
- Dulu hal ini tidak terhindarkan, tetapi sekarang menjadi beban yang tidak perlu karena engine dan API modern sudah sangat berkembang
- Ke depan, kelompok kecil ini perlu memelihara stack terpisah, sementara yang lain beralih ke basis kode yang ringan dan modern
- Proyek seperti e18e dan npmx mendukung arah ini lewat dokumentasi dan tooling, dan setiap developer juga perlu meninjau dependensinya sendiri serta bertanya “mengapa ini diperlukan?”
- Kita semua bisa merapikannya bersama
2 komentar
Saat saya membuat library, saya sendiri masih menyediakan build cjs,
namun saya berharap library yang bahkan di tahun 2026 belum punya contoh esm sama sekali dan semuanya masih berbasis
requirebisa sedikit diperbarui.Komentar Hacker News
Akhir-akhir ini menurut saya arah terbaik adalah mengembangkan dengan JavaScript tanpa dependensi
Library standar JS/CSS juga sudah sangat bagus, dan analisis statis (pengecekan JSDoc di TypeScript), modul ES, web components, dan sebagainya juga sudah cukup kuat
Orang-orang bilang pendekatan ini kurang unggul untuk skalabilitas atau pemeliharaan, tetapi dari pengalaman saya justru lebih mudah mempertahankan struktur yang sederhana dan gampang diubah
Sebagian besar hal yang dilakukan framework atau build tool bisa digantikan dengan fitur bawaan browser dan pola vanilla
Hanya saja, pendekatan ini masih terasa asing, jadi masalahnya adalah ekosistem tutorial kebanyakan berpusat pada framework besar
Dalam praktiknya, bahkan jika kode React dipindahkan sepenuhnya ke vanilla, modularitas tetap terjaga dan panjang kode hanya bertambah sekitar 1,5 kali, sementara performa justru lebih baik karena tidak ada dependensi
Tentu bukan berarti dependensi itu buruk. Hanya saja banyak developer terjebak dalam anggapan baku bahwa itu “harus dipakai”
Misalnya saya membuat situs dengan banyak fitur peta, jadi saya harus memakai library seperti mapbox/maplibre/openlayers yang nyaris tidak punya pengganti
Klien juga tidak mengeluarkan biaya sepeser pun untuk migrasi
Saya penasaran bagaimana pembaruan model ditangani, seperti dijelaskan di artikel ini
Justru jadi lebih mudah memelihara codebase skala besar dengan tim kecil
Berkat tool masa kini, implementasi mandiri jauh lebih mudah dibanding dulu, dan ini juga cocok dengan agentic engineering
Tulisannya bagus, tidak emosional, dan menjelaskan masalahnya dengan jelas
Saya rasa salah satu penyebab situasi ini adalah JS belum punya “library standar” yang benar-benar memadai
Artikel yang bagus, tetapi menurut saya akar masalahnya adalah penambahan yang tidak perlu (=bloat) itu sendiri
Saya ingin mengutip Saint-Exupéry: “kesempurnaan dicapai bukan ketika tidak ada lagi yang bisa ditambahkan, tetapi ketika tidak ada lagi yang bisa dihilangkan”
Sebagian besar software ditulis dengan pertanyaan “bagaimana cara menambahkan dengan lebih mudah?” alih-alih “bagaimana cara membuatnya lebih elegan?”
Jawabannya selalu
npm i more-stuffSeperti kontras antara Demosthenes dan Cicero, kode yang baik adalah kode yang tidak lagi bisa dikurangi
JS harus mempertimbangkan kompatibilitas browser masa lalu dan masa depan, dan karena berpusat pada UI, ukurannya membengkak akibat aksesibilitas, internasionalisasi, dukungan mobile, dan sebagainya
Dalam banyak kasus, ini terlihat seperti masalah utang teknis yang tersembunyi
Penyebabnya adalah target kompilasi tidak dinaikkan ke ESx, dan paket maupun implementasi tidak diperbarui
ES5 sudah didukung semua browser selama 13 tahun (caniuse.com/es5)
Keduanya menganggap tindakan mereka sebagai fitur, dan mereka memelihara banyak paket populer
Karena itu, sulit berubah. Kadang komunitas mengkritik mereka, tetapi mereka juga punya logika sendiri
Jika ditranspilasi ke versi lama dengan Babel, kodenya jadi besar dan lambat, sementara di browser lama tetap tidak berjalan karena keterbatasan CSS atau fitur JS
Bahkan polyfill juga pernah menimbulkan masalah (polyfill operator eksponen yang tidak bisa menangani BigInt)
Ada banyak lingkungan seperti konsol, TV, Android lama, iPod touch, browser bawaan Facebook, dan lain-lain
Karena itu saya hanya memakai satu modul eksternal, dan sisanya diselesaikan lewat pengaturan transpiler
Dulu mereka menimpa
setTimeoutdan semacamnya untuk melacak asinkron, tetapi sekarang itu bisa ditangani jauh lebih sederhana dengan signalsSaya rasa beberapa penulis paket sengaja memecah pohon dependensi untuk meningkatkan jumlah unduhan
Tidak masuk akal ada paket yang hanya terdiri dari 7 baris. Metadata lockfile lebih besar daripada kodenya
Dulu sekitar 5% dependensi create-react-app adalah paket mini dari satu penulis
Ada contoh seperti has-symbols, is-string, ljharb
Misalnya Anthropic memberi Claude gratis kepada pemelihara open source dengan jumlah unduhan npm yang tinggi
Persaingan jumlah unduhan justru memperbesar risiko
Tetapi di budaya lain, justru itu dianggap hal yang baik
Sebelum mengkritik ekosistem JS, sebaiknya baca 30 years of br tags
Itu membantu memahami proses evolusi JS dan tool-toolnya
Mengatakan sekadar “developer JS yang bermasalah” menunjukkan kurangnya cara berpikir rekayasa
Kita harus terus memikirkan teori dan praktik yang lebih baik
Karena dunia software berubah cepat, kita perlu mengadakan “pemakaman palsu” bagi diri sendiri dan meninggalkan kebiasaan usang
Saya sedang mengelola codebase Node.js berusia 9 tahun, dan dependensinya hanya 8, semuanya tanpa dependensi turunan
Saya memprioritaskan fitur bawaan Node dan hanya mengimplementasikan bagian yang perlu
Hasilnya jauh lebih stabil dan jauh lebih sedikit stres
Library standar Deno juga sangat bagus, jadi dengan fitur bawaan runtime, beberapa paket saja sudah cukup untuk membangun aplikasi
JS adalah bahasa yang cukup bagus jika didekati dengan hati-hati
Saya paham klaim keamanan lintas realm dari paket seperti
is-string, tetapi situasi seperti itu jarang terjadiMasalahnya adalah npm membuat publikasi terlalu mudah, sehingga filosofi “pecah modul lalu publikasikan” mengalami ekspansi berlebihan
Konsumen tidak mengaudit pohon dependensi dan langsung menginstalnya, sehingga biaya yang seharusnya opsional menjadi biaya default
Masalah ponyfill seharusnya bisa diatasi dengan otomasi
Misalnya bot bergaya Renovate yang otomatis mendeteksi fitur yang sudah didukung di versi Node LTS lalu menghapusnya akan sangat membantu
Prinsip PWA internal kami cuma satu:
“Upgrade ke Chrome versi terbaru. Kalau masih bermasalah, baru kita lihat”
Saya paham Safari lebih hemat memori, tetapi standarisasi lewat kebijakan lebih efisien
Saya benar-benar sulit memahami pernyataan “harus mendukung sampai ES3 (setara IE6/7)”
Dari sisi keamanan, bahkan situs bank pun seharusnya memblokir browser setua itu
Upgrade stack Webpack, Babel, dan polyfill adalah pekerjaan besar, jadi akhirnya dibiarkan begitu saja
Budayanya seperti “kalau belum rusak, jangan diperbaiki”