This article is a revised version. It was previously published at refactoring-legacy-code.net published.
This is a series of articles. You can find the second article here:
When it comes to legacy code, many developers feel uneasy. Starting a project "on a greenfield site" is much more fun than being in the swamp of the "brownfield". The lack of automated tests is one problem. But a much bigger challenge is that even with tests, there is no clear idea of how to tackle complex refactorings.
Too often, developers have experienced that after days or weeks of trying to bring the code back under their control, nothing has improved in the end. The structures are too complex, methods and classes too long, nobody can see through them. When in doubt, the big refactoring, which was supposed to finally improve everything, is aborted for fear that nothing will work as it should in the end. Unfortunately, it is often overlooked that even simple refactorings contribute to making the code more comprehensible and therefore more changeable. Read my articles on this topic to simple refactorings.
Reasons for refactorings
There must always be a good reason for complex refactoring on a legacy project. Clean code is not an end in itself. There are only two possible reasons:
- Either a new Feature be supplemented or an existing one modified
- or there must be a Error be remedied.
Your sponsor will show little willingness to invest in complex refactorings without a return. It is even more dramatic: the client assumes that you have produced changeable code from the outset. Just as you assume when you buy margarine that it won't kill you. Or do you explicitly point out to the cashier at the supermarket that you want margarine that won't kill you? Certainly not. Nor do our clients explicitly tell us that they expect automated testing and changeable code. They expect it implicitly because it is the state of the art. So if your code is not currently changeable, then you haven't done your homework. Unfortunately, I have to put it so drastically. You are responsible for paying back the debt you have incurred. This is only economically viable in the context of changes that are due anyway.
Chaos
If there is a real reason for refactoring, the code can begin to be changed. This typically proceeds as shown in the following series of images.
(7 - 9) And so it goes on until no new problems occur.
This may not seem so tragic. But such a process often takes weeks. Therefore, these changes are usually made on a Branch version control. This means that the refactoring measures do not interfere with further development, which is simultaneously based on the Trunk or another Branch is carried out.
And as if the refactoring itself wasn't already complex enough, the subsequent Merge of all the changes posed new challenges: In the meantime, the Trunk is also further developed, so that merging all the changes is very time-consuming. Individual refactoring steps are often carried out again because this seems simpler than a Merge. No wonder that developers prefer to keep their hands off such refactorings.
The Mikado method
The two authors Ola Ellnestam and Daniel Brolund have in their book "The Mikado Method" presented a simple yet brilliant idea for dealing with complex refactorings. With the help of the Mikado method complex refactorings lose their terror. See the procedure using the same example.
(11 - 20) At the end of these steps, the Mikado Graph provides a visualization of the procedure.
The following illustration shows the Mikado Graph for the series of images.
The result of the experiments
While attempts were made to implement the individual changes naively, a Mikado graph was created that breaks down the complex refactoring into small steps. These insights cannot usually be gained through analysis. Many details are overlooked in an analysis that consists of looking at the code. These details only become apparent when an attempt is actually made to implement the changes.
The refactoring of legacy code is a complex Problem. There is also complicated Things. About complicated I think about problems for longer and eventually find a solution. By thinking about it, I can be sure that it really is a solution. Math problems can be complicated. By thinking and puzzling for a long time, I find a solution and am then sure that it is indeed a solution.
On the other hand, there are also complex Problems. These are characterized by the fact that no matter how long you think about it, you cannot be sure whether the idea for a solution is actually a solution. Problems in a partnership are often complex. You think about a solution. But in the end, you can only be sure that it is actually a solution by experimenting. You simply try out the solution and find out whether your idea works.
It is similar with complex refactorings. You cannot usually find out whether your idea is actually a solution by analyzing and thinking about it. Only an experiment leads you to all the details or shows that you have found a solution. The Mikado method relies on many small experiments. Only by actually trying out the code do the many small, ugly details become apparent.
Another advantage of the Mikado Graph is the visualization. With such a graph, it is much easier to discuss within the team who will take on which tasks, when the individual steps will be carried out, etc. Furthermore, the formerly large refactoring, which could only be considered as "all or nothing", can now be carried out over a longer period of time. The Mikado Graph makes it possible to implement the leaves of the graph step by step. This makes it possible to plan the individual refactoring steps in concrete terms and dovetail them with the further development of the system.
Implementation of complex refactoring
Once the Mikado Graph has been created, the individual challenges are tackled. We now work in the other direction, from the outside in. The individual challenges are solved and transferred to version control. Note that these changes are made on the Trunk as they are of short duration. A branch is not required because the changes do not take long and the result should potentially be deliverable.
The Mikado graph is a directed graph. At the root is the Mikado goal that is to be achieved by the individual refactoring measures. This can be something like "Save data in the cloud instead of locally" or "Fix errors when creating a new product". The individual naive implementation attempts have resulted in the Mikado Graph as a dependency graph. The implementation now takes place in the opposite direction of the dependencies, from the leaves to the root.
The next article explains the Mikado method procedure in detail.
The right seminar on refactoring: Clean Code Developer Refactoring.