Introduction to refactoring
Refactoring is not changing the code.
It is a lot more than that. Two immediate questions should come to mind:
- How do you guarantee that behaviour does not change?
- What is the point of changing the code if the behaviour doesn’t change?
Refactoring is the process of taking existing base code and improving it. While it makes the code more readable and understandable, it also becomes much easier to add new features, build larger applications, and spot and fix bugs. The key point of refactoring is to combat technical debt. It transforms a mess into a clean code and simple design. Some benefits with refactoring are that it can decrease complicity, fixing bugs, improve maintainability, create the code design modern and easier to work with.
The best approach is to “refactor” in a series of small behaviour-preserving transformations, each of which “too small to be worth doing”. But, the cumulative effect of each of these transformations is quite significant. By doing them in small steps the risk of introducing errors could be reduced. You also avoid introducing new bugs once refactoring/redesigning the code.
Pay attention to this part in the development process. If you don’t take care of the code and clean it, it may cause that technical debt will rise quick. Thus, try to set up ahead for refactoring or you will receive what is called for “bad code smells”.
Some of the clean code features:
- Clean code is easier and cheaper to maintain!
- Clean code passes all tests
Your code is dirty if it passes only 95% of tests. When your test coverage is zero, you failed automatically. And it is not about super sophisticated algorithms.
- Clean code is evident to other computer specialists
It is not about some super complex algorithms. Poor variable naming, bloated classes and methods – all this blurs the evidence of the code.
- Clean code does not include duplication
If you have to change a piece of code with duplication, you will need to remember to make the same changes in other places where the code repeats. It will all slow down and interfere with the progress.
- Clean code contains a minimum of classes and other moving parts
The smaller the code, the less it needs to be kept in mind.
All people initially try to write clean code. There is hardly a programmer who intentionally produces dirty code to the detriment of the project. But then why does clean code ever get dirty?
Ward Cunningham originally introduced the term of “technical debt” in regards to unclean code.
Getting a bank loan, you can speed up some purchases. However, you will have to return not only the principal amount of the loan but also the additional interest that will run in the time until you repay the loan. Also, you can take several credits at the same time. Moreover, you can collect so many loans that the amount of interest outweighs your total income and makes full repayment impossible.
The same happens with the code. Today you temporarily accelerate without writing tests for a new feature. But every day, while this feature has to be tested by hand, it slows down your overall progress. At some point, the amount of this time will exceed the one you would have spent on the initial writing of the test.
Technical debt reasons
- Lack of understanding of the implications of technical debt
It will appear when a business does not understand that technical debt “charges interest” in the form of a slowdown in development as debt accumulates. Because of this, it is too difficult to allocate time for the team to refactor, as the management does not see this as value.
- Business pressure
It will appear when a business makes features roll out before they are fully completed. In this case, patches and “crutches” appear in the code, which hides the unfinished parts of the project.
- Lack of struggle with the strict connectivity of components
This is when the project resembles a monolith and not the connection between individual modules. In this case, any changes to one part of the project affect others. Team development is difficult, as it is difficult to isolate areas of work of individuals.
- Lack of documentation
Missing or outdated documentation slows down the introduction of new people into the project. Such a project risks completely stalling if key employees leave.
- Lack of auto-test
Lack of immediate feedback encourages quick but risky fixes and crutches, sometimes right on production. The effects of this are catastrophic. For example, an innocent hotfix sends a test email to the entire customer database or deletes real customer data in the database.
- Lack of interaction between team members
When the knowledge base does not spread across the organization, people work with an outdated understanding of the processes and details of the project. The situation is exacerbated when younger developers are incorrectly trained by their mentors.
- Lack of competence
When a developer simply does not know how to write quality code.
- Long-term simultaneous development in several branches
May cause accumulation of technical debt that needs to be replenished when merging changes together. The more changes that are made isolated, the more final technical debt.
- Lack of control over compliance with standards
Each project participant writes the code in the way he considers correct (as he wrote on the past project). As a result, the project code turns into a salad of coding styles, making it difficult to understand the code for all team members.
- Delayed refactoring
Project requirements are constantly changing and at a certain point, it may become obvious that parts of the code are outdated, become cumbersome and need to be reworked to fit new requirements. On the other hand, project programmers write new code every day that works with obsolete parts. Therefore, the longer the refactoring is delayed, the more dependent code you will have to shovel in the future.
What Is Refactoring and Isn’t
It is very important to distinguish between refactoring and other identical processes. Here is a list of things that are not refactoring. Instead, they create new code and features:
- Creating an app/program from scratch
- Rebuilding an existing app/program in a new framework
- Addressing a user by first and last name instead of the first name
- Adding square root functionality to a calculator application
- Adding a new package to an application or program
- Optimizing performance
- Converting code to use a different interface
There are three things that are not refactoring.
First, refactoring is not debugging. Your code already needs to work. It is not a way to find either large showstopper bugs, you should have fixed those as soon as they happened. There are profilers, debuggers, test suites, refactoring is not one of those ways.
Second, refactoring is not performance. This is another very typical misinterpretation. We are not going to refactor to improve our code. The code needs to be cleaned up and as a result to become faster.
Third, refactoring is not adding features. If during this process you add a new feature, you support and you use case, you add a single tiny menu item to an application, you are no longer refactoring. Because in refactoring we do not change the behaviour of the code.
When to use refactoring
Rule of Three:
- When adding a feature
- When fixing a bug
- During a code review
When adding a feature
Refactoring helps to understand someone else’s code. If the code to which you want to add a new feature is not clear enough, refactoring allows you to make it more obvious for you and for someone who will work with it in the future. Refactoring makes it easier to write new code. After refactoring, adding a new feature is much smoother and takes less time.
When fixing a bug
Try to clean up the code and bugs will be found automatically. In addition, you do not have to create special tasks for refactoring that managers do not like to see in reports.
During a code review
When you make a review of a new feature, this is probably the last chance to clean the code before it is publicly available. It is best to conduct such a review with the author of the code. In this case, you will propose to the author changes, and then decide together how difficult it is to make a change. In this case, small changes can be carried out very quickly.
Checklist for the right refactoring
- The code should be cleaner
If after refactoring the code remains the same dirty, it may happen when you move away from refactoring with small changes and mix a bunch of refactorings into one big change. But it also happens when working with especially running code. Whatever you improve, the code as a whole still remains ugly. In this case, you can improve the code by completely rewriting some parts of it. But remember that before that you should have written tests, and also planned a fair chunk of time.
- All existing tests must pass successfully
There are two cases when tests fail after refactoring:
- You made a mistake when changing the code.
- The tests are low-level. Most often this happens due to the fact that your tests check the work of private methods of classes. Such a situation can be generally avoided by creating tests in the style of BDD.
- In the process of refactoring, new functionality is not created
Do not mix refactoring and direct development of new features. Try to separate these processes, at least within separate commits.
Usually, not only managers but also programmers tend to refactor purely from some perfectionist fixation on aesthetics. Technical experts want the code should be perfect and beautiful. Sounds familiar? But there is no actual point in that. The refactoring must always be done for a reason. It is not just adding features, refactoring is not finding bugs, refactoring is not about performance, however, it is often done simply because programmers want to do those things. Refactoring allows to ensure that new features could be developed within a reasonable time and to limit regressions by improving the design. It allows developers to (re)take pleasure to make the product evolve. So it is essential in a project and should be done regularly.
In conclusion, we should like to say that you always remember the golden rule that tells us we should always leave the code in a better condition than when we found it. This rule will improve the overall quality of the code and to the reversal of the technical debt.