Kenapa Immutability Itu Penting (Javascript)
Beberapa hari yang lalu PO kami menemukan bug yang cukup unik di salah satu projek legacy kami dimana setelah user meng-upload foto profile-nya, foto tersebut akan tampil dan langsung menghilang sepersekian detik kemudian. Kayak baru kenal tapi langsung diputusin gitu π Saya pun melakukan Pair Programming dengan temen satu team selama kurang lebih setengah jam. Dataflow-nya oke, redux action gak ada yang masalah, payload dari/ke server pun fine-fine saja. Hmm. Sampai momen dimana kami menemukan satu baris kode yang kelihatannya oke tapi gak oke.
Langsung aja kami misuh-misuh di tempat, βThis is such a ridiculous bug! π©π©π©π‘π€¬π€¬π€¬β
Const dan Let
Sebelum memahami kenapa kami bisa menyimpulkan code di atas adalah biang masalahnya, saya mau mengulas dulu apa sih Immutability itu. Immutability dalam programming adalah suatu value yang tidak bisa diubah ketika sudah dideklarasikan. Perhatikan potongan code berikut
Javascript akan complain bahwa variable name
tidak dapat diubah: Uncaught TypeError: Assignment to constant variable
. Mirip-mirip begitu lah. Selama saya bekerja dengan Javascript tiga tahun belakangan, saya hampir-hampir tidak pernah menggunakan let
dan lebih memilih const
. Sekedar menghindari mutability.
Kira-kira apa jawaban log yang pertama dan apa jawaban log yang kedua?
Log yang pertama akan bernilai true
karena keduanya bernilai 10 console.log(10 === 10)
. Operasi ini bersifat immutable karena tidak ada variable yang diubah ketika runtime.
Tapi log yang kedua akan bernilai false
karena operasi yang satu ini bersifat mutable: nilai i
berubah-ubah. Ketika Javascript menjalankan i++
pertama, nilai i
berubah menjadi 10.
Setelahnya, nilai i
akan berubah lagi menjadi 11 disebabkan oleh statement i++
yang kedua.
Sampe sejauh ini kita paham bahwa const
bisa digunakan ketika kita ingin variable tersebut tidak bisa diganti, dan let
bisa digunakan ketika ada variable yang ingin diganti over time.
Object di Javascript
Flat Object
Gak selamanya variable yang dideklarasikan menggunakan const
itu nggak bisa berubah. Iya, Javascript ini emang rada-rada gaes. Contohnya gimana, Mas Jihad?
Bisa jadi fatal sekali kalau kita nggak aware sama behaviour ini. Gak jarang saya temui beberapa junior developer atau bahkan sudah bisa dibilang mid-level tapi tetap melakukan mutasi seperti di atas tanpa sadar akan konsekuensinya.
Kok bisa?? Bukannya yang satu harusnya undefined
dan yang satunya tetep 'Jihad'
?? Kok dua-duanya undefined
??
βMereka kira mereka bisa menjawab sedangkan mereka termasuk orang-orang yang tidak tahuβ β JS 1:12
Keduanya bernilai undefined
karena secara default, Object dalam Javascript sifatnya pass by reference, bukan pass by value ketika dilempar ke dalam suatu function/method. Jadi sebenarnya variable jihad
dan userWithoutName
adalah variable yang sama (point to the same address), hanya namanya saja yang berbeda. Untuk mengakalinya, kita harus ubah sedikit dengan object destructuring atau spread operator.
Now it works..
Nested Object
Prinsip di atas bisa diaplikasikan juga untuk Object di dalam Object. Karena sejatinya operasi { ...user }
tidaklah cukup jika object user
memiliki object lagi.
Keduanya lagi-lagi bernilai undefined
karena pada code di baris ke-2 hanya membuat Object baru di level pertama saja. Level berikutnya (profile.picture
dan profile.userName
) akan tetap menunjuk pada reference sebelumnya. Solusinya adalah dengan membuat object baru lagi!
Mirip dengan solusi2
. Dan alasan inilah kenapa saya lebih suka βstyleβ solusi2
dibandingkan solusi1
karena code-nya lebih straighforward dan terhindar dari any possible bugs yang diakibatkan oleh mutasi object.
Array
Aturan di atas juga berlaku untuk Array karena pada dasarnya Array adalah object π€
Dibilang Javascript ini rada-rada. Tapi intinya, diperlukan kehati-hatian juga dalam hal ini.
Intermezzo dengan React
Satu kasus yang cukup simple dimana mutability bisa mengakibatkan kita garuk-garuk kepala, mikir keras kenapa component kita gak jalan sesuai yang diharapkan. Mari berasumsi ada sebuah component yang gemuk dan expensive dari segi rerendering sehingga kita perlu mengimplementasikan method shouldComponentUpdate
Jika object user
diubah dengan cara yang mutable, component tersebut gak akan pernah bisa melakukan rerendering karena object user
yang baru dianggap sama dengan yang lama β bisa-bisa gak reaktif sama sekali. Immutability dalam hal ini membantu menghilangkan kompleksitas-kompleksitas yang sebenarnya tidak perlu.
Penutup
Sekarang kita sudah cukup paham behaviour Object di Javascript yang memiliki nature pass by reference π Ada beberapa keuntungan yang didapat jika menghindari mutasi variable dan object.
- Sadar atau tidak sadar, ketiga function di atas (
solusi1
,solusi2
,solusi3
) semuanya adalah pure function. Yang dimaksud dengan pure function adalah function yang tidak mengubah nilai di luar scope-nya. Ketiga function tersebut tidak mengubah objectuser
, mereka justru mengembalikan object baru. - Karena pure function inilah referential transparency dapat tercapai. Sehingga nggak akan ada ceritanya suatu function ngebuat error bagian aplikasi yang lain yang sama sekali gak ada hubungannya sama function ini. Unknown side effects are always evil.
- Dan yang paling penting: memudahkan proses debugging! Gak pingin kan dijadiin bahan cacian sama developer lain yang maintain code kita nantinya hanya karena rookie mistake begini.. π€ͺ
- Di beberapa bahasa yang support multithreading semacam Java atau Scala, Immutability dapat menghindari program dari race condition dan berjalan di thread yang safe
Saran saya pribadi: kalau mutability bisa dihindari, hindari saja. Kalau memang tidak bisa dihindari karena alasan-alasan tertentu, pastikan scope-nya tidak terlalu besar agar kedepannya lebih mudah di-debug.
Semoga bermanfaat π