Refactor All the Things

Yudhistira Erlandinata
Energizer AAA
Published in
5 min readApr 17, 2019

Programmers talk about “refactoring,” but they usually just mean “rewriting.”

Gambar 1. Sebuah ilustrasi terkait Code Smell

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:

Gambar 2. Ilustrasi prinsip “If it is stupid but it works, it is not stupid

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.

Gambar 3. Pemuatan aset gambar beresolusi 2812x1560 memakan bandwidth 280KB

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:

Gambar 4. Pemuatan aset gambar menggunakan image resizer. Ukuran gambar sekarang menjadi 630x350 dan dapat diatur-atur. Bandwith yang terpakai menjadi 50KB.
Gambar 5. Pemakaian service image resizer pada kode.

API Error handler

Gambar 6. Hasil refactor 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

Gambar 7. Hasil refactor unit test backend

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.

Gambar 8. Fungsi fetchJobEpic

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.

Gambar 9. Unit test fetchJobEpic

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.

Gambar 10. Singleton pattern pada ImageService

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.

Gambar 11. Komponen “app” pada frontend ConnectDot

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.

Gambar 12. Penerapan 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”.

--

--