I recently had a discussion about refactoring with Rafaël A. Van Coppenolle, Product Director at Nexapp and Axify, where he was looking to learn more about the developer's work. More specifically, how to justify the refactors we perform during development?
My answer was: It depends...
I know, I know, it's not a very satisfying answer!
However, the concept of refactoring is quite broad, so it's hard to group everything under a single answer. Let's take a moment today to talk about the different types of refactoring, why they are relevant, and most importantly, when are they relevant?
Code Readability and Clarity
A first type of refactoring aim to increase the readability and clarity of the code. We want the latter to be as easy to understand as possible. A developer spends the vast majority of their time reading rather than writing. The ratio is around 90% reading time, versus 10% writing time. It is therefore relevant to work on increasing reading speed when a team wants to increase its development speed.
When is it relevant to work on code readability? Always! During the development of a feature, it is important to make the effort to structure the code well, to name variables and functions well, and to have well-formatted files. The earlier the effort is made, the better the development will go and the less significant subsequent refactoring will be.
When our feature requires modifying existing code, it happens that the latter has lacked a little love. The refactor then becomes relevant to clarify what the code does, develop the necessary knowledge to do the work, and make the code simpler for those who will pass by later.
Recommendation: When a refactor could facilitate your work, create a branch next to the features with only the refactoring. Open a "merge request" with these changes and start your feature on these changes. Separating the refactor and the feature will make your peers' work easier when reviewing your code.
New software architecture
When I was discussing with Rafaël, he told me stories about developers who mentioned large refactors requiring several days of work. This type of change is rarely only to increase readability. Generally, it occurs when the current architecture no longer meets the new needs of the product. A new feature? A rapid change in product vision? There are several reasons.
It is important to understand that the need to change the architecture does not come from a bad decision or a bad implementation. Keep in mind that the team that set up the initial architecture did so to the best of their technical knowledge, as well as the product to be realized. We build software. As the name suggests, it's soft, flexible. We should expect the code to be malleable and constantly evolving.
Too often, this reality of software development is not explained to clients. Developing an application is extremely different from building a bridge. Building a bridge is complicated, but there are no unknowns. The plan is final and will be implemented in the same way, no matter how many times the plan is built. It is not possible to say during construction: "Hey, I changed my mind. I would like to have one more lane in each direction!". If building a bridge is complicated, developing an application is something complex. For the same feature, there are a thousand ways to implement it. Moreover, it is possible, and even common, for the client to make changes along the way!
Therefore, it is necessary to take the time to explain to clients and managers that changes in the existing code do not necessarily mean that the work was initially poorly done.
The Scouts' rule for refactoring
During the development of a product, it happens that team conventions change. Whether it's for naming, file structuring, or communication between two systems, there will always be changes in the way things are done. When this happens, care must be taken in how these changes are applied. Applying a decision to the entire project can have major repercussions. Depending on the size of the latter, a convention change may require several days of work before the entire project is up to date. It is in these situations that management, and even the client, begins to ask questions. This kind of refactor is extremely difficult to explain and justify. How can we prevent this from happening?
An interesting practice is one of the Scout rules: always leave the place a little cleaner than when we arrived. This means a refactor should be applied only in the part of the code on which the team is working.
Take, for example, a team that decides to abstract a library to eventually get rid of it. The first reflex would be to do the work all at once! However, this is used everywhere in the application, so it represents a colossal change. A full week of work. What will happen, in the short term, to the team's velocity if the change is made all at once? It will decrease! And that's when the client starts asking questions.
And what if, in addition to the decrease in velocity, an error is introduced into a part of the application that has not been in development for months. What will happen? More questions from the client!
Making the change all at once has a lot of impact on the product development, as you can see. Using the scout rule and applying the change only if we have to do in this part of the code solves these problems. As development progresses, our library abstraction will continue to advance. It can take 6 months, 1 year, 2 years! The goal is to constantly increase the quality of the code without causing adverse impact. By applying this rule, it becomes not needed to justify this type of work to the client.
How to change your vocabulary to better promote refactoring
The term "refactor" doesn't mean much to those who are not in our field. It can even scare some people! All it takes is one bad experience with a development team and the word becomes a "red flag" for the client. A change in the developers' speech can greatly improve communication with the product team and the client. It's about replacing the word "refactor" in our vocabulary with "preparing the ground".
Before starting a feature, we need to examine, analyze the code, and then prepare the ground to do our job well. This is an essential step in software development to get a quality product that will evolve well over time. This is part of our job as a software developer and we should not have to justify it, in the same way that an electrician should not have to justify the time it will take to go and cut the power before doing his job.
By changing our vocabulary to say that we are preparing the ground, it leads us to ask interesting questions about the refactoring we want to carry out.
- Is it relevant to prepare the ground after completing a task?
- Is it relevant to prepare the ground for work to be done in three months?
- Is it necessary to prepare a ground the size of a football field for a feature as small as a shed?
This way of communicating brings a certain structure to the size, timing and relevance of a refactoring.
I would like to finish with a quote from Martin Fowler from his book "Refactoring: Improving the Design of Existing Code":