Refactor All the Things
Programmers talk about “refactoring,” but they usually just mean “rewriting.”
Refactoring adalah kegiatan memperbaiki struktur internal program tanpa mengubah perilaku eksternal program. Hal ini menjadi sebuah kewajiban seiring berjalannya waktu. Biasanya, ketika ada fitur baru yang harus ditambahkan, maka akan ada refactoring pada kode yang berkaitan. Tidak hanya ketika itu saja, selain itu, ketika sebuah bug ditemukan, biasanya refactoring juga harus dilakukan karena bug tersebut tidak terlihat di awal karena kode yang kurang bagus.
Kenapa harus refactoring
Perbaikan desain perangkat lunak— Metode pengembangan perangkat lunak yang agile memungkinkan adanya perubahan requirement atau penambahan requirement baru di tengah berlangsungnya pengembangan perangkat lunak. Hal ini dapat menyebabkan munculnya kasus-kasus tertentu yang tidak terpikirkan di awal, sehingga menyebabkan desain yang sudah ada menjadi kurang bagus. Daripada dipaksakan melanjutkan pengembangan dengan desain yang kurang bagus, sebaiknya perubahan desain atau refactoring dilakukan sedini mungkin. Semakin besar kode maka perubahan desain akan menjadi semakin sulit.
Membuat kode lebih mudah dipahami — Biasanya, tekanan dari stakeholder membuat developer bekerja terburu-buru. Hal ini dapat menyebabkan developer berprinsip:
Product owner, pengguna produk, CEO, BOD, dan kawan-kawannya tentu tidak peduli dengan kualitas kode. Akan tetapi, rekan satu tim akan mengalami dampak langsung apabila menemui kode yang “not stupid”. Biasanya, karena tekanan dari berbagai arah, kode “not stupid” ini diloloskan code review. Sebaiknya sesegera mungkin kode tersebut di-refactor karena orang lain yang menemukannya mungkin butuh waktu yang tidak singkat untuk memahaminya. Waktu yang terbuang dapat diartikan sebagai uang yang terbuang, hal yang seperti ini lah yang dipedulikan oleh CEO dan BOD.
Contoh Kasus
Slow Image Asset Loading
Kasus ini diambil dari blog post sebelumnya, yaitu performance profiling. Masalah yang dihadapi adalah waktu pemuatan aset gambar yang cukup lambat walaupun sudah menggunakan high-performace google cloud storage.
Solusi yang tidak perlu memakai otak adalah meresize semua gambar dari aplikasi Paint lalu mengupload lagi ke cloud storage. Tentunya, solusi seperti ini tidak scalable. Sebaiknya, layar yang beresolusi rendah mendapatkan gambar resolusi rendah juga. Berikut solusi dari kami:
API Error handler
Tadinya, semua yang memakai backend API minimal terdiri dari dua error handler, yaitu kasus dimana server mengembalikan pesan error yang tidak diketahui frontend dan kasus dimana bahkan server tidak mengembalikan pesan error. Sekarang, kode frontend sudah sangat DRY.
Fake JWT Generator for Unit Tests
Kode menjadi jauh lebih ringkas setelah refactoring. Kebanyakan kode yang tidak ringkas adalah pada unit tests, ini berlaku untuk backend dan frontend. Hal-hal seperti ini lolos dari code review, mungkin karena yang mereview kurang krits.
Bonus: Design Pattern
Refactoring dan design pattern merupakan satu kesatuan yang tak terpisahkan. Biasanya ketika refactoring sedang dilakukan, developer baru tersadar bahwa kode yang sedang direfactor seharusnya menerapkan design pattern.
Dependency Inversion
Dependency inversion ini membalik yang tadinya setiap komponen meng-import dependency, menjadi kode yang menggunakan komponen terkait lah yang menyediakan dependency. Prosedur memberikan dependency ke setiap komponen terkait dikenal dengan istilah “dependency injection”. Pattern ini diterapkan di frontend dan backend.
Frontend menggunakan paradigma functional programming, sehingga satu-satunya cara untuk melakukan dependeny injection adalah melalui parameter fungsi.
Fungsi fetchJobEpic menggunakan dependency API. Di sini, dependency API diberikan oleh fungsi pemanggilnya. Hal ini membuat unit test menjadi sangat mudah, tidak perlu menggunakan mocking library yang canggih-canggih, cukup berikan komponen palsu saja.
Singleton Pattern
Pada backend, terdapat service-service seperti FirebaseService dan ImageService yang harus diimplementasikan sebagai object aktif dalam memory supaya kode yang memakai komponen ini tidak perlu memberikan API key berkali-kali, melainkan API key diberikan satu kali saja seumur hidup (selama server hidup). Pembuatan object replika berkali-kali tidaklah efisien. Maka dari itu, pattern singleton diterapkan.
Lazy Singleton Pattern
ImageService pada backend menerapkan lazy singleton pattern. Ini berarti instance ImageService hanya akan dibuat ketika ada komponen yang membutuhkannya. Setelah instance dibuat, instance akan aktif selamanya di memory.
Eager Singleton Pattern
Ini adalah lawan dari lazy singleton pattern. Pattern eager langsung membuat instance object ketika server dimulai. Pattern ini tidak diterapkan pada project connectdot karena dianggap tidak efisien membuat object ketika belum dibutuhkan.
Composite Pattern
Composite pattern memperlakukan sekumpulan object sebagai satu kesatuan object yang tipenya dianggap sama. Composite pattern ini banyak sekali diterapkan pada frontend. Setiap “React.Component” merupakan komposisi dari beberapa “React.Component” lain. Jadi, pada level tertinggi, sebuah aplikasi frontend react sebenarnya hanya punya satu komponen, yaitu “root”. Biasanya pada implementasinya, komponen “root” react juga hanya punya satu komponen, yaitu “app”. Baru lah “app” ini yang biasanya terdiri dari banyak komponen.
Decorator Pattern
Inti dari decorator pattern adalah penambahan perilaku object tanpa mengubah class si object itu sendiri. Pattern ini sangat berguna sekali karena kode class object terkait biasanya sudah ditest secara komprehensif dan perubahan pada class terkait biasanya dapat merusak perilaku object secara drastis. Dengan decorator pattern, developer hanya perlu membuat class “decorator” yang nantinya akan berisi object dari class asli yang sudah “diubah” perilakunya.
Pada frontend, decorator pattern diimplementasikan dengan teknik “Higher Order Function”. Higher order function adalah fungsi yang menerima fungsi, dan biasanya harus mengembalikan fungsi lagi. Berikut adalah implementasi decorator pattern pada frontend.
Komponen JobListinSearchForm tadinya hanyalah form HTML biasa. Dengan decorator Higher-Order-Function “reduxForm”, komponen JobListingSearchForm mempunyai fungsionalitas reduxForm, yaitu isi dari form terkait sekarang menjadi bisa diakses secara global oleh komponen apa pun yang terhubung dengan “redux”.