- uv unggul dalam kecepatan, manajemen versi Python, dan integrasi dalam satu biner, tetapi UX pengelolaan paket pada tahap pemeliharaan masih terasa kasar
- Paket lama bisa diperiksa dengan
uv pip list --outdated, tetapi karena berada di bawah namespace kompatibilitas pip dan bukan perintah tingkat atas, tingkat ketertemuannya rendah
uv add pydantic secara default menambahkan batasan tanpa batas atas seperti pydantic>=2.13.4, sehingga kenaikan versi mayor pun diizinkan
- Upgrade penuh dilakukan dengan
uv lock --upgrade, dan untuk beberapa paket tertentu perlu mengulang --upgrade-package, sehingga perintah menjadi panjang
- Dengan pengaturan
add-bounds = "major", batasan default yang lebih aman bisa diterapkan, tetapi ini masih fitur pratinjau dan aplikasi membutuhkan UX pembaruan yang lebih intuitif
Kelebihan uv dan ketidaknyamanan pada tahap pemeliharaan
- uv dari Astral punya keunggulan pada kecepatan, manajemen versi Python, dan kemampuannya menggantikan beberapa alat dengan satu biner
- Alur memulai proyek Python baru dan menambahkan dependensi pertama memang mudah, tetapi ketika proyek masuk ke tahap pemeliharaan, UX untuk memeriksa paket lama dan melakukan upgrade berkala terasa lebih kasar dibanding pnpm atau Poetry
- Ketidaknyamanan utamanya terletak pada ketertemuan perintah untuk memeriksa paket lama, tidak adanya batas atas pada batasan versi default, dan panjangnya perintah upgrade
Memeriksa paket yang sudah usang
- Dalam proyek JavaScript,
pnpm outdated memungkinkan kita melihat paket yang sudah usang, versi saat ini, versi terbaru, dan versi yang masih diizinkan oleh constraint secara ringkas
- Di uv tidak ada perintah tingkat atas
uv outdated, dan pada awalnya perintah berikut digunakan sebagai alternatif
$ uv tree --outdated --depth 1
uv tree --outdated --depth 1 tidak hanya memfilter item yang usang, tetapi menampilkan seluruh pohon dependensi tingkat atas lalu menambahkan anotasi kecil di samping item yang bisa diperbarui
- Jika ada 50 dependensi dan hanya 2 paket yang usang, kita tetap harus menelusuri daftar 50 baris
poetry show --outdated juga punya nama perintah yang kurang intuitif, tetapi output nyatanya hanya menampilkan paket yang usang
Risiko batasan versi default
-
Pendekatan default pnpm dan Poetry
pnpm add menulis requirement caret seperti ^1.23.4 ke package.json
^1.23.4 mengizinkan versi 1.x.x, tetapi tidak akan memperbarui ke 2.0.0
- Poetry juga secara default menggunakan format seperti
>=1.23.4,<2.0.0; penulisannya memang kurang mudah dibaca, tetapi efeknya sama
- Pada kedua alat itu, dengan asumsi paket mengikuti SemVer, menjalankan
pnpm update atau poetry update dapat mengurangi kemungkinan build rusak akibat perubahan API mayor
-
Pendekatan default uv
uv add pydantic menambahkan batasan tanpa batas atas berikut ke pyproject.toml
dependencies = [
"pydantic>=2.13.4",
]
- Dengan batasan ini, pydantic versi 2, 3, bahkan 100 semuanya diizinkan
- Saat menjalankan pembaruan massal, kita bukan hanya menerima perbaikan bug, tetapi juga perubahan yang memutus kompatibilitas yang dirilis oleh semua pemelihara di dalam graf dependensi
- Khususnya dalam pemeliharaan aplikasi, ini bisa menjadi risiko stabilitas
UX perintah upgrade
- Di pnpm dan Poetry, pembaruan penuh sesederhana berikut
$ pnpm update
$ poetry update
- Di uv, perintah berikut digunakan untuk upgrade penuh
$ uv lock --upgrade
uv lock --upgrade bukan uv update atau uv upgrade, melainkan opsi dari perintah lock, sehingga terasa kurang intuitif sebagai perintah pengelolaan paket yang digunakan manusia
- Jika digabungkan dengan batasan tanpa batas atas,
uv lock --upgrade pada dasarnya menjadi pilihan untuk menaikkan semua paket di lockfile ke versi terbaru mutlak
- Pembaruan ini juga bisa mencakup dependensi bertingkat dalam yang bahkan tidak kita ketahui secara langsung
- Untuk memperbarui hanya paket tertentu, pnpm cukup menuliskan nama paket seperti berikut
$ pnpm update pydantic httpx uvicorn
- Di uv, kita harus mengulang flag
--upgrade-package untuk setiap paket
$ uv lock --upgrade-package pydantic --upgrade-package httpx --upgrade-package uvicorn
- Saat menaikkan beberapa paket sekaligus, pengulangan flag menjadi kerepotan besar
Flag --bounds dan pengaturan
- uv baru-baru ini menambahkan opsi
--bounds untuk uv add
$ uv add pydantic --bounds major
- Perintah ini menghasilkan batasan yang lebih aman, yaitu
pydantic>=2.13.4,<3.0.0
--bounds major saat ini adalah fitur pratinjau dan merupakan fitur opt-in yang harus diketik langsung pada setiap perintah
- Belakangan diketahui bahwa nilai default juga bisa diatur sekali di
pyproject.toml
[tool.uv]
add-bounds = "major"
- Dengan pengaturan ini, kita bisa mendapatkan default yang lebih masuk akal pada
uv add berikutnya tanpa harus mengetik --bounds major setiap kali
- Untuk aplikasi, perilaku ini lebih baik jika menjadi default, tetapi dari sisi kenyamanan penggunaan nyata, keadaannya tidak seburuk yang digambarkan pada awalnya
Perbedaan aplikasi dan library
- Saran standar dalam packaging Python adalah agar library yang didistribusikan ke PyPI tidak mengunci batas atas, dan saran ini memang masuk akal
- Jika semua library mengunci batas atas, pohon dependensi konsumen di bawahnya bisa gagal diresolusikan
- Sebaliknya, aplikasi adalah simpul ujung dari graf dependensi, dan tidak ada pengguna lain yang melakukan resolusi berdasarkan constraint tersebut
- Dalam aplikasi, tidak ada biaya dari penggunaan batas atas, dan itu bisa melindungi dari kenaikan versi mayor yang tak terduga
- Ruang lingkup pembahasan di sini adalah pemeliharaan aplikasi seperti situs web, layanan, atau alat internal; untuk distribusi library, default tanpa batas atas bisa jadi masuk akal
Bagian yang telah dikoreksi dan masalah yang tersisa
- Dengan
uv pip list --outdated, kita bisa melihat hanya paket yang sudah usang
$ uv pip list --outdated
- Karena itu, kritik terhadap output yang berisik dari
uv tree --outdated --depth 1 menjadi berkurang
- Masalah yang tersisa adalah fitur ini berada di bawah namespace kompatibilitas
pip, bukan sebagai perintah tingkat atas, sehingga ketertemuannya rendah
- Dengan pengaturan
add-bounds = "major", kita bisa menentukan bounds default, jadi ini bukan lagi pilihan biner antara harus mengedit batas atas secara manual setiap kali atau menerima risiko
- Meski begitu, fitur tersebut masih pratinjau, dan dalam pengelolaan paket aplikasi tetap dibutuhkan batasan default yang lebih aman serta perintah pembaruan yang lebih intuitif
Arah perbaikan yang diharapkan
- Dibutuhkan perintah
uv outdated khusus yang dengan jelas hanya menampilkan paket yang sudah usang
- Dibutuhkan perintah
update yang lebih ergonomis agar tidak perlu mengulang flag saat memperbarui beberapa paket
- Batasan versi default seharusnya lebih mencerminkan ekspektasi stabilitas dari semantic versioning (SemVer)
- Dalam kondisi saat ini, masih ada beban untuk memeriksa setiap baris perubahan lockfile dengan rasa curiga
1 komentar
Komentar Hacker News
Rentang versi default untuk
uv addbisa ditetapkan sebagai pengaturan permanen, jadi tidak perlu diberikan setiap kaliReferensi: https://docs.astral.sh/uv/reference/settings/#add-bounds
Alasan tidak menambahkan batas atas secara default adalah karena itu menimbulkan banyak konflik yang tidak perlu di ekosistem, dan saat masih memakai Poetry saya juga pernah mengumpulkan materi terkait: https://github.com/zanieb/poetry-relax#references
Saat memakai dependensi di proyek web, saya ingin ada batas atas untuk mencegah perubahan yang breaking, dengan asumsi dependensi tersebut mengikuti SemVer
Mereka mendorong agar tidak menambahkan batas atas defensif, lalu terus membangun bagian besar ekosistem yang aktif pada setiap rilis untuk menemukan masalah kompatibilitas nyata, mengirim notifikasi otomatis kepada pemilik, dan memberikan jadwal yang jelas agar tetap masuk ke rilis "LTS" berikutnya
Sekarang tampaknya interpreter Cabal saja pun sudah cukup stabil, tetapi build nightly yang luas serta kegagalan dan pemblokiran yang terlihat kemungkinan membantu menjaga ekosistem tetap bisa di-resolve
add-bounds, dan ini berguna untuk proyek tempat pin dependensi yang presisi itu penting tetapi mudah terlewat oleh developer yang kurang berpengalaman, terutama produk akhir, bukan library--native-tlssecara permanenKarena konfigurasi Zscaler di kantor, UV selalu gagal tanpa flag ini
Saya juga penasaran apakah ada rencana mendukung fitur untuk menentukan versi Python kompatibel yang sesuai arsitektur tertentu. Ada satu paket yang dipelihara perusahaan saya yang harus memakai Python 32-bit, jadi saya selalu harus memberi
--python /path/to/32bitexclude-newerdipyproject.tomlSaat menjalankan
uv run,exclude-newerdihapus daripyproject.tomlSaya memang bisa menjalankan
uv run —-frozenatauuv run --exclude-newersetiap kali, tetapi rasanya itu bukan alur yang tepat, jadi saya penasaran apakah ada cara idiomatis yang saya lewatkanuvmembutuhkan satu hasil resolusi tunggal, jadi tanpa batas atas adalah desain yang disengajanpm bisa memasang hasil resolusi yang berbeda di bagian pohon yang berbeda, tetapi di Python itu bukan pilihan. Di Rye kami juga harus mengambil keputusan yang sama, dan memang tidak ada solusi yang lebih baik di sini
Menambahkan batas atas pada praktiknya bisa menghasilkan pohon yang tidak lagi bisa di-resolve. Sebagian ekosistem paket Python di masa lalu bahkan pernah merilis override untuk paket lama yang dirilis dengan asumsi batas atas yang keliru
Hari ini, Anda belum bisa tahu apakah paket Anda akan kompatibel atau tidak dengan paket yang bahkan belum dirilis
uvsaat update bahwa paket-paket tidak kompatibel, lalu bisa melakukan override bila perluItu lebih baik daripada menemui error inkompatibilitas versi saat runtime yang sulit dilacak
pyproject.tomlbukan masalah yang sebenarnyaMasalah sebenarnya adalah bahwa
uv lock —-upgrademeng-upgrade semuanya sekaligus yang tidak punya batas atasJika ada cara untuk meng-upgrade paket tanpa menaikkan major version, perintah ini akan jauh lebih aman
Dibanding sebelum uv, ini jauh lebih baik, tetapi tampaknya sulit menjadi benar-benar baik tanpa perubahan yang agak memecahkan seluruh ekosistem. Mengingat transisi Python 2 ke 3, sepertinya untuk sementara ini juga belum banyak keinginan ke arah perubahan seperti itu
Flag
—-boundmembantu, tetapi itu satu hal lagi yang harus diketik dan diingatKalau uv bisa tahu bahwa proyek itu bukan library, mungkin saja secara default ia menambahkan batas atas
=dan jangan percaya update non-major tidak akan merusak, lalu hanya update secara manualSaya punya 257 dependensi Python di aplikasi produksi, dan lebih dari setengahnya adalah dependensi langsung
Di
pyproject.tomlsaya tidak memberi batas atas, dan setiap dua minggu saya menjalankanuv lock --upgradelewat GitHub ActionsCakupan pengujian kami bagus, jadi kalau ada yang rusak tes akan gagal, dan kami juga punya alur review berbantuan AI. Saat PR upgrade dibuat, workflow AI mencantumkan update major dan minor version dengan skrip Python, mencari changelog, menautkan dan merangkumnya, lalu menganalisis tingkat risiko tiap paket berdasarkan bagaimana paket itu dipakai di codebase
Umumnya nyaris tidak menyakitkan, dan tidak perlu menaikkan paket satu per satu, memeriksa paket usang, atau menangani dependensi yang terbengkalai. Sangat jarang ada kasus yang butuh perbaikan dari penulis dependensi sehingga tidak bisa diselesaikan di kode kami, kira-kira setahun sekali. Dalam 3 bulan terakhir ada 18 kenaikan major version, dan hanya satu yang membutuhkan perubahan kode
Saya ingin melakukan ini juga di frontend, tetapi tesnya belum cukup untuk dijalankan dengan aman. Tes backend lebih mudah ditulis dan lebih penting, jadi menurut saya semua codebase harus memilikinya. Kalau ada tes, Anda bisa langsung meng-upgrade semuanya secara otomatis
Setidaknya mereka sangat bagus dalam mengubah instruksi bahasa alami menjadi tes yang akurat
Sudah lama saya tidak menulis tes dengan tangan sendiri, dulu itu hal yang selalu saya keluhkan, sekarang tidak lagi
UV sudah banyak membantu Python, tetapi hari ini saya cukup bergulat dengannya
Saya mencoba mengelola secara terpusat skrip-skrip yang tersebar di banyak repositori dan implementasinya makin berbeda seiring waktu
Pendekatan yang saya bayangkan adalah
uv run --with $package main --help, dan saya ingin secara otomatis 1) memasang lalu menjalankan jika belum ada, 2) tidak memasang jika sudah terbaru, 3) kalau belum terbaru maka diperbaruiNamun ketiganya ternyata lebih rumit dari perkiraan. Pada dasarnya
uv runmemasang ulang setiap kali, dan butuh 6 detik untuk virtual environment dan instalasiuvxatauuv tooljuga tidak jauh lebih baik, tetapi malah memunculkan masalah baru bahwa pengguna tidak mendapat upgradeAkhirnya saya membuat skrip mengirim GET pagination ke CodeArtifact, lalu jika ada versi non-development yang lebih baru maka diperbarui dan dijalankan ulang. Itu memang berfungsi, dan latensi 200ms lebih baik daripada 6 detik, tetapi bukan pengalaman yang saya inginkan
uv run --with $package main --helpseharusnya melakukan perilaku yang Anda sebutkan dengan overhead yang sangat kecilItu tidak memasang ulang setiap kali, dan environment
--withdisimpan di cache. Bahkan jika environment belum di-cache, dependensinya tetap di-cache, dan memasang dari cache sangat cepat. Seharusnya jelas di bawah 200msKalau Anda bisa membuka contoh reproduksi yang rinci, saya bisa melihatnya
uv tool installdanuv tool upgradetampaknya yang tepatTetapi ketidaknyamanan kecil seperti ini sepertinya relatif mudah diperbaiki, jadi akan bagus kalau Anda membuka issue
uv run mainMaka dependensi yang diperlukan akan otomatis dipasang, di-cache, dan dijalankan: https://docs.astral.sh/uv/guides/scripts/
uv tool upgrade?Saya tidak tahu apakah use case-nya persis sama, tetapi ini sangat bagus untuk menyinkronkan ekosistem microservice polyrepo
Saya cukup terkejut melihat rekomendasi
"uv tree --outdated --depth 1"untuk melihat daftar dependensi lamaSecara pribadi, sejak fitur itu diperkenalkan saya memakai
"uv pip list --outdated"Namun saya setuju bahwa perintah sepenting ini pantas punya subperintah tingkat atas tersendiri
Memang benar
"uv pip list --outdated"memberi output yang jauh lebih baik, terima kasihTetapi fakta bahwa ada sampai 2 cara untuk melihat paket usang dan output-nya sangat berbeda juga membuat saya merasa UX-nya berantakan
"uv tree -od1"mungkin juga akan bekerjaNamun kritik terhadap manajer paket seperti pacman juga, seperti apt, adalah bahwa perintah yang sering dipakai seharusnya punya perintah yang mudah dipahami manusia
Dibanding kata “berantakan” di judulnya, contoh yang diberikan sebenarnya hanya soal perlu menulis beberapa argumen tambahan
Judul yang lebih baik tampaknya peningkatan kualitas hidup yang saya harapkan ada di UV
Feedback-nya sendiri berguna dan sebagian besar saya setujui, tetapi frasa seperti itu menurunkan nilai feedback dan memancing respons defensif
Antarmuka command line uv menurut saya pribadi juga merepotkan, tetapi saya mengerti kenapa ditulis seperti ini
uvmemang hebat, tetapi masalah terbesar Python packaging saat ini tetaplah menangani packaging saintifik/machine learning dengan baikJika ingin memasang PyTorch, Anda harus memikirkan versinya, apakah CUDA, dan kalau CUDA maka ada 6 varian menurut versi CUDA, sementara wheel-nya terlalu besar untuk diunggah ke PyPI sehingga harus diambil langsung
Conda hanya menyelesaikan masalah ini sebagian. Spack sangat bisa dikonfigurasi dan dapat menyiapkan dependensi C/C++/Fortran yang dibutuhkan beserta toolchain compiler untuk mengeluarkan performa tertinggi, tetapi tidak terintegrasi baik dengan uv dan semacamnya. Karena itu, sulit membawa proyek machine learning eksperimental buatan peneliti sampai ke produksi
Pada akhirnya kita kembali lagi ke situasi yang disebut di awal
Ada banyak feedback yang berguna, tetapi bercampur dengan ungkapan ala clickbait
Soal
pnpm outdated, selama ini memang tidak terlalu sering muncul, tetapi tampaknya itu permintaan yang masuk akal. Mungkin ini berasal dari perbedaan budaya antara Python dan JavaScript. Saya hampir tidak pernah peduli apakah dependensi itu usang kecuali rentan atau rusak, tetapi di ekosistem JavaScript tampaknya cukup umum untuk meng-upgrade saat ada kesempatan. Ini bukan berarti buruk, melainkan menunjukkan betapa berbeda intuisi tentang apa yang perlu ditampilkan di antarmuka command line antara komunitas pemrograman besarSeperti kata Armin, perilaku batas atas uv itu disengaja dan secara fungsional memang perlu karena cara resolusi Python bekerja. Ini adalah trade-off yang diambil Python dibanding bahasa lain, tetapi saya rasa ini trade-off yang baik karena hanya ada satu dari tiap dependensi di pohon dependensi dan semua kebutuhan yang saling terkait di-resolve agar cocok dengan itu
Alasan
uv lock --upgradeseperti itu adalah karena ia meng-upgrade lockfile, bukan requirement milik pengguna sendiri. Sebaliknya,pnpm updatetampaknya meng-upgrade requirement pengguna dipackage.json. Ini memang bisa membingungkan, tetapi meletakkannya di bawahuv locklebih akurat. Kalau tidak, akan ada pengguna yang bingung karenauv upgradetidak melakukan upgrade seperti yang mereka bayangkan. Meski begitu, masih ada ruang untuk penyajian yang lebih rapi, dan memang jelas ada permintaan pengguna untuk subperintah uv yang juga langsung meng-upgrade requirement itu sendirihttps://news.ycombinator.com/item?id=48230048
Masuk akal bahwa perintah
uv lockhanya menangani lockfile, tetapi pengguna punya kebutuhan nyata untuk meng-upgrade dependensi langsung dan transitif. Dependensi transitif bisa dilakukan denganuv lock --upgrade-package, tetapi agak panjang. Itu juga bekerja pada dependensi langsung, tetapi tidak menyentuhpyproject.toml, padahal file ini jauh lebih terlihat bagi developerJika versi paket di
uv.lockmelaju lebih jauh daripadapyproject.toml, makapyproject.tomlmenjadi kurang bisa diandalkan sebagai panduan untuk memahami permukaan dependensi. Akan bagus jika ada perintahuv upgradeyang ramahJebakan UX terbesar uv yang pernah saya lihat sejauh ini adalah
uv pip. Banyak proyek yang memakai uv dengan benar untuk pengembangan lewatpyproject.tomldanuv.lock, tetapi di Dockerfile deployment atau tool CI mereka memakaiuv pip install -r pyproject.tomlsehingga melewatiuv.lockMemang masalahnya agen coding punya terlalu banyak
pipdi data pelatihan sehingga merekomendasikan polauv pipyang buruk, tetapi uv juga harus menyediakan pengaman untuk melindungi penggunaMenurut saya uv adalah alat yang hebat dan seharusnya dipakai lebih luas: https://aleyan.com/blog/2026-why-arent-we-uv-yet
poetry updatejuga meng-update lockfile. Saya merasa cara penyusunan CLI uv cukup menyebalkan untuk dipakai. Rasanya dirancang demi akurasi dan mesin, bukan demi keramahan bagi penggunauv pip list --outdateduv upgradeada di roadmapAlasan belum dikerjakan adalah karena sulit dibuat menjadi pengalaman yang hebat, nuansanya jauh lebih banyak daripada yang orang kira, timnya kecil, dan prioritasnya banyak
pnpm outdatedadalah untuk melihat apa yang akan di-update jika menjalankan"uv sync --update"atau"uv lock --update"Meski begitu, mungkin akan lebih baik jika perintah-perintah itu punya prompt konfirmasi
Pixi memakai uv sebagai backend, dan saya menyukai UI-nya karena mudah menambahkan alias task untuk menampilkan paket usang dengan rapi
Khususnya Pixi-diff-to-markdown membuat update paket otomatis di CI mudah ditinjau sekilas
Jika ingin melihat paket usang mana yang akan di-update, Anda bisa membuat alias task seperti ini di proyek
pixi task add outdated "pixi update --dry-run --json | pixi exec pixi-diff-to-markdown"Lalu di proyek cukup jalankan
pixi run outdatedOutput-nya berupa tabel Markdown yang mudah dibaca berisi paket yang akan di-update, versi lama, dan versi baru yang akan dipasang oleh perintah
pixi update. Tentu saja ini bisa berbeda tergantung selera dan situasiBaru-baru ini skrip
envmuncul di path saya dan mengganggu penggunaan perintah UNIXenvyang normalTernyata penginstal uv membuat ini saat menjalankan perintah di bawah
curl -LsSf [https://astral.sh/uv/install.sh](<https://astral.sh/uv/install.sh>) | shIa memasang ke
$HOME/.cargo/bin/, lalu membuat skrip shell di$HOME/.cargo/envyang menambahkan$HOME/.cargo/bin/ke depanPATHjika belum adaSulit memahami programmer macam apa yang menulis kode yang menimpa perintah UNIX dasar dengan cara seperti ini