Before deciding to refactor code, we will first need to understand why it needs refactoring and whether it is worth the time investment. Some of the common indicators that the code needs refactoring are:
- The code is hard to understand
- There’s redundant code
- Methods are long and complicated
- Methods are hard to test
- Tests have a bunch of repeated setup code
- Classes are missing functionality
- Crucial parts of the codebase are missing tests
Now let’s look at a few simple ways to refactor code.
Fix incorrect or inconsistent naming
Variables, Methods or Classes with ambiguous names are sometimes the best low hanging fruits yet useful ones to refactor. If there is inconsistency in the naming format such as snake_case vs camelCase, make sure to use the same format throughout the code. Try and follow the convention for that specific programming language. For ex: snake_case is more popular in Python but Java code goes with camelCase. Move constants to a separate file and use all CAPS in their variable names.
// Existing Code
first_name = Person.getFirstName();
last_name = Person.getLastName();
//Refactored Code
firstName = Person.getFirstName();
lastName = Person.getLastName();
Make it modular
If you find functions that are super long and hard to understand, capture chunks of code into separate functions and give them a relevant name. Each function should focus on a specific objective. If it is starting to have more than one purpose, the other ones should be delegated to new functions. It gets tricky when you have to decide between creating an instance method vs a standalone function.
- If a set of methods define or modify the object instantiated by a Class, then include the function as an instance method of a Class.
- If the function cannot belong in any Class or is generic enough to be used in multiple places, then create a standalone function.
# Existing code
def extract_user_info(info_object):
first_name = info_object["name"]["first_name"]
last_name = info_object["name"]["last_name"]
user_address = info_object["address"]["line_1"] + info_object["address"]["line_2"]
user_age = info_object["age"]
# Do more things with the above info
# Refactored Code
class UserInfo:
def __init__(self, user_info):
self.user_info = user_info
def get_user_name(self):
return " ".join(self.user_info["name"]["first_name"], self.user_info["name"]["last_name"])
def get_user_address(self):
return ",".join(self.user_info["address"]["line_1"], self.user_info["address"]["line_2"])
def get_user_age(self):
return self.user_info["age"]
def extract_user_info(self):
user_name = self.get_user_name()
user_address = self.get_user_address()
user_age = self.get_user_age()
# Do something more with all this info
Remove duplicate code
Code duplication can creep up in many ways.
- When a list of setup steps have to be copied over multiple times
- When you are trying to change just one thing about a Class, but this involves changing/ adding new methods to handle this change
- When you find yourself copy pasting the same few lines over and over again
Let’s first learn about the Single Responsibility Principle. It states that every module, class or function in a computer program should have responsibility over a single part of that program’s functionality, and it should encapsulate that part. Source: Wikipedia
- If you find that a class is doing too many things, capture the non-core functionalities into a separate class.
- If a class needs to have all the properties of another class but with additional/ changed functionality of certain instance methods or variables, then try to add inheritance to the classes.
- If too many classes have similar/ duplicate functionality, then create a super class.
- If multiple files or modules are using the same set of code lines to accomplish something, for example: setting up a database connection etc. then create a separate function for this setup and use it in all the places.
Expand incomplete classes
Classes are incomplete when they don’t provide the user with the right functions to access the method variables.
A getter method returns the value of an instance variable while a setter method sets or updates the value. These methods make it safer to access or mutate an instance variable and should be made available as instance methods of a class. Overriding the toString() method in the class that gives the textual representation of an object at any moment can be a useful addition for clients to debug the objects containing user specified values.
class User {
private String userName;
private int age;
private String address;
User(String name, int age, String address) {
this.userName = name;
this.age = age;
this.address = address;
}
// Example setter method
public void setName(String name) {
this.userName = name;
}
// Example getter method
public void getName() {
return this.userName;
}
// Override toString method
public String toString() {
return this.userName + "is" + this.age + " years old and lives at: " + this.address;
Introduce type checking
This part refers to languages that use dynamic typing. Compile time type checking is super useful although Python programmers may have been very used to dynamic typing. Missing type declarations can make it quite difficult to understand the type of various parameters in the code especially when we are dealing with complex types such as maps, objects etc. This can also introduce bugs into the code if parts of it are not meticulously explained with comments so that developers don’t mishandle objects. For example: you can look into tools such as mypy to add typing to a Python code base.
def extract_user_info(user_info_map: dict[str, str]) -> str:
Add unit tests
Refactoring any code can introduce bugs if there isn’t a proper test coverage. Make sure that parts of the code that you plan on refactoring has unit tests to begin with. Then write new tests or extend the existing tests before changing the code so that you can develop iteratively using Test Driven Development.
You can go one step further and look into coverage tools that give a detailed summary of lines of code that have not been tested.
Now get refactoring!!
