Paket Node.js saling bergantung satu sama lain melalui deklarasi dependensi pada package.json. Setiap dependensi menyebut nama paket dan rentang versi yang diterima. Topik ini membahas skema semver, arti operator caret dan tilde, serta implikasi perubahan mayor yang dapat merusak kode. Setelah membaca topik ini, deklarasi dependensi dapat ditulis dengan sengaja, bukan sekadar menerima apa pun yang dipasang npm.
Skema semver#
Semver adalah singkatan dari Semantic Versioning, skema penomoran versi tiga segmen MAJOR.MINOR.PATCH. Setiap segmen menyatakan jenis perubahan berbeda. Nomor PATCH naik untuk perbaikan yang kompatibel dengan API lama, misalnya perbaikan bug yang tidak mengubah kontrak fungsi. Nomor MINOR naik untuk penambahan fitur baru yang tetap kompatibel dengan API lama. Nomor MAJOR naik untuk perubahan yang merusak kompatibilitas, misalnya penghapusan fungsi, perubahan tanda tangan, atau perubahan perilaku baku.
Skema ini menjadi kontrak antara pemelihara paket dan pengguna. Pengguna yang memasang versi 1.2.3 dapat mengharapkan bahwa 1.2.4 dan 1.3.0 kompatibel, sedangkan 2.0.0 berpotensi merusak kode yang ada. Kontrak ini hanya berlaku bila pemelihara mematuhi semver, sehingga pemilihan paket juga perlu mempertimbangkan rekam jejak pemelihara dalam menjaga kompatibilitas.
Caret dan tilde#
Deklarasi dependensi pada package.json jarang menyebut versi persis, melainkan rentang yang memungkinkan npm memasang versi lebih baru selama masih kompatibel. Dua operator yang paling lazim adalah caret (^) dan tilde (~).
Caret membatasi rentang pada perubahan yang tidak merusak menurut semver. Untuk versi ^1.2.3, rentang yang diterima adalah >=1.2.3 <2.0.0, yaitu seluruh versi 1.x.x mulai dari 1.2.3. Tilde lebih konservatif: ~1.2.3 menerima >=1.2.3 <1.3.0, yaitu hanya versi 1.2.x. Penulisan tanpa operator (1.2.3) memaksa npm memasang persis versi tersebut.
Demo berikut memasang paket semver dengan tiga operator berbeda pada direktori terpisah di /tmp, lalu memeriksa versi yang dipasang. Versi 7.5.0 dipakai sebagai batas bawah karena tersedia banyak rilis 7.x.x setelahnya.
# siapkan tiga direktori demo (bersihkan dulu bila ada)
rm -rf /tmp/semver-caret /tmp/semver-tilde /tmp/semver-exact
mkdir -p /tmp/semver-caret /tmp/semver-tilde /tmp/semver-exact
echo "=== caret (^7.5.0) ==="
(cd /tmp/semver-caret && npm init -y >/dev/null && npm install semver@^7.5.0 >/dev/null 2>&1 && npm ls semver)
echo "=== tilde (~7.5.0) ==="
(cd /tmp/semver-tilde && npm init -y >/dev/null && npm install semver@~7.5.0 >/dev/null 2>&1 && npm ls semver)
echo "=== exact (7.5.0) ==="
(cd /tmp/semver-exact && npm init -y >/dev/null && npm install [email protected] >/dev/null 2>&1 && npm ls semver)=== caret (^7.5.0) ===
[email protected] /tmp/semver-caret
└── [email protected]
=== tilde (~7.5.0) ===
[email protected] /tmp/semver-tilde
└── [email protected]
=== exact (7.5.0) ===
[email protected] /tmp/semver-exact
└── [email protected]Caret memasang 7.8.5 karena versi tersebut adalah rilis 7.x.x tertinggi yang tersedia. Tilde memasang 7.5.4 karena versi tersebut adalah rilis 7.5.x tertinggi. Penulisan persis memasang 7.5.0 apa adanya. npm menambahkan caret secara default saat memakai npm install <pkg>, sehingga deklarasi ^7.5.0 muncul di package.json meski perintah hanya menyebut [email protected].
{
"dependencies": {
"semver": "^7.5.0",
"is-odd": "~3.0.1",
"debug": "4.3.4"
}
}Pada contoh di atas, semver menerima kenaikan minor dan patch dalam major 7, is-odd menerima kenaikan patch dalam minor 3.0, dan debug dipasang persis pada versi 4.3.4 tanpa ruang untuk pembaruan otomatis. Kombinasi ini lazim dipakai untuk dependensi inti yang stabil, dependensi yang sangat sensitif terhadap patch, dan dependensi yang telah dikunci penuh karena alasan kompatibilitas.
Perubahan yang merusak#
Perubahan mayor pada sebuah dependensi berpotensi merusak kode pengguna. Karena caret membatasi rentang pada major yang sama, kenaikan major pada pemelihara paket tidak otomatis terpasang. Pengguna harus sengaja mengubah deklarasi, misalnya dari ^1.2.3 ke ^2.0.0, lalu memverifikasi bahwa kode tetap berjalan.
Perubahan yang merusak datang dalam beberapa bentuk. Penghapusan fungsi publik menyebabkan error not a function saat pemanggilan lama. Perubahan tanda tangan fungsi, misalnya parameter kedua dihilangkan atau urutan diubah, menyebabkan perilaku salah tanpa error. Perubahan nilai kembalian dari string menjadi objek menyebabkan kode yang mengakses properti string lama gagal. Perubahan default perilaku, misalnya waktu habis yang tadinya tak terhingga menjadi 30 detik, menyebabkan bug yang muncul hanya pada beban nyata.
npm tidak dapat mendeteksi perubahan semantik. Verifikasi kenaikan major dilakukan oleh pengguna lewat pengujian. Karena itu, kenaikan major pada dependensi inti proyek sebaiknya dilakukan pada cabang terpisah dengan pengujian menyeluruh sebelum digabung.
Memilih package#
Pemilihan dependensi mempertimbangkan beberapa hal. Perawakan pemelihara terlihat dari frekuensi rilis, kecepatan respons terhadap isu, dan konsistensi penomoran versi. Paket dengan riwayat kenaikan major yang disertai catatan migrasi terperinci memberi sinyal positif. Ukuran paket memengaruhi waktu instalasi dan ukuran bundle akhir, sehingga untuk kasus yang sensitif terhadap ukuran, bandingkan alternatif yang lebih ringan.
Dokumentasi dan contoh pemakaian menentukan apakah API dapat dipelajari tanpa membaca sumber. Paket dengan halaman dokumentasi terstruktur dan contoh kode yang dapat dijalankan mengurangi waktu integrasi. Lisensi juga perlu diperiksa: lisensi permissive seperti MIT dan Apache-2.0 umumnya aman dipakai pada proyek proprietary, sedangkan lisensi copyleft seperti GPL memerlukan pertimbangan hukum lebih lanjut.
Kesalahan umum#
Penulisan rentang tanpa operator pada seluruh dependensi (1.2.3) membuat setiap pembaruan dependensi memerlukan perubahan package.json manual. Pendekatan ini stabil tetapi mahal, dan tidak memanfaatkan perbaikan bug otomatis pada patch. Gunakan caret sebagai default, dan tilde bila perlu membatasi pada minor tertentu.
Pemakaian rentang sangat longgar seperti * atau latest memasang versi apa pun, termasuk major baru yang merusak. Hindari rentang ini pada dependensi produksi. Penyebutan latest secara eksplisit pada npm install <pkg>@latest memperbarui versi terpasang ke yang paling baru saat ini. Namun deklarasi pada package.json tetap memakai rentang lama, sehingga bila rentang lama tidak lagi terpenuhi oleh versi terbaru, npm install berikutnya dapat memasang versi yang berbeda.
Pengabaian terhadap peerDependencies sering menyebabkan error runtime yang pesannya membingungkan. Bila paket memerlukan peer tertentu (mis. versi React yang spesifik), pasang peer yang sesuai sebelum memverifikasi integrasi.
Ringkasan#
- Semver menomori versi sebagai
MAJOR.MINOR.PATCH, di mana major naik untuk perubahan yang merusak, minor untuk fitur tambahan kompatibel, dan patch untuk perbaikan kompatibel. - Caret
^1.2.3menerima>=1.2.3 <2.0.0, tilde~1.2.3menerima>=1.2.3 <1.3.0, dan1.2.3memasang persis versi tersebut. - Kenaikan major pada dependensi tidak otomatis terpasang oleh caret, sehingga verifikasi perubahan major dilakukan secara sengaja lewat pengujian.
- Pemilihan paket mempertimbangkan perawakan pemelihara, ukuran, dokumentasi, dan lisensi, bukan hanya popularitas.
Referensi#
- About semantic versioning — npm Docs — Penjelasan npm mengenai semver dan bagaimana operator caret serta tilbe diterapkan pada deklarasi dependensi.
- semver: Semantic Versioning 2.0.0 — semver.org — Spesifikasi resmi Semantic Versioning yang menjadi acuan ekosistem npm.
- Dependencies — npm Docs
— Daftar bidang
dependencies,devDependencies, danpeerDependenciespadapackage.json.