12 poin oleh GN⁺ 2025-07-19 | 2 komentar | Bagikan ke WhatsApp
  • Dependensi terlihat seperti fitur gratis, tetapi pada kenyataannya membawa berbagai biaya dan kompleksitas
  • Dependensi yang salah menimbulkan berbagai risiko seperti kurva belajar, perubahan antarmuka mendadak, serta masalah deployment dan instalasi
  • Sebagai contoh utama, TigerBeetle mengarah pada kebijakan "tanpa dependensi" demi keamanan, performa, dan kesederhanaan operasional
  • Penulis mengusulkan kerangka evaluasi dependensi (ubiquity, stability, depth, ergonomics, watertightness)
  • Berpikir kritis untuk membedakan dependensi yang baik dan buruk, serta mempertimbangkan bukan hanya produktivitas jangka pendek tetapi juga biaya total dan risiko, adalah hal yang wajib saat memilih dependensi

Dependensi yang salah lebih mahal daripada NIH (Not Invented Here)

Kekurangan dan biaya tersembunyi dari dependensi

  • Banyak pengembang menganggap penambahan dependensi sebagai "fitur gratis", tetapi kenyataannya biaya berikut tetap muncul
    • Waktu dan kompleksitas untuk mempelajarinya
    • Cukup sering instalasi dependensinya sendiri sulit dilakukan
    • Saat upgrade versi mayor, ada beban untuk memodifikasi kode kita secara besar-besaran
    • Pada akhirnya dependensi harus ikut masuk ke lingkungan deployment/instalasi (menambah kompleksitas seperti container dan bundling)
  • Penerapan dependensi yang salah juga dapat menciptakan struktur deployment yang rumit dan tidak terkait dengan fungsi inti

Prinsip tanpa dependensi dari TigerBeetle

  • TigerBeetle adalah database finansial berbasis Vanilla Zig yang mengadopsi kebijakan tanpa dependensi
    • Dikembangkan hanya dengan bahasa Zig, tanpa dependensi eksternal di luar toolchain Zig
    • Tujuannya untuk menghindari masalah seperti risiko serangan supply chain, penurunan performa, dan bertambahnya waktu instalasi akibat dependensi
    • Semakin dalam perangkat lunak tertanam di infrastruktur, semakin biaya akibat dependensi diperbesar ke seluruh stack
    • Pemanfaatan toolbox kecil yang terstandarisasi lebih menguntungkan untuk pemeliharaan dan efisiensi pengembangan
  • Fokusnya adalah menggunakan Zig saja untuk menangani masalah baru dengan cepat dan mengurangi kompleksitas

Kerangka evaluasi dependensi

  • Sambil mengakui bahwa ketidakbergantungan total tidak mungkin bagi semua pengembang, penulis menekankan bahwa dependensi harus dievaluasi dengan hati-hati
  • Penulis mengusulkan evaluasi dependensi dengan 5 kriteria berikut
    • Ubiquity: Seberapa umum tersedia di lingkungan target? Apakah perlu deployment/instalasi terpisah
    • Stability: Seberapa sering muncul isu seperti kompatibilitas mundur, perubahan API, atau penghentian dukungan
    • Depth: Seberapa banyak fungsi nyata yang tersembunyi di bawah API, dan seberapa sulit menggantinya jika diimplementasikan sendiri
    • Ergonomics: Apakah API intuitif/deklaratif dan mudah digunakan, serta bagaimana kondisi dokumentasinya
    • Watertightness: Seberapa baik abstraksinya bekerja, dan sejauh mana kita tidak perlu memikirkan teknologi di bawahnya
  • Komunitas pengembang umumnya hanya membahas usability, sementara empat hal lainnya sering diabaikan

