Update legacy code step by step with the Mikado method

The lightweight Mikado method opens up a structured way to make significant changes even to complex legacy code.

listen Print view
Old-fashioned half-timbered house

(Image: erstellt mit Midjourney durch iX)

17 min. read
By
  • Falk Sippach
Contents

Legacy systems exude a certain fascination because they offer their own unique, exciting challenges. These can be much more interesting than assembling modern frameworks into an application from scratch. However, developers are often reluctant to make changes to legacy systems because it's not always clear what might break.

Falk Sippach
Falk Sippach

Falk Sippach is a software architect, consultant, and trainer at embarc Software Consulting GmbH. He is actively involved in the Java community and shares his knowledge in articles, blog posts, and lectures.

Many legacy systems are technologically over ten years old and thus no longer up to date. It's difficult to find suitable employees for maintenance and further development. Regular updates of the libraries and frameworks used unfortunately help only a little. The source code, which has passed through so many hands over the years, doesn't get any better as a result.

The biggest difficulties lie in the lack of knowledge about the internal structure, the decisions made at the beginning of the project, and the unclear consequences of changes. Unfortunately, most developers in training or university often only learn how to create new systems, not how to maintain or further develop existing software.

Refactoring is a means to improve structure and design. And in complex environments, the Mikado method offers additional support. In the words of Ola Ellnestam and Daniel Brolund, who invented the method: “The Mikado Method is a structured way to make significant changes to complex code… for changes to a system that is too large to analyze first and then work on. This fundamentally applies to every productive system in the world” (Ola Ellnestam, Daniel Brolund: The Mikado Method; Manning 2014).

Videos by heise

When editing a legacy code base, developers can sometimes break a sweat. Especially when changes get out of control and after several hours of work, they find themselves back at square one. It's easier if there's extensive documentation or at least automated tests. But often, the entire truth is only in the source code, and the development team searches in vain for tests.

This is where the Mikado method provides support thanks to its structured approach. Refactoring complex code is similar to rearranging furniture in a home. If the old upholstered suite cannot be easily replaced by the newly purchased three-seater sofa due to space constraints (the door would be blocked), then major renovation work is required (see Figure 1).

Rearranging furniture often presents similar problems to refactoring software (Fig. 1).

The dining table has to make way for the new sofa. It is to be moved to the place of the old upholstered suite, which must first be removed. Just like in programming, one can quickly get lost in chaos here. If the residents simply put the new sofa in the room, there might not be enough space to move the furniture around.

Therefore, the steps are first thought through theoretically, and the obstacles (e.g., when moving the dining table) are recorded. In the next step, the residents will try to eliminate the problem they just discovered. However, this often involves further obstacles, such as the old upholstered suite in this case. This must first be removed from the apartment. Then the dining table can be moved, and finally, the new sofa can finally be placed in the planned spot.

Refactoring is a tool for improving software design. Over twenty years ago, Martin Fowler defined it in his book as follows: “Refactoring (noun): A change to the internal structure of a software system in order to improve the understanding of the system without changing its external behavior” (Martin Fowler; Refactoring: Improving the Design of Existing Code; Addison Wesley 2019).

Anyone who wants to restructure source code will ultimately apply a series of refactorings. These are so-called behavior-preserving code transformations: the functional logic does not change. The goal is a more understandable internal structure that facilitates future changes and increases testability. For refactoring to work well, it needs some allies.

These include fast compile and build times, existing automated unit or integration tests, and version control to easily undo changes at any time. Furthermore, the use of tools can support safe and rapid execution. Many modern development environments like Eclipse or IntelliJ come with a comfortable toolset.

Through refactoring, developers secure existing investments in their software, prevent design decay, and increase readability, modifiability, and testability. They can find well-hidden bugs if necessary, transfer knowledge through active learning on the code, and they also help subsequent developers, who are often forgotten users of the source code. True to the motto of Martin Golding: “Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.”

The working method is based on two fundamental principles: first, not to break the program code, and second, to work on only one thing at a time. Therefore, a lot of self-discipline is required during execution. Developers should always take small steps and then compile the code and run the tests. Then it starts all over again.

Kent Beck's metaphor of the two hats is also important. You can only wear one hat at a time. Therefore, during refactoring, no functional code should be changed, meaning no new features should be added or bugs fixed.

Refactorings can be applied on various occasions: for example, regularly for half an hour a day to keep the code healthy. Or when developers don't (or no longer) understand the source code and want to actively learn it by reading it. Refactoring is also useful for planned changes, such as before extensions or bug fixes. Or simply during a code review when inconsistencies in the code are noticed. True to the motto of the Boyscout Rule by Robert C. Martin: “Leaving your campsite cleaner than when you found it.” (Kevlin Henney: 97 Things Every Programmer Should Know; O'Reilly 2010)

Developers will never really be finished, by the way. Every refactoring may have a counter-refactoring. Because there are no real dead ends, and one can continue indefinitely. Therefore, it makes sense to set boundaries. Developers could stop when the code to be analyzed is understood, a set time limit has been reached, or the boss or client comes around the corner with another task.

Refactoring seems quite simple, yet not entirely so. The process – taking the smallest possible steps and constantly compiling or running tests – is quickly forgotten. What works well in a manageable context becomes significantly more difficult with complex software systems. According to the Mikado method, blocking elements can be quickly identified and analyzed. However, these obstacles are initially only noted and then removed step by step after the entire analysis. In the words of Ellnestam and Brolund: “The Mikado Method is a framework that helps you visualize, plan, and perform business value-focused refactorings over iterations and increments. ” (Ola Ellnestam, Daniel Brolund: The Mikado Method; Manning 2014).

The procedure is relatively simple (see Figure 2). First, the developers define the goal (1). This is the starting point for the upcoming changes and at the same time the success criterion for the end. Then, the team begins to try out initial solutions (2). During these experiments, they can actively change the code to test hypotheses and observe their effects. The testers visualize obstacles transparently for everyone in the Mikado graph (3). They then immediately undo the changes introduced by the experiments (4), for example, using git reset. Then, the process continues at step 2 and repeats until no more obstacles are in the way.

Figure 2 shows the overall process of the Mikado method. Through the recurring cycle of trying, visualizing, and undoing (2), the Mikado graph is built up piece by piece. This graph contains a lot of valuable information about the structure of the system. Its creation can be interrupted at any time and continued later. The code base is always in a runnable state, as all experiments performed are undone after documentation. As soon as all obstacles on the way to the defined goal have been analyzed, the graph is unwound in reverse. All documented changes are implemented in sequence (3) until the original goal is reached (4).

Overall process: After the cycles are completed, the team implements all changes in reverse order. The right, colorful part of the graphic shows the following example (Fig. 2).

Don't miss any news – follow us on Facebook, LinkedIn or Mastodon.

This article was originally published in German. It was translated with technical assistance and editorially reviewed before publication.