- Sistem manajemen dependensi Rust memang memudahkan pengembangan, tetapi jumlah dan kualitas dependensi menjadi kekhawatiran
- Bahkan crate yang umum dipakai pun belum tentu selalu mutakhir, sehingga kadang lebih baik mengimplementasikannya sendiri
- Setelah menambahkan crate populer seperti Axum dan Tokio, total jumlah baris kode termasuk dependensi mencapai 3,6 juta sehingga sulit ditangani
- Kode yang benar-benar saya tulis hanya sekitar 1.000 baris, tetapi kode di sekitarnya praktis tidak mungkin saya telaah dan audit
- Belum ada solusi yang jelas soal apakah pustaka standar Rust perlu diperluas dan bagaimana infrastruktur inti sebaiknya diimplementasikan; komunitas secara keseluruhan perlu memikirkan keseimbangan antara performa, keamanan, dan pemeliharaan bersama-sama
Gambaran umum masalah dependensi Rust
- Rust adalah bahasa favorit saya, dan komunitas serta kemudahan penggunaan bahasanya sangat unggul
- Produktivitas pengembangan tinggi, tetapi belakangan saya mulai khawatir dari sisi manajemen dependensi
Kelebihan crate Rust dan Cargo
- Dengan Cargo, manajemen paket dan otomatisasi proses build bisa dilakukan sehingga produktivitas meningkat pesat
- Perpindahan antar berbagai sistem operasi dan arsitektur menjadi mudah, dan kita tidak perlu terlalu memikirkan pengelolaan file manual atau konfigurasi alat build
- Kita bisa langsung menulis kode tanpa perlu banyak memikirkan manajemen paket terpisah
Kekurangan pengelolaan crate Rust
- Karena jadi lebih jarang memikirkan manajemen paket, kita bisa lengah terhadap stabilitas
- Misalnya, saya memakai crate dotenv, lalu mengetahui lewat Security Advisory bahwa pemeliharaannya telah dihentikan
- Sambil mempertimbangkan crate pengganti (dotenvy), pada akhirnya saya hanya mengimplementasikan sendiri bagian yang benar-benar dibutuhkan dalam sekitar 35 baris
- Masalah paket yang tidak lagi dipelihara sering terjadi di banyak bahasa, jadi inti persoalannya adalah situasi ketika dependensi tidak bisa dihindari
Ledakan jumlah kode akibat dependensi
- Saya menggunakan paket penting dan berkualitas tinggi dalam ekosistem Rust seperti Tokio dan Axum
- Sebagai dependensi, saya menambahkan Axum, Reqwest, ripunzip, serde, serde_json, tokio, tower-http, tracing, dan tracing-subscriber
- Tujuan utamanya hanya web server, ekstraksi file terkompresi, dan logging, jadi proyeknya sendiri sederhana
- Saya memanfaatkan fitur Cargo vendor untuk mengunduh semua crate dependensi secara lokal
- Setelah menganalisis jumlah baris kode dengan tokei, totalnya mencapai sekitar 3,6 juta baris termasuk dependensi (sekitar 11.136 baris jika crate yang di-vendor tidak dihitung)
- Sebagai perbandingan, keseluruhan kernel Linux diketahui sekitar 27,8 juta baris, jadi proyek kecil saya setara sekitar sepertujuh jumlah itu
- Kode yang benar-benar saya tulis sendiri hanya sekitar 1.000 baris
- Mengawasi dan melakukan audit terhadap kode dependensi sebanyak itu pada praktiknya hampir mustahil
Mencari solusi
- Untuk saat ini, belum ada solusi yang benar-benar jelas
- Sebagian orang berpendapat pustaka standar perlu diperluas seperti Go, tetapi itu juga menimbulkan masalah baru seperti beban pemeliharaan
- Rust mengejar performa tinggi, keamanan, dan modularitas, serta menargetkan persaingan di ranah embedded dan C++, jadi perluasan pustaka standarnya harus dipertimbangkan dengan hati-hati
- Misalnya, runtime canggih seperti Tokio juga dikelola sangat aktif di Github dan Discord
- Secara realistis, mengimplementasikan sendiri infrastruktur inti seperti runtime asinkron atau web server terlalu berat bagi pengembang individu
- Bahkan layanan besar seperti Cloudflare juga menggunakan dependensi tokio dan crates.io apa adanya, dan tidak jelas seberapa sering mereka melakukan audit
- Clickhouse juga menyinggung masalah ukuran biner dan jumlah crate
- Dengan Cargo, sulit untuk mengidentifikasi secara tepat baris kode mana yang benar-benar masuk ke biner akhir, dan ada keterbatasan karena kode yang tidak diperlukan untuk platform tertentu pun ikut tercakup
- Pada akhirnya, kenyataannya kita hanya bisa meminta jawaban dari komunitas secara keseluruhan
3 komentar
Kalau dijalankan dengan Trivy, dibandingkan js NPM atau Java Maven, jumlah temuan high atau critical jauh lebih sedikit dan lebih aman. Jadi, tulisan ini sebenarnya ingin menyampaikan apa dengan membawa-bawa Rust?
Komentar Hacker News
import foolib, dan tak ada yang peduli apa isi di dalamnya. Di setiap lapisan mungkin kita hanya butuh sekitar 5% fungsinya, tetapi makin dalam pohon dependensinya, makin banyak kode tak berguna yang ikut menumpuk. Akhirnya satu biner sederhana bisa jadi 500MiB, dan bahkan untuk memformat angka saja kita malah menarik sebuah dependensi. Go dan Rust mendorong semuanya dimasukkan ke satu berkas, jadi kalau hanya ingin memakai sebagian kecil saja situasinya jadi serba tanggung. Dalam jangka panjang, solusi sebenarnya tampaknya adalah pelacakan simbol/dependensi yang sangat rinci, sehingga setiap fungsi/tipe menyatakan dengan tepat elemen yang dibutuhkan, lalu hanya kode itu yang dipakai dan sisanya dibuang. Saya pribadi tidak terlalu suka ide ini, tetapi saya juga belum melihat cara lain untuk memperbaiki sistem saat ini yang menyeret seluruh alam semesta dari pohon dependensiserde_jsonyang bisa saya lepaskan dengan sedikit modifikasi. Dependensi yang lebih besar (winit/wgpudan lain-lain) butuh perubahan arsitektur sehingga tidak mudah dilepas.otersendiri lalu dibundel ke dalam arsip.a, dan linker hanya mengambil fungsi yang memang diperlukan. Namespacing pun dilakukan dengan gaya sepertifoolib_do_thing(). Sekarang polanya lebih mirip god object, di mana objek top-level memegang semua fungsi, jadi begitufoolibdi-import, semuanya ikut tertarik. Dalam keadaan seperti itu, linker sulit menentukan fungsi mana yang benar-benar wajib. Sebaliknya, Go sangat bagus dalam menghapus dead code, jadi bagian yang tak dipakai akan dipotong dari hasil kompilasimin-sized-rustisEven,isOdd,leftpaddi npm yang terdiri dari banyak potongan pustaka kecil, pustaka umum yang besar dan dikelola tim federatif jauh lebih menjamin masa depan dan kesinambungan--gc-sectionsglobseharusnya hanya fungsi globbing sederhana, tetapi penulisnya juga membundel alat command line sehingga dependensinya ikut menarik parser besar. Ini menyebabkan peringatandependency out-of-datemuncul terus-menerus. Selain itu, ruang lingkup tanggung jawab pustakaglobsendiri juga masih bisa diperdebatkan. Hanya melakukan pencocokan pola string sebenarnya desain yang lebih fleksibel, misalnya untuk testing atau abstraksi filesystem. Banyak pengguna memang ingin pustaka serba bisa yang "melakukan semuanya", tetapi makin begitu makin besar pula efek sampingnya. Saya rasa Rust pun tidak jauh berbedastdlib::data_structures::automata::weighted_finite_state_transducer, dengan namespace yang rapi dan pendekatan "batteries included". Karena bahasanya sendiri sudah menanamkan versioning dan kompatibilitas mundur, saya optimistis masih bisa berkembang ke arah sanaglobPOSIX memang benar-benar menelusuri filesystem. Untuk pencocokan string adafnmatch. Idealnyafnmatchdipisah jadi modul tersendiri lalu menjadi dependensi dariglob. Kalau mencoba mengimplementasikangloblangsung, ternyata cukup sulit karena ada kebutuhan kompleks seperti struktur direktori dan brace expansion, jadi memang perlu kombinasi fungsi yang dirancang dengan baikglob#![deny(unsafe_code)], penggunaan kode unsafe bisa diblokir sebagai error kompilasi dan fakta itu bisa diinformasikan ke pengguna. Namun ini bukan pemeriksaan paksa total, karena unsafe tetap bisa dipakai jika secara khusus diizinkan. Bisa dibayangkan capability system yang seperti feature flag, yang mengendalikan kemampuan standard library secara transitifpanic, lalu siapkan dan distribusikan capability profile untuk tiap pustaka. Hal serupa pada dasarnya sudah terbukti di TypeScripttls,x509,base64, dan sebagainya masih menyakitkan karena harus memilih dan mengelola pustaka sendiriunsafe codedom0, tiap pustaka di template VM terpisah, dan komunikasi memakai network namespace. Untuk industri sensitif, ini cukup praktisblessed.rsmerekomendasikan daftar pustaka berguna yang sulit dimasukkan ke standard library. Saya suka sistem ini karena membuat sebagian besar paket tetap dibatasi untuk tujuan tertentu dan lebih mudah dikelolacargo-vetjuga layak direkomendasikan. Ia bisa melacak dan mendefinisikan paket yang dipercaya, misalnya dari paket yang wajib diaudit pakar sebelum diimpor sampai kebijakan semi-YOLO seperti "paket yang dikelola maintainer tokio anggap saja tepercaya". Pendekatannya lebih formal daripadablessed.rs, dan bagus sebagai sarana berbagi daftar kuasi-standar resmi di dalam timleftpad, citra package manager masih menyisakan kesan negatif. Hal sepertitokiopada dasarnya sudah setara fitur tingkat bahasa, jadi kalau OP menganggap seluruh Go atau bahkan V8 di Node juga harus diaudit langsung, itu tidak realististokiojuga memang terus diaudit oleh seseorang. Memang bukan banyak orang, tetapi tetap ada yang melakukannyacargo tree, pohon dependensi juga sangat mudah dilihat. Tampilan jumlah baris kode yang benar-benar masuk ke biner sebenarnya kurang bermakna, karena kalau fungsi di-inline, sebagian besar akhirnya menyatu kemainIni bukan masalah yang hanya dimiliki Rust.
Semua bahasa yang punya pengelola paket dengan repositori paket publik dan mendukung dependensi transitif memiliki keunggulan sekaligus potensi masalah yang sama.
Pada akhirnya, orang yang memakainya harus menggunakannya dengan benar…
Meski sudah ada insiden leftpad di Node&npm, tidak ada yang berubah.