preface

The word “refactoring” is familiar to most engineers. The reality is that most people just “hear more” and do less. Not many people actually refactor code, and even fewer people refactor it continuously as part of their development.

On the one hand, refactoring code requires a lot more of an engineer’s ability than just writing code. Refactoring requires you to be able to identify bad smells or design flaws in your code, and to use theoretical knowledge of design ideas, principles, patterns, and programming norms to solve these problems.

Why, on the other hand, many engineers to refactor, what refactoring, when and how to reconstruct the related problems, such as understanding is not deep, to reconstruct no systematic, overall understanding, in the face of a pile of bad code, without the guidance of a reconstruction techniques, change can only think of what to what, can not comprehensively improve the code quality.

Refactoring mainly includes the following aspects:

A general introduction to refactoring, including the purpose (why), object (what), time (when), method (how) of refactoring; Means of ensuring refactoring is error-free, with a focus on unit testing and code testability; Refactoring at different scales, focusing on large, high-level refactoring (such as system, module, code structure, class-to-class interactions, etc.) and small, low-level refactoring (classes, functions, variables, etc.). The purpose of refactoring: Why? How does the Master describe refactoring? Software design guru Martin Fowler defines refactoring this way: “Refactoring is an improvement to the internal structure of software in order to make it easier to understand and cheaper to modify without changing the visible behavior of the software.”

In fact, when it comes to refactoring, many books cite this definition. There is one important point in this definition that is worth emphasizing: “Refactoring does not change the external visible behavior.” We can think of refactoring as the use of design ideas, principles, patterns, programming norms and other theories to optimize code, correct design deficiencies, and improve code quality while maintaining the same functionality.

With a brief look at the definition of refactoring, let’s focus on why you want to refactor your code.

First, refactoring is an extremely effective way to ensure code quality at all times, without corrupting code to the point where it becomes irredeemable. The project evolves and the code keeps piling up. Without human responsibility for the quality of the code, the code will always evolve in an increasingly chaotic direction. When the chaos reaches a certain level, quantitative change leads to qualitative change, and the maintenance cost of the project is higher than the cost of redeveloping a new set of code, and no one can do it again.

Second, great code or architecture is not designed from the start, just as great companies and products are built iteratively. As the system evolves, code refactoring is inevitable because we cannot meet 100% of the future requirements, nor do we have the energy, time, or resources to pay for them in the distant future.

Finally, refactoring is an effective way to avoid overdesigning. In the process of maintaining the code, when we really encounter problems, we can reconstruct the code, which can effectively avoid investing too much time in the early stage to do excessive design, and achieve a targeted goal.

In addition, refactoring is important for the growth of an engineer’s own skills.

From the definition of refactoring, refactoring is actually an application of the classic design ideas, design principles, design patterns, and programming norms that we have learned. Refactoring is actually a good way to put this theoretical knowledge into practice, to train our ability to use it skillfully. On top of that, you might not feel like you’re growing much when you’re building business logic, but you’ll feel a sense of accomplishment when you’re reconstructing a bad piece of code into a better piece of code.

In addition, the ability to refactor is a good measure of an engineer’s ability to code. “Junior engineers are maintaining the code, senior engineers are designing the code, and senior engineers are refactoring the code.” This means that junior engineers are fixing bugs, modifying and adding functional code within the existing code framework. Senior engineers design the code structure and build the code framework from scratch; The senior engineer is responsible for the quality of the code, and needs to find the problems existing in the code, refactor the code, and ensure the quality of the code in a controllable state at all times.

Refactoring objects: What exactly are you refactoring?

Based on the scale of refactoring, we can categorize large-scale, high-level refactoring (hereafter referred to as “large refactoring”) and small-scale, low-level refactoring (hereafter referred to as “small refactoring”).

Large-scale refactoring refers to the reconfiguration of top-level code design, including system, module, code structure, and the relationship between classes, etc. The methods of reconfiguration include layering, modularization, decoupling, abstracting reusable components and so on. The tools for this kind of refactoring are the design ideas, principles, and patterns that we learned about. This kind of refactoring involves a lot of code changes and has a large impact, so it will be difficult, time-consuming and the risk of introducing bugs will be relatively large.

Minor refactoring refers to the refactoring of code details, mainly at the code level of classes, functions, variables, etc., such as canonical naming, canonical comments, elimination of large classes or functions, extraction of duplicate code, etc. Minor refactoring takes more advantage of the coding conventions we’ll talk about later. This kind of refactoring is concentrated, simple, operable, time consuming short, and the risk of introducing bugs is relatively small. You just need to be familiar with the various coding specifications, you can do it.

Timing of refactoring: When?

Now that we know why we’re refactoring and what we’re refactoring, let’s take a look. When do we refactor? Is the code bad enough to refactor? Of course not. Because when the code is really bad to the point of “low development efficiency, hiring a lot of people, working overtime every day, but not much work, frequent online bugs, angry leaders, middle management is helpless, engineers complain constantly, it is difficult to find bugs”, basically refactoring can not solve the problem.

