A book that every programmer should read.
- Refactoring: A basic skill that is often overlooked
- Author: hengg
FundebugReproduced with authorization, copyright belongs to the original author.
In early May, a friend told me that Reconstruction was coming out in its second edition, and I was excited to order it. It took me a week to read it in one sitting before I had this review. I couldn’t agree more with douban user tianxin Yi’s comment:
Although this book is very popular, there are still too many people who should have read it and didn’t.
Confessions of an old reader
As a developer, I was writing Java when I first read this book in 2012; In the 2019 reprint of this book, I’m writing JavaScript. True to the old adage, “Any application that can be written in JavaScript will eventually be written in JavaScript.”
JavaScript is particularly well suited for refactoring because it is easy to write and not maintainable.
This is a joke, of course, as the authors actually explain: the philosophy and architecture behind refactoring applies to any programming language, but JavaScript is chosen because it is so widely used. You can write good code or bad code in any programming language, and you can refactor with the ideas and techniques in this book.
Using JavaScript to present code examples does not mean that the techniques presented in this book apply only to JavaScript.
Compared with the old and new editions, the author “reconstructs” the book: the first chapters are expanded, and the second chapters are more structured, removing the original 12-14 chapters. Overall, the refactored version 2 is more down to earth and appropriate for The Times: no more “big refactorings” and more focus on operational details.
“Instead of raising the bar, Mr. Fowler has made his kung fu even more solid.” — From translator’s Preface
Although the book’s subtitle is “Improving the Design of Existing Code,” after reading it, I found it to be a good guide on how to avoid “bad smells” when designing new systems.
Refactoring and Agile development are brothers
There is no mention of refactoring without agile development, and Martin Fowler himself is one of the founders of agile development. Agile as a “hot chicken” has a lot in common with refactoring.
One is that both of these can easily become the “sheep” of “dog meat”. In many cases, refactoring means taking the time to rewrite existing code that is almost unmaintainable, just as many “agile” practices “don’t resist requirements change” without actually responding to change. Second, they are both difficult to implement and their practices can be cross-cutting — they both focus on details rather than frameworks, both welcome change, and both emphasize small steps and continuous improvement. Thirdly, design and reconstruction are two important links in agile development, which complement each other and maintain strong adaptability in the process of practice.
Refactoring Techniques
It’s fair to say that most of the questions I encountered during refactoring were answered in this book.
Let’s look at the authors’ definition of refactoring:
Refactoring, n: An adjustment to the internal structure of software to improve its comprehensibility and reduce its modification costs without changing its observable behavior.
Refactoring (verb) : The use of a series of refactoring techniques to adjust the structure of software without changing its observable behavior.
Why, how, principles and methods of refactoring can be found in this book. Starting with Chapter 5, the author provides a 300-page list of refactorings and detailed techniques for more than 60 refactorings (the old version had more than 70 refactorings; the new version has removed refactorings for large-scale projects). I find this very detailed list of refactoring techniques more akin to a dictionary, which you can look up after a rough reading.
As for when to use this directory, the authors also discuss it in Chapter 3: When code has a “bad taste” you can start refactoring. I don’t think “bad taste” is an immutable formula, but rather an unquantifiable experience based on the team, the project, the technology stack adopted, and so on. Therefore, the author uses the experience of “taste” to refer to the place that needs to be reconstructed. For each of the “bad smells” listed by the authors, there is a refactoring technique. While the author’s list of over 20 “bad smells” covers a lot of ground, you and your team can still draw on your own experiences to guide refactoring. In fact, compared with the first version, the “bad taste” in the second version added “mysterious names”, “global data”, “looping statements” and deleted “imperfect library classes”.
In my opinion, the most important and overlooked chapter of this book is chapter 4, Building a testing architecture. In Chapter 4, the author builds a complete unit test architecture step by step through an example production plan. Obviously, there is a cost to mastering unit testing, which has led some developers (especially on the front end) to ignore unit testing altogether. They think testing is QA’s job and they just need to make sure the smoke test passes. Counterintuitively, however, good unit testing is not only a prerequisite for refactoring and a good aid, but also helps us organize our design ideas so that we can write good code. Because when we write unit tests, we assume we’re code breakers, thinking about how to break the code, looking for boundary conditions that might go wrong. Unit tests can be written and run after code is written or before code is written. The technique of writing unit tests before code is called test-driven development (TDD) and is one of the cornerstones of agile development. The author’s friend Kent Beck has written a book about the art of TDD, Test-Driven Development.
In the example in Chapter 1, the author says, “Take small steps so your code is always working.” And the author goes out of his way to emphasize that “whenever I do a refactoring, the first step is always the same: I want to make sure that the code I’m about to change has a solid set of tests.”
I have a little lesson to share with you about unit testing: ** Write as pure a function as possible. ** A pure function is a function that has no side effects and gives the same argument value. A pure function always returns the same result and does not depend on values other than the argument. Obviously, pure functions are easier to unit test.
Of course, unit testing is not a panacea, it can not detect all bugs, and the coverage of unit test set is also an indicator of opinion, the specific need to write how much unit test, how much code coverage, we need to combine the actual situation in the development of their own balance. In any case, unit testing has always been a very important and often overlooked skill.
In addition, I adhere to a “432” principle in the development practice, for your reference:
- A class should contain no more than 400 lines of comment code;
- A pure function should have no more than 30 lines;
- The loop inside the function is nested at most 2 levels.
The state of refactoring
Some friends do not support or even hate refactoring.
- Some developers don’t want to “waste” their efforts on refactoring
They see refactoring as “fixing the engine of an airplane in flight,” with lots of problems and little to show for it. Refactoring can “inadvertently” break functionality, cause a lot of trouble, cost out of proportion to the benefits, and is rarely the focus of an interview, making it a thankless effort.
- Many leaders oppose blind refactoring
For reasons that need not be explained, there is little demand for refactoring in startups; In large organizations, leaders don’t always like refactoring, because spending time refactoring takes time away from developing new features, and refactoring when the code still runs, or even looks good, is gilding the gild; The benefits of refactoring are less compelling than the risks.
- Most QA is cautiously skeptical of refactoring
Changes in code mean regression testing, and in the agile era, where QA is focused on new features in each iteration, there is limited energy available for regression testing, and refactoring after a test is passed can make the change opaque to QA, potentially increasing the risk of going live.
I think the above anti-refactoring scenarios are the result of inappropriate refactoring.
People are just getting more comfortable with the word “refactoring” because it sounds good, it has a sense of being positive about change, but what you’re really doing is just doing the same, random modification as before.
In practice, refactoring is demanding: it requires detailed unit testing, an environment for continuous integration, and improvements in “small steps to get the code working forever.” It is precisely because many project “refactoring” is carried out without meeting the above conditions, without cost estimation and strategic planning, that it is easy to fail.
- The water
In fact, there are a few developers who recognize refactoring as an effective way to improve the quality of their code, a reflection of the “work hard now so you don’t have to work harder later.” However, to some extent, this is not applicable in the current 996. Icu environment. On this point can only see eye to eye, oneself measure.
There is no silver bullet
Finally, I want to say: there is no silver bullet.
Refactoring, like design patterns, is a distillation of best practices, a collection of techniques, not a panacea. If you are an ambitious programmer who has never systematically learned about refactoring (although I don’t believe there are any such programmers), you will find that you have inadvertently used all of the refactoring techniques mentioned in this book in your daily work.
Reconstruction is the art of practice. To understand the concept without practice is to knead the sand and make rice in vain. Trying to use it as a “one-size-fits-all” solution to all problems will only lead to the trap of inappropriate refactoring, which will ultimately outweigh the benefits. Only in the appropriate scene under the appropriate practice, will realize its due value.