Extra Meal. Do You Really Understand Refactoring

Bonus: Do you really understand Refactoring? #

Today (March 15th), the Chinese version of Martin Fowler’s “Refactoring: Improving the Design of Existing Code” second edition has been officially released. Not long ago, Professor Yang Hailing from Renmin University approached me and asked for my help in writing a recommendation for this book. Without any hesitation, I gladly accepted the opportunity to write a recommendation for this classic work, which is a personal honor.

However, I immediately realized that in my column, I only mentioned refactoring when discussing TDD (Test-Driven Development), but I didn’t dedicate a separate topic to it. So, I’ve decided to provide an additional segment to my readers, specifically discussing refactoring. After all, refactoring is a term that almost every programmer will come across. But do you really understand refactoring?

Things Every Programmer Should Do #

As programmers, we all want our code to be perfect. But no code is perfect because as long as your code is alive, there will always be new requirements coming in, often ones that you didn’t anticipate when writing that piece of code.

Many people’s instinctive choice is to continue writing based on the existing code structure, adding an “if” statement here and a flag there. Over time, the code becomes rotten.

If we describe this phenomenon using a term from physics, it would be “entropy increase,” which is famously known as the second law of thermodynamics. If there is no external intervention, the system will develop towards increasing disorder. One way to counteract entropy increase is by introducing negative entropy, making the system more ordered. The process of introducing negative entropy into the code is called “refactoring.”

Adjusting code is a habit that all programmers have, but making it a more systematic practice, elevating it to the level of “refactoring,” started within a small circle. The core of this small circle is the two master programmers mentioned earlier in this column: Ward Cunningham and Kent Beck.

However, it was Martin Fowler who truly brought this concept out of the small circle and presented it to the public with his renowned book “Refactoring: Improving the Design of Existing Code” published in 1999.

Martin Fowler’s talent lies in his strong ability to explain, and many terms defined by him have become buzzwords in the industry, and refactoring is one of them.

The term refactoring sounds much more advanced than “adjusting code.” Until today, many people use the term refactoring casually, saying things like, “This system is too messy, it needs to be refactored.”

Sadly, many programmers have misunderstood refactoring.

Refactoring is a Micro-operation #

What do you understand about refactoring? Let’s take the previous sentence as an example: This system is too messy and needs to be refactored. If we were to ask you how you plan to refactor it, some people would tell you that they plan to start from scratch and re-implement the system. I’m sorry, but what you plan to do is called rewriting, not refactoring.

“Refactoring” is a best-selling book, but from my understanding, very few people actually read it completely because Martin Fowler wrote it in the style of two books (Duplex Book). The first half explains the content, and the second half is a manual.

What really made this book famous is the first half, which explains the meaning of refactoring, while very few people read the refactoring manual in the second half. Many people think they understand refactoring just by reading the first half, so to them, refactoring is just code adjustments. There are many ways to adjust code, and rewriting is one of them.

If you really took the time to read the second half of this book, you will likely find it boring because each refactoring technique is very subtle, such as renaming variables, extracting methods, and so on. Especially these days, these techniques have become menus in IDEs. This is probably why many people put the book down and think that refactoring is nothing more than that.

Therefore, there are various misunderstandings about refactoring in the industry, mostly because people do not understand the meaning of these refactoring techniques.

Refactoring is essentially a practice of “micro-operations.” If you cannot understand the meaning of “micro-operations,” you naturally cannot understand the true meaning of refactoring, nor can you understand why rewriting, which involves major changes, is not within the scope of refactoring.

In my article “The Work Secrets of Master Programmers,” I once introduced you to “micro-operations.” Each step is small, so small that many people consider it insignificant.

Refactoring also belongs to the category of micro-operations, and when combined with the task decomposition we introduced, you can understand the meaning of those refactoring techniques very well: You need to adjust the code you are working on, break it down into several standalone “refactoring” actions, and then complete them step by step.

For example, if there is a common method in a service class that is not suitable to be within a class with business meaning, what would you do?

The drastic approach would definitely be to create a new common class and then copy the method over and fix all the compilation errors. Refactoring techniques would break it down like this:

  • Add a new common class to place this method in.
  • Add a field in the business class whose type is the newly added common class.
  • Move the instance method and move this method to the new class.