Contoh dependensi yang baik

  • System call POSIX

    • Universalitas: Dapat digunakan di hampir semua platform seperti Linux, Android, macOS, dan BSD
    • Stabilitas: Kompatibilitas antarmuka sangat tinggi dan hampir tidak berubah
    • Kedalaman: Satu API menyembunyikan ratusan ribu baris kode kernel
    • Usability: Meski agak bergaya C tradisional, secara umum tidak terlalu menyulitkan
    • Kelengkapan: Umumnya tidak bermasalah, meskipun ada isu detail seperti persistensi data pada media penyimpanan
  • Kode kontrol terminal ECMA-48

    • Universalitas: Didukung oleh sebagian besar terminal kecuali cmd.exe di Windows
    • Stabilitas: Tidak berubah sejak 1991
    • Kedalaman: Membuat standar semacam ini sendiri akan sangat sulit
    • Usability: Selain kurang terbaca karena karakter Esc, secara umum cukup baik
    • Kelengkapan: Kekhawatiran terkait ketergantungan pada hardware sangat kecil
  • Platform web (Web API, HTML, JS, CSS, dll.)

    • Universalitas: Browser web terpasang di hampir semua lingkungan di seluruh dunia
    • Stabilitas: Memiliki kebijakan kompatibilitas mundur yang kuat
    • Kedalaman: Membuat browser sendiri pada praktiknya sedalam dan sesulit itu hingga nyaris mustahil
    • Usability: Ada sedikit kompleksitas, tetapi dokumentasi dan alat pengembangnya sangat baik
    • Kelengkapan: Sangat lengkap kecuali untuk situasi khusus seperti file, audio, dan video

Kesimpulan

  • Alih-alih copy-and-paste dan penyalahgunaan dependensi, yang dibutuhkan adalah berpikir kritis dan analisis biaya secara menyeluruh
  • Saat memperkenalkan dependensi, kita harus mengevaluasi biaya dan manfaat dari semua dependensi secara kritis, dan juga harus mempertimbangkan risiko serta biaya potensial dari seluruh sistem, sehingga dapat memilih dengan hati-hati; dalam jangka panjang pendekatan ini jauh lebih murah dan aman

