This website uses cookies. By using the website you agree with our use of cookies. Know more

Technology

Is Refactoring a way to speed up delivery?

By Carlos Corrêa da Silva
Adept of the Software Craftsmanship movement and passionate about Extreme Programming. Loves coding iOS and wearing The North Face.
View All Posts
Is Refactoring a way to speed up delivery?
It is common to hear the word "Refactor” associated with a negative connotation. To some people, it may sound like duplicated work, money loss, a waste of time, going slow, and the list goes on. But is this what refactoring is all about? 

First, we have to understand what it is, and according to Refactoring.com, from one of the best-known technologists on the matter, Martin Fowler:

Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behaviour.

What often happens is that the existing code we need to alter slowly becomes a huge part of our system, and it finally ends as a very important almost "untouchable” engine that makes the entire machine get the job done. In situations like this, touching this code can be risky, especially if you don’t have tests to make sure you are not going to break it.

Picture this: The code became messy, the development team is claiming for improvements, features are becoming hard and slow to deliver and everybody is afraid of touching it. The question is, how do you avoid getting to this point? Is this the right time to do something about it?

Refactoring is not meant to be put in practice only when there’s a situation where you can’t do anything else other than erasing what was written and rewriting it from scratch. You and your team shouldn’t have to ask for a User Story to refactor the existing code or even convince someone that "it is necessary for a greater good”. Of course, there are situations where the requirements have completely changed and the current solution is no longer viable, so it is recommended to have in mind these "preparatory refactorings” when estimating the user stories.

For enterprise systems to become flexible, understandable, and easy to maintain, this practice has to be part of each team member’s skill set. Every time you and your peers touch the code it is an opportunity to refactor, to leave things better than you found. Yes, I’m referring to the old Boy Scout Rule, which states: 

"Always leave the campground cleaner than you found it”.

Whenever you have to build functionality on top of existing code, before starting to dig in, one should take a step back, look at the big picture, and ask yourself: Is there something to improve here? Does the current code provide the best solution for the given problem? Is this code slowing us down? Is it clean? Is it optimized? Are there any Code Smells?

As of today, the Farfetch iOS app has a codebase which is still a mix of Swift and Objective-C, and sometimes, it is part of our job to add new functionality on top of this existing Objective-C foundation.

Tasks like this are often seen as good candidates to refactor because since we’re moving to an entire Swift codebase, it’s like a red carpet invitation to improve the existing code, pull out some logics to brand new Swift files, write tests, etc. It’s important to use your refactoring-sense to be aware of situations to put it in practice. Consider that large refactorings tend to be risky and time-consuming so if you face something you can’t refactor right-away, go back to your team and point out the code that requires some work in the long term.

For cases like this, there’s a workflow that may help you to get the most out of a task other than simply adding code on top of an existing foundation.

The Workflow

1. Look at the tests of such class/module

If there are no tests, stop immediately and write it, so you know everything is still working fine while adding and changing the code.

On the other hand, if the code already has tests, look at how they test the code to get a better sense of what it does and how it behaves to the external world. This will help to identify the entities involved and to get a glimpse of the big picture. Dig into the code structure and see if there are any areas to improve.

2. Get a Whiff

In this moment of studying, always keep one eye out for Code Smells (yes, code definitely can stink!). For each smell, take notes on a piece of paper or in a notes app to better organize yourself. A simple bullet list grouped by Code Smell containing files, classes, and functions to revisit later to see how to improve them.

Example of a hypothetical list of smells found to be treated:

  • Feature Envy
    • checkStockForProduct method in StockProvider class
  • Large Class
    • StockProvider has 1542 lines of code (a possible Single Responsibility Principle violation)
    • CreditCardValidator class has 645 lines of code (there is some duplicated code in here)
  • Refused Bequest
    • SizesDataSource is a subclass of ProductsDataSource to take advantage of the products list property, which is causing us to override functions that are not meant to be used by this class.
  • Primitive Obsession
    • sellProducts function takes a lot of primitive values used to calculate the taxes. Maybe we should consider encapsulating these values and pass in an object instead.