Thanks to the enhanced capabilities of modern IDEs, for the final step, you can press a shortcut key and it can help us complete the move and modify all the calls easily.

In these decomposed steps, each step can be completed quickly, and moreover, you can stop after each step. That’s the real meaning of a micro-operation. This is something that the drastic approach cannot achieve. When you’re fixing compilation errors, you don’t know how many places you need to modify and when it will all be over.

Of course, this is a very simple example, and it wouldn’t matter much if you made a drastic change. But in reality, for many slightly larger-scale changes, if they cannot be done by refactoring, you quickly lose track of what you have modified. This is also the biggest risk faced by many so-called “rewriting” projects - once started, they cannot be stopped.

Now that you understand, refactoring is not just a bunch of refactoring techniques, but more importantly, you need to have the ability to “break down the action of adjusting code into individual refactoring micro-actions.”

Refactoring the Map #

Below, I’m going to provide you with a knowledge map about refactoring, which will help you understand its relationship with many other related topics and assist you in better understanding refactoring.

To learn about refactoring, it is important to understand its definition. In this regard, Martin Fowler provides two definitions, one as a noun and one as a verb.

Refactoring (noun): A technique for adjusting the internal structure of software, without changing its observable behavior, to improve its understandability and reduce the cost of modification.

Refactoring (verb): The process of restructuring software using a series of techniques, without changing its observable behavior.

Understanding the definition of refactoring is crucial because the knowledge map of refactoring revolves around this definition.

Firstly, we need to adjust the internal structure of the software. The first question we must answer is why we need to do this. Martin Fowler’s answer to this question is “code smells.”

In my opinion, code smells are the most significant inspiration this book provides to the industry. Many people are often unable to detect code smells, so they allow the code to deteriorate, which is often demonstrated by the careless addition of if statements or flags.

I often recommend the book “Refactoring” to people, but I also frequently add that if you don’t have much time, read Chapter 3, “Code Smells.”

By the way, when comparing the two editions of “Refactoring,” you will find that they have some differences in the definition of code smells. In the new edition of “Refactoring,” mutable data and loops are defined as code smells. If you haven’t been keeping up with recent trends in programming, this kind of definition may be shocking. However, once you understand the trend of functional programming, it is not difficult to understand their origins.

In other words, functional programming has become mainstream. If you’re not familiar with it yet, make an effort to learn about it.

Let’s get back to the definition of refactoring. Refactoring is meant to not change the observable behavior of the software. The most common way to determine if the observable behavior has changed is through testing.

I have already discussed testing in the “Task Decomposition” module, so now you should have a better understanding of concepts such as refactoring and TDD and how they work together, right?

Furthermore, refactoring is meant to improve understandability. But to what extent does refactoring qualify as a breakthrough? When refactoring was being extensively discussed in the past, someone provided an answer: Refactoring to Patterns. Of course, this is also the title of a book, so if you’re interested, you can find it and give it a read.

Personally, I have a hypothesis. If this discussion could be extended to 2008 when Robert Martin’s “Clean Code” was published, perhaps someone would have proposed “refactoring to clean code.” So, I recommend you to learn about both design patterns and clean code.

So far, I have organized the surrounding knowledge of refactoring to allow you to see not only the individual trees but also the forest while learning refactoring. Of course, for the specific knowledge of refactoring, go read Martin Fowler’s book!

Recap #

Let’s summarize today’s content. Today, I introduced a concept that is well-known to everyone: refactoring. However, this concept is often misunderstood, and people often think that adjusting code is synonymous with refactoring.

Refactoring, essentially, is a series of small operations. The core of this practice, refactoring, is to break down the action of code adjustment into small individual actions. If you cannot understand this point, it will be difficult for you to understand the value of refactoring itself.

However, for readers of our column, the difficulty of understanding this concept should be reduced since you have already learned the “task decomposition” module.

Since the core of refactoring is also decomposition, it requires a lot of practice. Just like the task decomposition principle mentioned earlier, I have put a lot of effort into specific exercises in refactoring to be able to do it step by step. But shouldn’t a software craftsman with aspirations cultivate their basic skills in this way?

If there is one thing you remember from today’s content, please remember this: hone your refactoring skills.

Finally, I would like to ask you to share your understanding of refactoring. Feel free to write down your thoughts in the comments section.

Thank you for reading. If you find this article helpful, please feel free to share it with your friends.