4 Refactoring Techniques to Boost Code Quality
Check how you can improve the readability, maintainability, and overall quality of your code using these proven techniques. You will learn how to write cleaner, more efficient code!
So you have written some code and your application works how you wanted it to. You are adding new features and you notice that time comes… refactoring!
For many developers, refactoring is a frightening process. In my opinion, the problem is their lack of self-confidence about the code they have made, and the second thing is the lack of unit tests that can give them the feeling that their refactoring has gone well.
I want to help you in this process by showing you the 4 refactoring methods I use most. After reading this article, you should be able to easily identify code smells in your code and refactor them quickly.
Nested conditional hell
In my opinion, this is the easiest of all code smells to pick up on. When you see code with a group of nested conditions, this is a sign that you can make the code easier to understand. You just need to rewrite each condition and return the value as quickly as possible.
function getPetFood($pet) {
if ($pet->isCat) {
$result = catFood();
} else {
if ($pet->isDog) {
$result = dogFood();
} else {
if ($pet->isBird) {
$result = birdFood();
} else {
$result = unknownPetFood();
}
}
}
return $result;
}It takes much more effort to read and understand this than to read and understand our changed code:
function getPetFood($pet)
{
if ($pet->isCat) {
return catFood();
}
if ($pet->isDog) {
return dogFood();
}
if ($pet->isBird) {
return birdFood();
}
return unknownPetFood();
}Because we have used techniques that return results as quickly as possible, it is much easier to read code, make changes, and write tests. The conditions are clear.
Throw an Exception instead of returning a null
This is a common way for many developers to handle non-existent objects (from databases for example). Null looks like a good solution when you have a function whose role is to return an object based on some parameters, and the object cannot be returned.
What is wrong with this approach? Let's take a look at an example.
function getUserById($id): User|null
{
$user = getUserFromDatabase($id);
if ($user) {
return $user;
}
return null;
}This looks like a classic piece of code for a repository or a service. What this function does is return an object or null, which requires us to check that we are working with a user or null every time we use this function, so we are repeating ourselves in the code.
function getUserById($id): User
{
$user = getUserFromDatabase($id);
if($user == null) {
throw new Exception('User not found');
}
return $user;
}After refactoring, any developer who is going to use our function will be sure that if the function returns something, that means that we have a User object that we can work with as we expected. Now in our code, we use the try-catch language mechanism to catch unexpected situations and deal with them, which is used in situations like connection with a database, so we do not add another condition to the code.
Long function arguments list
If your function has a long list of parameters that can mean a couple of problems, like for example your function is doing too many things, but in this case we are going to focus on refactoring methods that will help us to make this list easier to maintain and reuse.
function summaryOrder($orderPrice, $productList, $discount, $shipping)
{
// some calculations for order
}In situations where my function parameter list is growing I just group parameters into an object. This gives me an option to easily add new data to it and also lets me reuse it across my code base.
function summaryOrder($order: Order)
{
// some calculations for order
}To let you use this technique parameters list must be related to each other without that you have to think about excluding part of the code to separate functions because probably too much is happening in your function.
Extract a method
In the world of software development, this technique is the most controversial. Even though we have a lot of books about it, we have design principles like SOLID. For a lot of developers having smaller functions is still overengineering.
I am in a group that tries to have small methods, this gives me many advantages: My functions are self-documented because of the single thing they do, writing unit tests is easy, and for other developers my code is much easier to understand.
So let’s jump into an example where I would use such an extraction.
function employeeSalaryReport($employee)
{
$salaryDetails = getEmployeeSalaryDetails($employee);
print("Total payment: {$salaryDetails->total}");
print("Total tax: {$salaryDetails->tax}");
print("Net pay: {$salaryDetails->netPay}");
}We want to prepare a report for the Employee about how much he is getting paid. It looks like a small function that only shows us the report. But I see space for refactoring here.
function employeeSalaryReport($employee)
{
$salaryDetails = getEmployeeSalaryDetails($employee);
printSalaryReport($salaryDetails);
}
function printSalaryReport($salaryDetails)
{
print("Total payment: {$salaryDetails->total}");
print("Total tax: {$salaryDetails->tax}");
print("Net pay: {$salaryDetails->netPay}");
}After these changes, we have a new function that is only responsible for printing the report. We can reuse this function in other places and we can write unit tests for different cases much easier.
I understand that this method is very individual. You have to discuss with your team where you want to set the boundaries for this kind of refactoring.
Summary
Refactoring is a huge space to discuss, to learn, and to find your way. It can help improve code quality, readability, and maintainability, and is an important part of software development.
There are many different refactoring techniques that can be used. To learn more about them, I recommend these two sources:



This was a great read. Thanks for sharing Michal sir!