2 komentar

 
GN⁺ 2025-07-19
Opini Hacker News
  • Saya baru pertama kali mendengar contoh TigerBeetle, jadi saya mencarinya sendiri. Jika Anda sedang membuat database ledger keuangan berbasis Zig yang menuntut keamanan dan performa tinggi, dan harus menangani sejuta transaksi per detik pada satu inti CPU, maka menghindari penambahan dependensi karena risikonya memang sepenuhnya masuk akal. Namun, sebagian besar developer biasa membuat sistem CRUD yang umum, dan biasanya mereka tidak sekuat itu. Banyak dependensi memang bisa penuh bug, tetapi sering kali kualitasnya tetap lebih baik daripada jika mayoritas developer membuatnya sendiri. Perusahaan juga pada praktiknya terhambat oleh kualitas developer yang bisa direkrut. Situasi tiap orang berbeda, jadi perlu diingat bahwa nasihat yang saling bertentangan pun bisa sama-sama benar dalam konteks masing-masing

    • Saya benar-benar bekerja di TigerBeetle. Intinya adalah konteks. TigerBeetle maupun rust-analyzer sama-sama punya budaya engineering yang kuat, tetapi masalah yang mereka selesaikan berbeda sehingga membentuk budaya yang berbeda pula. Dependensi yang disebut dalam tulisan itu lebih mirip antarmuka sistem seperti POSIX, ECMA-48, dan platform web, bukan library biasa. Dependensi library kalau bermasalah masih bisa ditulis ulang sendiri, tetapi hal yang mendasar seperti antarmuka sistem pada dasarnya hampir mustahil atau sangat mahal untuk diganti. Sangat kuat jika kita memutuskan untuk tidak mengerjakan hal-hal yang tidak sesuai dengan cakupan software kita. Misalnya, bila ada tim khusus yang ahli membuat kode perkalian matriks, maka untuk library lain yang tidak berkaitan dengan pekerjaan inti boleh saja memakai milik eksternal, tetapi saya rasa pemecahan tanggung jawab produknya harus didesain lebih baik. Dengan begitu, dependensi yang benar-benar wajib bisa diisolasi lebih baik di dalam sistem

    • Masalah dari sudut pandang ini adalah ia hanya berlaku jika yang dibayangkan cuma developer level bawah. Bidang teknologi punya kecenderungan mengencerkan nasihat agar cocok untuk level paling rendah, tetapi jujur saja, hampir tidak pernah saya bekerja di tempat yang developernya begitu lemah sampai tidak bisa menyalin dependensi yang rusak untuk memperbaiki masalah. Ada juga banyak nada meremehkan terhadap CRUD, padahal abstraksi yang buruk bisa menyebabkan pemborosan waktu dan penderitaan besar di CRUD. Bahkan hal-hal populer pun sering bermasalah dan tidak produktif kalau sudah lewat dari tutorial yang benar-benar dasar

    • Ini bukan soal level developer itu sendiri. Apa pun toolchain atau produk yang dipakai, pada akhirnya Anda tetap menggunakan dependensi buatan orang lain. Sangat sedikit orang di sekitar saya yang benar-benar mengimplementasikan kode perkalian matriks sendiri, dan bahkan mereka pun tidak sampai membuat sendiri library open source yang tak ada hubungannya dengan mereka. Biasanya orang jadi terobsesi pada dependensi itu sendiri karena tuntutan regulasi, minat pribadi, atau keterikatan pada library tertentu. Kalau semua orang menerapkan prinsip ini sepenuhnya, kita semua akan hidup di pantai sambil memungut pasir

    • Pendapat bahwa "developer CRUD rata-rata tidak kuat" terlalu menggeneralisasi. Kebanyakan developer tidak bisa memilih sistem yang akan mereka kerjakan, dan selama masa pengembangan pun resource selalu kurang. Mereka perlu memanfaatkan dependensi yang 'murah' agar bisa merilis software yang bekerja dengan cepat. Rasanya pendapat itu muncul tanpa benar-benar memahami realitas ini

    • TigerBeetle adalah startup yang relatif baru dirilis, dan versi resmi 1.0 pun belum ada. Menurut saya terlalu dini untuk mengatakan bahwa pendekatan seperti ini benar-benar efektif

  • NIH(Not Invented Here) sendiri sangat berguna jika dipakai dengan penilaian realistis tentang sampai batas mana kita mau bertanggung jawab. Misalnya, framework web frontend yang benar-benar pas dengan domain saya kadang memang layak dibuat dan dipelihara sendiri. Namun untuk database, game engine, web server, fungsi dasar kriptografi, dan sejenisnya, ceritanya berbeda. Jika tingkat kesulitannya sudah setinggi itu sampai solusi yang ada tidak bisa menyelesaikannya, saya rasa yang perlu dipikirkan lebih dulu adalah mendefinisikan ulang masalahnya. Daripada membuat ulang seluruh test SQLite dari nol, menurut saya jauh lebih murah menata ulang masalahnya sendiri

    • Database, game engine, web server, crypto primitive, dan sejenisnya juga dalam beberapa kasus memang lebih baik dibuat sendiri. Jika file sederhana dan indeks runtime saja sudah cukup, sering kali bahkan SQLite pun terlalu berlebihan. Banyak game, terutama yang dikerjakan tim kecil, justru diuntungkan oleh engine kustom. Keunggulan engine jadi biasanya hanya pada pipeline, tetapi dengan overhead besar. Web server juga tidak punya perbedaan kompleksitas dibanding aplikasi FastCGI. Dalam kriptografi pun tidak semua situasi adalah masalah keamanan, jadi hal sederhana seperti verifikasi hash bisa saja diimplementasikan sendiri. Saya rasa tidak baik memiliki learned helplessness terhadap topik-topik yang terlihat sulit. Penting juga untuk diingat bahwa hanya karena solusi yang ada bisa menyelesaikan masalah, bukan berarti itu cara yang optimal atau paling efisien

    • Kalau begitu, mengapa ada begitu banyak engine database? Pada akhirnya sistem komputer yang kompleks selalu punya trade-off masing-masing. Constraint, skalabilitas, konkurensi, keamanan, karakteristik data, cara penyimpanan, dan lainnya memberi pilihan yang sangat beragam. Saya cenderung lebih percaya pada dependensi mahal jika dependensi itu sendiri berupaya meminimalkan dependensi lain. Sistem manajemen dependensi yang otomatis justru sering membuat masalah makin rumit, jadi pengelolaan manual yang hati-hati menurut saya bisa mengurangi beban

    • Saya melihat ada dua alasan memakai dependensi pihak ketiga. (1) Dipublikasikan langsung oleh penyedia layanan, dan masa hidupnya kurang lebih selaras. (2) Menggantikan kode kompleks yang saya tidak ingin tulis sendiri. Untuk (1), karena ada alasan bisnis, tidak masalah. Hanya saja kita harus menerima bahwa pembaruan dari layanan itu bisa membawa perubahan besar. Untuk (2), nilainya bergantung pada seberapa rumit kode yang ingin saya hindari. Memperkenalkan dependensi berarti saya harus menghabiskan waktu dan resource untuk update/testing mengikuti jadwal pihak lain, dan saya ikut menanggung tanggung jawab itu

    • Anda akan cukup cepat menemui masalah yang tidak bisa diselesaikan dengan RDBMS. RDBMS dirancang dengan fokus pada modifikasi data serentak dan dukungan dataset yang berubah-ubah, jadi bila itu tidak diperlukan, indeks sederhana saja bisa memberi peningkatan performa yang sangat besar. Jika datanya immutable, implementasi sendiri bisa jauh lebih cepat dibanding RDBMS

    • Contoh RDBMS ini menarik. Di Wikipedia ada lebih dari 100 jenis RDBMS, dan masing-masing punya masalah yang bisa dan tidak bisa mereka selesaikan. Itu adalah hasil dari memikirkan solusi praktis lalu benar-benar mengeksekusinya

  • Dependensi memang menimbulkan risiko, tetapi jika sama sekali tidak digunakan, kita bisa tertinggal dalam daya saing pengembangan maupun time-to-market. Karena itu proses manajemen dependensi sangat penting

    1. Hanya mempertimbangkan dependensi open source
    2. Saat menambah yang baru, selain code review juga meninjaunya dari banyak sisi seperti lisensi, usaha yang dibutuhkan untuk melepasnya, riwayat kerentanan keamanan dan bug, keberlanjutan update, serta vitalitas komunitas
    3. Jika memungkinkan, perlu pemeriksaan berkala terhadap isu keamanan dependensi. Pada skala besar, ini menjadi masalah biaya yang besar. (Berbagi ide terkait: https://blog.majid.info/supply-chain-vetting/)
    4. Hanya mengadopsi dependensi yang bebannya untuk dipelihara/fork masih sanggup ditanggung. Minimal, kita pernah membangunnya langsung dari source
    5. Fork semua dependensi secara proaktif. Repositori bisa tiba-tiba hilang seperti kasus left-pad
    • Poin 4 dan 5 benar-benar penting tetapi sering terlupakan. Bahkan di proyek pribadi, saat kembali setelah lama ditinggalkan, saya pernah mendapati dependensi sudah usang atau repositorinya terhapus. Karena itu sekarang saya cenderung mem-fork source-nya secara privat dan membangunnya sendiri, serta mem-fork hingga level source untuk dependensi dari semua dependensi saya. Dengan begitu, kalau ekosistem berubah drastis nanti, dampaknya bisa diminimalkan. Saya jadi lebih menyukai source library daripada binary

    • Untuk poin 5, fork bisa menjadi beban yang terlalu besar. Melakukan vendoring dengan menaruh dependensi di git sendiri atau cache proxy juga cara yang baik. Ini terutama cocok untuk proyek yang berumur panjang. Jika file dependensinya sangat banyak seperti di NodeJS, tool seperti Yarn atau PNPM lebih efisien

    • Terkait poin 4, dependensi terkenal seperti SQLite akan bertahan jauh lebih lama daripada produk yang saya buat. Jika saya mengira produk saya akan hidup lebih lama daripada open source itu sendiri, justru itu sikap yang lebih arogan. Saya juga tidak berniat membangun Linux kernel sendiri

    • Semua kode setidaknya harus bisa dibangun tanpa koneksi jaringan. Idealnya tanpa binary artifact, tetapi itu tidak selalu realistis

    • Wawasan yang luar biasa. Saya akan menambahkan ini ke dokumen prosedur adopsi dependensi saya. Masalahnya adalah kasus seperti JavaScript yang dependency tree-nya terlalu dalam

  • Dari pengalaman panjang, pada akhirnya semuanya kembali ke "It Depends™". Saat muda saya terpaku pada prinsip tanpa pengecualian, lalu malah menghasilkan kode terburuk karena memaksakan library atau paradigma yang tidak cocok. Sekarang, hal-hal yang sering saya gunakan saya jadikan library sendiri dan dipisahkan sebagai package, lalu untuk yang dibutuhkan tetapi tidak bisa saya lakukan sendiri saya isi dengan dependensi eksternal. Jika kualitas atau keterkelolaannya bisa saya terima, saya dengan senang hati juga menerima yang eksternal

  • Industri energi cenderung sengaja menghindari dependensi. Jika menambah dependensi eksternal, semua perubahan harus diperiksa. Tool kode AI sangat membantu, dan terutama saya pakai untuk membuat tool CLI. Dokumen OpenAPI juga saya buat dengan memanfaatkan LLM, lalu saya layani dengan Go standard library. LLM itu sendiri memang dependensi eksternal, tetapi tool CLI yang dibuat darinya tidak terkait langsung dengan kode nyata sehingga tuntutan kualitasnya lebih rendah. Tentu saja developer frontend tidak ingin bekerja tanpa React, tetapi karena produk seperti itu ditangani di luar, itu jadi pengecualian. Jika kita menyediakan tool kecil yang mengurangi obsesi engineer pada dependensi berkualitas, kebijakan meminimalkan dependensi jadi lebih mudah dijalankan

    • LLM kadang mengeluarkan sebagian kode open source yang dipakai saat training dalam bentuk kode juga; dalam kasus seperti itu saya penasaran apakah sebenarnya tidak jauh berbeda dari mem-fork dependensi lalu memperlakukannya seolah milik sendiri
  • Mampu membedakan dependensi yang baik dan buruk adalah kemampuan penting. Menurut saya, dependensi berbayar biasanya kurang menguntungkan. Sangat mungkin perusahaan penyedianya mendesainnya untuk mendorong lock-in. "Dependency minimalism" adalah konsep yang bagus (tweet terkait dari VitalikButerin)

    • Dependensi berbayar hanya punya satu sumber dukungan, jadi jika perusahaan penyedianya tutup, seluruh proyek bisa ikut terancam. Karena sebagian besar perusahaan tidak bertahan selamanya, kita harus benar-benar mempertimbangkan apakah masa depan dependensi itu akan memengaruhi lintasan proyek kita

    • Saya punya pengalaman buruk dengan dependensi berbayar yang dipaksakan oleh tim nonteknis. Sebaliknya, dependensi 'open core' seperti Sidekiq yang dipakai luas oleh komunitas jauh lebih bisa dipercaya karena kecil kemungkinannya menghilang tiba-tiba. Keunggulan yang berbayar adalah selama perusahaannya dikelola dengan sehat, kita tidak perlu terlalu khawatir soal dukungan

    • Baik komponen berbayar maupun gratis dari perusahaan sama-sama mengandung vendor lock-in. Manajemen risiko adalah tanggung jawab tim integrasi, dan risikonya harus dikendalikan lewat pencarian alternatif atau modularisasi

    • Jika berbayar, maka harus diserahkan melalui antarmuka yang membutuhkan standar terbuka atau implementasi alternatif agar lock-in bisa dicegah. Kalau ada pilihan lain, opsi untuk pindah tetap terjaga

    • Anggapan bahwa dependensi berbayar itu buruk bisa jadi menandakan kurangnya anggaran. Saya ingin ada orang yang mendukung kode saya sebagai bagian dari 'pekerjaan' mereka. Kalau jumlah pendukungnya banyak atau ada banyak developer yang sukarela memikul tanggung jawab, hasilnya lebih stabil; bahkan untuk proyek pribadi, meski source-nya terbuka, tetap perlu ada orang yang akan membantu saat masalah muncul. Rasa tanggung jawab untuk tidak menelantarkan meski perusahaan berhenti itu penting

  • Banyak orang cenderung terobsesi menulis kode baru, padahal kenyataannya bahkan memakai dependensi yang jelek pun 90% dari waktu jauh lebih efisien

    • Dependensi adalah pedang bermata dua. Perusahaan software besar sering memilih menulis ulang karena lebih murah daripada terus mempertahankan kode lama. Di agensi web/branding kecil, backend berkualitas tinggi nyaris tidak dibutuhkan. Sebaliknya, alasan munculnya enterprise pattern yang ditakuti adalah agar bahkan lima tahun kemudian, saat dokumentasi dan ingatan industri sudah hilang, kode tetap bisa diisolasi dan dipelihara tanpa dependensi eksternal. Dependensi eksternal selalu membawa dua risiko: dukungan dihentikan, atau terjadi perubahan yang breaking. Pada akhirnya ini memengaruhi alur pengembangan fitur. Kalau komponennya internal, trade-off seperti itu masih bisa dikendalikan dari dalam. Jika Anda membuat SaaS, memakai dependensi dengan cepat demi sukses jangka pendek memang masuk akal; tetapi bila keselamatan dan dukungan jangka panjang wajib, Anda harus berpikir lebih jauh. Hampir tidak pernah penulisan kode baru menjadi bottleneck organisasi

    • Saya penasaran seberapa serius perusahaan mengelola kerentanan keamanan dan lisensi. Dulu saya cukup permisif terhadap dependensi, tetapi setelah pindah ke perusahaan yang ketat soal keamanan dan lisensi, sudut pandang saya berubah drastis

  • Perbedaan antara library dan framework juga penting. Library adalah alat yang melakukan satu hal dengan baik, sedangkan framework menentukan struktur keseluruhan aplikasi. Komunitas Go cenderung menjauhi framework besar dan lebih menyukai standard library, library ringan, dan bila perlu menyalin source secara langsung. Misalnya framework seperti Gin(web API) dan GORM(ORM) memang nyaman dipakai, tetapi membatasi struktur internal dan menambah kompleksitas. Karena standard SDK Go sendiri sudah cukup kuat, saya rasa benar untuk tidak menambah dependensi lebih dari yang perlu

  • Penulisnya berasal dari Selandia Baru. Di baliknya ada semangat Number 8 wire dari Selandia Baru, yaitu 'sikap menyelesaikan apa pun dengan apa yang ada dan keterampilan tangan' (artikel wiki Number 8 wire)

    • Developer berpengalaman dari negara selain Selandia Baru pun sering sependapat dengan hal serupa. Hampir semua orang pernah menderita karena salah memilih dependensi atau karena upgrade library yang tidak perlu

    • Sebagai orang Selandia Baru, rasanya mentalitas Number 8 Wire itu sudah hilang sejak 20 tahun lalu

    • Ini pertama kalinya saya tahu soal itu hari ini. Saya jadi teringat ungkapan Australia, "She'll buff out, mate"

  • Skala besar dan dasar komersial sebuah dependensi juga memengaruhi skalabilitas. Jika suatu tool digunakan di tempat yang deployment-nya 100 hingga 1000 kali lebih besar daripada saya, kemungkinan tool itu mentok di masalah saya menjadi lebih kecil. Pada skala itu bug juga cenderung lebih cepat ditemukan atau diperbaiki, sehingga hasil akhirnya lebih aman untuk saya

    • Bahkan library besar pun kadang sama sekali tidak berjalan di lingkungan kecil. Misalnya compiler Swift protocol buffer dulu pernah crash pada field yang tidak terduga. Banyak perusahaan besar juga tidak benar-benar menguji jalur itu di luar tujuan skala besar mereka

    • Saya pernah menemukan bug serius di library terkenal buatan perusahaan besar(Meta, Google, Microsoft, dll.). Bahkan setelah issue dilaporkan, perbaikannya butuh waktu lama dan ada resistensi besar terhadap perubahan. Dalam situasi seperti itu, ketika akhirnya saya mengimplementasikannya sendiri, justru hasilnya lebih cepat dan performanya juga meningkat. Terutama di industri konsultasi, arah pekerjaan sering goyah karena permintaan klien yang tidak masuk akal. Begitu keyakinan saya sebagai developer bahwa saya bisa mengerjakannya sendiri makin kuat, sering kali implementasi saya sendiri lebih baik daripada bergantung pada dependensi eksternal raksasa. Tentu saya tidak mengerjakan hal yang benar-benar masif seperti browser atau model AI, tetapi misalnya inference engine lokal, HTML renderer, atau graph database buatan sendiri, itu saya buat sendiri. Yang diinginkan klien bukanlah hal baru(inovasi), melainkan pengurangan risiko. Jika saya mengembangkan sendiri, jauh lebih mudah memenuhi jadwal. Daripada menghabiskan waktu membaca dokumentasi Google atau perusahaan besar lainnya, sering kali lebih efisien membuatnya sendiri. Selama tiga bulan terakhir saya bekerja lembur 12 jam sehari untuk menyelamatkan proyek yang ditelantarkan tim sebelumnya, jadi akhir-akhir ini saya banyak memikirkan hal semacam ini hingga larut malam