Skip links

Refactoring the code with no pain

Refactoring the code in legacy software is an opportunity to restructure and improve the code source while keeping the same features. Mastering refactoring techniques can save time than throwing the code away and building it from scratch. Refactoring of code becomes strategic when you need to perform modifications to a legacy code base, especially when it lacks unit tests to ensure you won’t introduce regressions. This post is a short journey toward 8 refactoring techniques that would ease your work as a developer. They stem from a live session on refactoring we recently hosted. Check out this page to register to the next sessions.

Refactoring Tip #1: Give more meaningful names to code elements

Let’s start with a simple refactoring technique, the renaming. A common guideline in software engineering is to name your variables, functions, and classes so that we can understand their intent and their business value. Good naming will drastically improve the understandability of a piece of code. Assume now you cope with the following code in a class called TennisGame:

					
				
Not an easy one, right? If you struggle a little bit, you’ll eventually figure out the s variable, which has a key role in this function, is used to store the displayed score of the tennis game. Again, with your IDE, a quick win is to perform a rename operation:

					
				
At a glance, we quickly understand now the intent of this variable.

Refactoring Tip #2: Extract loop content in a separate method

Consider the following code (from the Gilded Rose kata):

					
				
This code has several indentation levels, making it more difficult to read and understand. One quick operation supported by all main IDEs is the Extract Method feature. Give a meaningful name, and here you go!

					
				
We now have two methods with separate responsibilities (browse the collection and compute each item) and a removed indentation level. Extract method operations are common when refactoring the code.

Refactoring Tip #3: Separate boolean conditions in nested if 

This one is not necessarily valid in all contexts, but in some cases, it can help make the code more readable and help understand the code’s decision tree. You should see this practice as an intermediate step during your refactoring. Consider the following code:

					
				

If we burst the conditions in the if statement, we obtain this result:


					
				
See the intent? You can observe that code in … else branch is indeed duplicated, but you may agree that the conditions are easier to read now. Once you’re more confident with this code, for instance, if you manage to write unit tests that cover the different cases, you’ll be able to make new changes in your code.

Refactoring Tip #4: Rewrite negative conditions

Similar to Tip #3, this is an intermediate step. Too many conditions can increase the mental load required to understand a code. A piece of advice can be to remove the negations, which means turning the following code:

					
				
Into this one:

					
				
Even though the first condition has no instruction, it still improves reading this code.

Refactoring Tip #5: Encapsulate a static dependency

This case commonly happens with the usage of the Singleton pattern. There are 2 issues with a singleton: 1) you can’t override it 2) its shared state can change from different unit tests execution, making them not reproducible depending on their execution order. The idea here is to extract the call to the dependency in a method so that we can override the default behavior in a sub-class during our unit tests. As an example here:

					
				
This can be turned into the following:

					
				
The getLoggedUser method will then be overridden if needed in our tests suite.

Refactoring Tip #6: Return early to reduce complexity

A method containing multiple if conditions and exits (return or exceptions thrown) might have a complex decision tree with several indentation levels. The suggestion for refactoring the code here is to turn conditions into their negative form to leave the method as soon as possible. Think as a Fail fast approach. Here is below an illustration with a method containing four exits points:

					
				
We can turn it into the following code that contains four flattened if conditions, reducing the cognitive complexity of understanding this code:

					
				
You might see this is counter-intuitive if we consider Tip #4. Remember that the most important thing is to remain pragmatic, depending on the context of your code. Refactoring is about improving the maintainability of the source code, so you’re likely to go through different intermediate steps; trust yourself to choose the one that helps you the most.

Refactoring Tip #7: Declare variables at the closest locations of their usage

An easy trick to improve the readability of a source code is to make sure you don’t declare a variable and use it several lines after. Reading code that doesn’t follow this practice lets us think you give irrelevant information, then do something else, and finally, use the previous information since you need it now. In this example, there’s no need to declare conversionRate so early:

					
				
So while I’m reading the first condition, I’m confused about the role of this variable. Instead, declare it just before using it:

					
				
The full story is easier to read, and you can keep refactoring the code.

Refactoring Tip #8: Encapsulate primitives types into business types

Business types make it easier to express intention and logic while ensuring validation. In the following code, we need to make assumptions about what are the three input parameters (despite the clear naming):

					
				
But you can see that the business logic with the amount < 0 condition is scattered in the code and should not be there. Instead, we can suggest the following code, introducing the Amount and Currency classes, which allows encapsulating the previous condition into a method isNegative().

					
				

Want to promote your refactoring practices?

In your context, there are certainly more best coding practices you’d like to share and gather with your team, not only about refactoring but also about architecture, security, a programming language, and even more. Packmind integrates with your IDE to help capture your source code modifications that illustrate a best practice to follow or not. Then you can share your expertise with your Packmind and review it during a dedicated workshop with your team. When refactoring your code, you can value your source code operations and make your expertise available for developers in your project and organization. Example of best practice of refactoring This is an example of what a best practice looks like in Packmind.

Ready to refactor the code you’re working on?

We bundled these practices into a catalog available on the public Hub of best coding practices powered by Packmind. Finally, if you’re looking for more refactoring techniques, you can check the dedicated section on the excellent Refactoring.guru website; more than 50 were available when we wrote this post. Hoe you enjoyed the read! Do you want more insights and best (coding practices)? Check out this page to register to the next live sessions.