I personally oppose, usually do not pay attention to the quality of the code, pile up bad code, really can not maintain the drastic refactoring, or even rewrite the behavior. Sometimes there is too much code for a project to refactor thoroughly, and you end up with a “weird monster”, which is even more troublesome! Therefore, it is not realistic to expect centralized refactoring to solve all problems after a certain amount of code is rotten. We must explore a sustainable, evolutionary approach.

So, the refactoring strategy I particularly advocate is continuous refactoring. When you have nothing to do, take a look at the code in your project that isn’t well written and can be optimized, and take the initiative to refactor it. Or, when you modify or add a feature code, you can easily refactor a design that doesn’t conform to the code specification. To sum up, just as unit testing and Code Review are part of development, it would be better for the project and for us to make continuous refactoring a part of development as a habit.

Although we say the ability to refactor is important, the awareness of continuous refactoring is even more important. We need to put code quality and refactoring in perspective. Technology is changing, requirements are changing, people are moving, code quality is always declining, code is always imperfect, and refactoring is always going to happen. Keep refactoring in mind at all times to avoid overdesigning early in development and the degradation of code quality during maintenance. The people who yell at someone else for having a flaw in their code, or spend all their time trying to come up with a perfect design, are often the ones who don’t have the right sense of code quality and don’t have the right sense of constant refactoring.

How to Refactor: How to Refactor?

As we mentioned earlier, refactoring can be broadly divided into large and small refactoring based on the size of the refactoring. We should treat these two types of refactoring differently.

For large refactoring, there will be a lot of modules and codes involved. If the project code quality is poor and the coupling is serious, it will often affect the whole body. Once you think that you can complete the refactoring in a day, you will find that more and more changes, more chaos, not a week or two can be done. New business development clashed with refactoring, and eventually revert fell by the wayside, dropping all of its changes and frustrated to pile up bad code again.

When making a large refactoring, we should do a good refactoring plan in advance, methodically and in stages to proceed. Each phase refactores a small portion of the code, commits, tests, runs, finds no problems, and then moves on to the next phase of refactoring to keep the code in the code repository in a runnable, logically correct state. At each stage, we controlled the scope of the code affected by the refactoring, thought about how to accommodate old code logic, and wrote compatible transitional code if necessary. Only in this way can we keep each stage of refactoring from taking too long (ideally in a day) to conflict with the development of new features.

Large-scale, high-level refactoring must be organized, planned, and carefully led by senior colleagues who are experienced and familiar with the business. On the other hand, small, low-level refactoring can be done whenever you want and have the time, because the scope is small and the change time is short. In fact, in addition to finding low-level quality problems manually, there are a number of mature static code analysis tools (such as CheckStyle, FindBugs, PMD) that can be used to automatically find problems in your code and then refactor and optimize them.

For refactoring, senior engineers and project leaders should take the responsibility to refactor the code and ensure that the code quality is in a good state at all times. Otherwise, once the “broken window effect” occurs, one person will pile in some bad code, and then more people will pile in even worse code. After all, the cost of piling bad code on a project is too low. However, the best way to maintain code quality is to create a technical climate that motivates people to pay attention to code quality and continue refactoring.

The key to review

Today’s presentation will help you get a proper, holistic view of refactoring and build awareness for continuous refactoring. This is probably more important than teaching you some refactoring skills, because a lot of technical problems aren’t solved by technology alone. It’s more important to have the awareness and awareness.

1. Purpose of Refactoring: Why?

For a project, refactoring keeps code quality in a constant state of control before it becomes irredeemably corrupt. For individuals, refactoring is a great exercise in one’s coding ability and a very fulfilling thing. It is the training ground for us to learn the classical design ideas, principles, patterns, programming norms and other theoretical knowledge.

2. Refactoring objects: What?

Based on the scale of refactoring, we can roughly divide refactoring into large-scale high-level refactoring and small-scale low-level refactoring. Large-scale, high-level refactoring includes layering code, modularization, decoupling, sorting out interactions between classes, abstracting reusable components, and so on. This part of the work is more use of more abstract, more top-level design ideas, principles, patterns. Small and low-level refactoring includes standard naming, annotation, correction of too many function parameters, elimination of super large classes, extraction of duplicate code and other programming details, mainly for class and function level refactoring. Smaller, lower-level refactoring takes more of the theoretical knowledge of coding specifications.

3. Timing of refactoring: When?

I’ve repeatedly said that we need to build a sense of continuous refactoring and make it an integral part of our daily development, rather than waiting until there’s a big problem with the code.

4. Method of refactoring: How?

Large-scale and high-level refactoring is more difficult, and it needs to be organized and planned, with small steps and quick runs in stages, so that the code is in a runnable state at all times. On the other hand, small, low-level refactoring can be done anywhere and anytime you want because of its small scope and short change time.

A thorough guide to Java refactoring