The importance of refactoring in code readability
Oziel Rea • March 25, 2024
clean-codeI'm back after a long time since my last post (and first one). I've been very busy with work and personal projects, but I haven't forgotten about this blog.
Today I want to talk about the importance of refactoring in software development, with some real examples I've faced in my career.
What is refactoring?
Okay, refactoring is a fancy word, but what does it actually mean? According to Wikipedia:
Refactoring is the process of restructuring existing computer code without changing its external behavior. It is intended to improve nonfunctional attributes of the software. Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior.
So what I get from this definition is that if we change some part of the code to improve it, but the external behavior remains the same, we're good to go.
Why is refactoring important?
Refactoring is important because it helps us understand the code better and keeps our code clean. But what does it mean to have clean code?
I like these words from Refactoring Guru:
Clean code is obvious for other programmers...
Clean code doesn't contain duplication...
Clean code is easier and cheaper to maintain!...
I love the part about "Clean code is obvious for other programmers" because we have to remember that we're writing code for humans to read, not for machines. You can write a mess of code, but as long as it works, the machine doesn't care if it's clean or not—other programmers do.
Real examples
Okay, enough theory. Let's see some real examples of refactoring for code readability. Don't worry too much about the business logic; just focus on the method structure.
# Before
def period_in_force(main_periodicity, period, investment)
main_periodicity.zero? &&
period.id.present? &&
((period.id + 1) % investment.other_periodicity).zero?
end
# After
def period_in_force?(period, main_periodicity:, other_periodicity:)
return false unless main_periodicity.zero?
return false unless period.id.present?
period.should_pay?(other_periodicity)
end
We even took advantage of named arguments and method naming in Ruby to make the code more readable, so we can use the method like this:
# using the method
period_in_force?(period,
main_periodicity: 1,
other_periodicity: investment.other_periodicity)
We also removed the investment parameter because we only need other_periodicity from investment, not the whole object. We also extracted some of the interval logic to a method called should_pay?.
Let's see another example:
# Before
def next_payment
payment = next_interest_payment
other_kind_of_payment = next_interest_payment_of_other_kind
return payment unless other_kind_of_payment.present?
return other_kind_of_payment unless payment.present?
aux_date = [other_kind_of_payment.date, payment.date].min
if payment.date == aux_date
return payment
else
return other_kind_of_payment
end
end
# After
def next_payment
payment = next_interest_payment
other_kind_of_payment = next_interest_payment_of_other_kind
return payment if other_kind_of_payment.nil?
return other_kind_of_payment if payment.nil?
[other_kind_of_payment, payment].min_by(&:date)
end
First, we changed the present? method to nil? because we only expect instances of a Period class or nil values. This allows us to change the unless condition to an if condition, which makes the code more readable in this specific case. We also removed the aux_date variable and used the min_by method to get the object with the minimum date value.
Okay, this is good, but I think we can go further. Let's change the code a little bit more:
def next_payment
[
next_interest_payment_of_other_kind,
next_interest_payment
].compact.min_by(&:date)
end
We removed the payment and other_kind_of_payment variables and used an array to store the values. Then we used the compact method to remove the nil values from the array, and finally we used the min_by method to get the object with the minimum date value.
In closing
The examples I showed are very simple, but I hope you get the idea of how little changes can make a big difference.
There is no perfect code—we can always improve it, and refactoring is a great way to do that. So don't be afraid to break things, but remember to have automated tests! 😅
I'd love to hear your thoughts on this. Do you have any refactoring tips? Let me know—get in touch with me on my contact page.