10 poin oleh GN⁺ 29 hari lalu | 2 komentar | Bagikan ke WhatsApp
  • 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

 
click 29 hari lalu

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 require bisa sedikit diperbarui.

 
GN⁺ 29 hari lalu
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

    • Saya juga sudah beberapa tahun bereksperimen dengan pendekatan ini, dan bahkan membuat situs tutorial bernama plainvanillaweb.com
      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”
    • Untuk halaman marketing yang sederhana mungkin bisa, tetapi untuk aplikasi dengan banyak fitur, sering kali dependensi itu wajib
      Misalnya saya membuat situs dengan banyak fitur peta, jadi saya harus memakai library seperti mapbox/maplibre/openlayers yang nyaris tidak punya pengganti
    • Saya mengerjakan proyek dengan cara ini pada 2022, dan sama sekali tidak mengalami masalah terkait CVE atau migrasi versi
      Klien juga tidak mengeluarkan biaya sepeser pun untuk migrasi
    • Merender komponen itu mudah, tetapi inti framework adalah menyediakan pembaruan reaktif pada model
      Saya penasaran bagaimana pembaruan model ditangani, seperti dijelaskan di artikel ini
    • Saya sudah hampir 20 tahun menangani JS, dan pada akhirnya menetap pada pendekatan memakai dependensi seminimal mungkin lalu membuat sisanya sendiri
      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

    • Belakangan ini library standar JS sudah cukup besar, jadi saya penasaran fitur apa yang menurut Anda masih kurang
    • Saya justru suka tulisan bernada rant. Itu membantu memahami bukan hanya emosi orang, tetapi juga alasannya
  • 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-stuff

    • Ini mengingatkan saya pada aturan menulis Kurt Vonnegut: “setiap kalimat harus mengungkapkan karakter atau mendorong tindakan maju”
      Seperti kontras antara Demosthenes dan Cicero, kode yang baik adalah kode yang tidak lagi bisa dikurangi
    • Semua software punya bagian yang tidak perlu, tetapi paket npm dan web app terasa paling parah
      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)

    • Kenyataannya ada orang yang ingin mendukung engine JS lama, dan ada juga yang membuat banyak sekali paket mini
      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
    • Keinginan mempertahankan kompatibilitas di bawah ES6 terasa aneh
      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)
    • Bukan hanya browser lama, tetapi juga perlu mendukung browser aneh
      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
    • Web punya budaya “rilis sekarang, perbaiki nanti”, jadi dependensi usang bertahan lama
    • Seperti keputusan desain Angular, struktur masa lalu kadang menyebabkan inefisiensi saat ini
      Dulu mereka menimpa setTimeout dan semacamnya untuk melacak asinkron, tetapi sekarang itu bisa ditangani jauh lebih sederhana dengan signals
  • Saya 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

    • Saya penasaran apakah tindakan seperti ini cuma soal gengsi, atau memang ada keuntungan nyata
      Misalnya Anthropic memberi Claude gratis kepada pemelihara open source dengan jumlah unduhan npm yang tinggi
    • Seperti tulisan immich.app/cursed-knowledge, ada juga orang yang menambahkan 50 paket dengan alasan “kompatibilitas mundur”
    • Dari sisi keamanan ini juga serius. Setiap mikro paket seperti ini menjadi permukaan serangan
      Persaingan jumlah unduhan justru memperbesar risiko
    • Dulu pernah ada orang yang menyusup ke organisasi lalu menambahkan paketnya sendiri ke dependensi demi menaikkan jumlah bintang untuk CV
    • Ini juga masalah budaya. Di komunitas JS, menyalin-tempel kode 7 baris sendiri sering dikritik sebagai “menciptakan ulang roda
      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

    • Sikap ingin memahami kenyataan buruk itu baik, tetapi penerimaan yang berlebihan adalah sikap kalah sebelum bertanding
      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
    • Bagi saya tulisan ini terasa bukan menyalahkan developer, melainkan kritik yang rasional terhadap keadaan saat ini
  • 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 terjadi
    Masalahnya 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”

    • Untuk penggunaan internal, ini memang pendekatan yang tepat. Jika perusahaan menentukan browser yang didukung, pengelolaannya jadi mudah
      Saya paham Safari lebih hemat memori, tetapi standarisasi lewat kebijakan lebih efisien
    • Menjaga semuanya tetap sederhana pada akhirnya memberi manfaat terbesar
  • 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

    • Tim seperti ini biasanya masih memakai build tool yang disetel sekitar 2015 dan belum pernah diubah
      Upgrade stack Webpack, Babel, dan polyfill adalah pekerjaan besar, jadi akhirnya dibiarkan begitu saja
      Budayanya seperti “kalau belum rusak, jangan diperbaiki”
    • Memang ada tokoh tertentu yang mendorong dukungan versi lama seperti itu, dan dia memelihara banyak paket tingkat rendah
    • Sebagai referensi, saya juga pernah dengar cerita bahwa Deutsche Bahn masih memakai Windows 3.1