The Code Smells list is big so we encourage you to search on the web or check out some books about this subject (Fowler has a great one called Refactoring) and get to know better how to fix them.

3. Add new code

Only then, start to Test-Drive your way to the solution, taking advantage of the fact that one of the steps of Test-Driven Development is the refactor itself. 



Once you are done with adding the functionality, it’s time to look at the list of items to refactor. It’s important to do these changes in separate steps: add the new feature in and only then, make the improvements. You might feel the need to refactor something that is in your list during the development phase maybe because that code is slowing you down or is way too difficult to understand. It’s easy to get messy if you do both at the same time but if that is the case, don’t worry. Stop adding new code, make sure your tests are passing, and only then, refactor it.

The preferred cycle for refactoring the existing code without adding new functionality is to always be in a green test suite situation, like a checkpoint, meaning that everything is working and you can keep pushing new small changes in without breaking anything. The tests should always be your safety net. This can be represented by another flow very similar to Test Driven Development:



Make one small refactor (extract a function for example), run the tests to make sure they are passing and commit the changes. Small cycles for huge benefits. 

Try to stay in a green state every two or three minutes, because if your change is taking too long to pass the tests, maybe the problem is bigger than you predicted.

The Benefits

By the end of this workflow, the team will see great improvements as the codebase starts to present higher code standards and desired evolutionary characteristics like:

1. Testability

Test-Driven Development makes us write code that can be fully tested in isolation and, because the code has tests, it will be easier to keep refactoring it in the future. Decoupled code is a lot easier to test.

2. Clean Code

There’s always a large function to break into smaller functions, change a weird name of a variable or delete irrelevant comments. With the refactoring mindset, every time you understand a complex piece of code, automatically begin to clean it out so you and your peers won’t need to reason too much about it in the future. There are other nice things about Clean Code that you can apply, for example, make sure the code adheres to the SOLID principles.

3. Get some bugs fixes for free

According to a paper named "On the relation of refactorings and software defect prediction” by Ratzinger et al., 2008:

"The number of software defects decreases if the number of refactorings increased in the preceding time period. As a result, refactoring should be a significant part of both bug fixes and other evolutionary changes to reduce software defects.[…]
[…]This means that an increase in refactorings has a significant positive impact on the quality of the software."

Also, this is an example applied to a feature development, but you can also apply this kind of workflow for bug fixes, as one shall write a test for the failing case scenario exposing the bug, fix the issue and only then, improve the code.

The Speed Up

We know that all this Refactoring, TDD and Clean Code buzzwords might look like a lot for "just adding that new if statement, along with the other 30 conditionals within that 350 lines long function” but, if no one does anything about it, the team is automatically agreeing on increasing the technical debt and adding code that nobody will ever want to mess with.

The time passes and now the team takes 3 times more to deliver a feature that was supposed to be something straightforward, just because they failed on refactoring more often.

The Design Stamina Hypothesis, also from Martin Fowler, is a pseudo-graph that illustrates in theory how a good design can help us to deliver more in a long term goal, and refactoring is a key factor in reaching a good design over time. Because as soon as we start improving the code the sooner it will get better, as opposed to being constantly damaged. We know that teams usually come up with a nice design at the beginning of a project but it is very rare to see it evolving healthily without constant improvements in code. 



If you ever need to convince someone about the benefits, it all comes down to simple economics: if you keep refactoring your code, you are moving towards a good and evolutive design that allows you to deliver more, as opposed to steadily closing your eyes to the mess, slowing down and taking more time to deliver a feature. 

It may sound pretty scary, but it’s logical to think that:

"By not refactoring, you’re being responsible for your company to waste more money than it should. And if you’re a shareholder, remember that it’s also your money."

So yes, if you keep your codebase healthy by constantly doing small refactorings, you may draw a nice curve towards a faster delivery cycle. If you and your company want to keep evolving, reshaping and innovating, remember to do the same with the code. Evolve it, keep polishing it, make it versatile, resilient. Do not close your eyes and stop giving excuses. 

Be relentless and clean up the mess now!
Related Articles