Refactoring workflows

Refactoring is a controlled technique for improving the design of an existing code base. Its essence is applying a series of small behavior-preserving transformations, each of which “too small to be worth doing”. However the cumulative effect of each of these transformations is quite significant. By doing them in small steps you reduce the risk of introducing errors. You also avoid having the system broken while you are carrying out the restructuring — which allows you to gradually refactor a system over an extended period of time.
Martin Fowler —

The key points are:

  • Improvements in Small steps, spanning over a few minutes or maybe a few hours.
  • Reduce the risk of introducing errors or broken systems as the restructure is done in small steps.

If either of the above is not met, then what we are doing is mostly rewrite not refactor.

The term “Refactoring” has been misused heavily. Many refer to Refactoring as a major restructure or rewrite of the application done over an extended period than in small steps.

Long-term refactoring

The most popular refactoring technique is done as part of the TDD cycle in which you add enough code to make the test green and then move on to clean up the code for better design and readability.

Another technique is by applying the boy-scout rule, i.e. cleaning up existing code while we are working on it, to improve readability and design to help us add further code to the same section in the future.

But this idea of small steps can be applied for major restructuring in the application such as changing frameworks or libraries which are being used heavily in the application. The standard technique, Branch by abstraction, is done in small steps by introducing an abstraction layer and moving to the new implementation over a period.

Workflows of Refactoring

Martin Fowler talks about five different types of Refactoring workflows the teams can apply in various scenarios.

The article refers to two hats the programmers have to wear while programming, but at a time one can wear only one hat.

Two Hats of programming

This metaphor of two hats helps us to focus on the right task — focus on the feature while wearing the Adding Function hat and focus on Refactoring while wearing the Refactoring hat. This way we end up making progress on both, adding function and refactoring, and don’t mix up both.

We spoke about TDD Refactoring, Litter-Pickup Refactoring [using the Boy-scout rule] and Long-term Refactoring already. The Comprehension Refactoring is somewhat similar to Litter-Pickup Refactoring with the focus on improving the comprehension of the code — improve the understanding for the reader.

Preparatory Refactoring

Sometimes we realise that we need to make some refactoring before adding new functionality. For e.g., I realised that I need to split a table into two, before adding further code.

Splitting table

The initial system had the support for only one payment per billing cycle. To support multiple payments for customers who make partial payments during the billing cycle, we first refactored the code and then added the support in the UI for the same. If we had delayed the refactoring, it would have added a lot of complexity into the system.

This refactoring called the Split Table Refactoring has a transition period from the old schema to the new schema. And both the old and new schema existed during that time, and later the old schema was dropped.

Planned Refactoring

Sometimes we delay the refactoring over delivering code faster, i.e. we deliver the code without enough refactoring, and then come back and do the appropriate refactoring. We do that because we value the learnings by delivering it early rather than delaying it until we refactor.

I do that frequently, for e.g., at times deliver with duplicated code than applying any refactoring to avoid the duplication. The key over here is to fix it ASAP before it becomes a debt.

Continuous Refactoring is very key to Continuous Delivery. And we switch between all kinds of refactoring mentioned above on a frequent basis. While the name for the type of refactoring doesn’t matter, but what matters is:

  • The timing of the refactoring
  • Balancing adding function and refactoring

For e.g., while reading the code if we realise that the classes and/or methods can be named better, it is better to do that then and there. The usual techniques are extracting bigger methods to smaller ones or renaming methods for better readability [Litter-pickup/Comprehension Refactoring] than planning it for later [Planned Refactoring]. This slide deck by Martin Fowler about Workflows helps us to be aware, plan and execute refactoring better.