Skip to main content

Your First Mikado Graph

Posted by manning_pubs on May 15, 2013 at 12:54 PM PDT



Your First Mikado Graph

by Ola Ellnestam and Daniel Brolund, authors of The Mikado Method

The Mikado Method is a structured way to make significant changes to complex code. What that means, is rather than getting caught up in the complexity of moving parts, analyzing the entire codebase and doing guess work, the Mikado Method removes the least amount of obstacles at a time in order to achieve real results without breaking code. In this article, based on chapter 2 of The Mikado Method, the authors give you a preview of what the Mikado Method can do. Save 42% on The Mikado Method with Promotional Code miklaunchjn only at manning.com.

In this article, we are going to look at a small part of a larger codebase. We will take a look at the Mikado process diagram and apply it to the example, step by step.

Figure 1 The Mikado Method Process

For this example, we want to change the way the application is launched. Right now, the application uses a hardcoded path to a user data file. What that means is that it makes the system inflexible, which means we can't change it when we need to test our application for instance. So we want to change how the application is launched and make it more flexible. Before we do this, let's look at what our launcher looks like right now.

There is a shell script that launches the application and it looks like this:

#!/bin/sh
java -cp app.jar com.ssem.app.Launcher

The shell script launches the Java class below:

Listing 1 Launcher.java

public class Launcher {

    public static void main(String[] argv) {
        try { #1
            App.setStorageFile("/opt/local/app/db.txt");
            App app = new App();
            app.launch();
        } catch (ApplicationException e) {
            System.err.println("Could not start application");
            e.printStackTrace();
        }
    }
}

#1 The location of the file is hardcoded.

So the above is what the launcher looks like now, but we want to avoid the hardcoded path. To get this result without breaking the code, we are going to use the Mikado Method.

Implementing changes without breaking the code

Our goal is to change the way the application is launched. Maybe you have seen this kind of situation before and know that there more than one way to solve the problem. You could configure the application via settings that are read from a file or you could pass a parameter to the program in runtime or something else. We try to avoid overanalyzing any change, we just try an idea and see what happens.

When we have several ideas we go for the simplest first. So, what is the goal? We choose to use the command line arguments passed to the main method.

Listing 2 Launcher.java

public class Launcher {

  public static void main(String[] argv) {
    try { #1

      App.setStorageFile(argv[0]);

      App app = new App();
      app.launch();
    } catch (ApplicationException e) {
      System.err.println("Could not start application");
      e.printStackTrace();
    }
  }
}

#1 The only thing we want to change do is provide the storage file as an argument.

This is the starting point of the Mikado Method. Let's put it in a Mikado Graph.

Drawing the goal


Figure 2 Drawing the Mikado Goal will help you focus on the task at hand.

Our goal is to use command line arguments argv in Launcher.java to configure database file. In the Mikado Graph it looks like this:


Figure 3 A clearly written goal makes it easier to know when done.

Implementing the goal naïvely


Figure 4 Trying things often give more feedback than hours of analysis.

We have a clearly stated goal, and since it is possible to naïvely implement the goal, we just do it.

Listing 3 Launcher.java with parameterized setStorageFile

public class Launcher {

    public static void main(String[] argv) {
        try {  #1

            App.setStorageFile(argv[0]);

            App app = new App();
            app.launch();
        } catch (ApplicationException e) {
            System.err.println("Could not start application");
            e.printStackTrace();
        }
    }
}

#1 Here we have done change we proposed earlier. The file is configured via an argument and then the Application uses that file.

Finding any errors


Figure 5 The compiler, tests, or running the system will help you find the errors faster.

Now we try to find any errors. One of the fastest ways to find errors is to execute the application. Launching our run.sh will result in an error:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
  at com.ssem.app.Launcher.main(Launcher.java:17)

The error is an ArrayIndexOutOfBoundsException, meaning that the String array argv does not contain even a single element, and especially not our database filepath. This example is pretty small, which means figuring this out beforehand isn't that hard, but when you get into more complex examples, executing parts or all of the program is a good way to find errors.

Thinking up immediate solutions


Figure 6 Don't think too hard about the consequences, just pick a solution that will pull the system in a good direction.

Now we need to come up with a solution that will prevent the previous error to occur. The first thing that springs into mind is editing our shell script, run.sh, and add a filepath there.

Drawing the solution as a prerequisite


Figure 7 Add the solutions to the graph as you come up with them.

We go to our piece of paper and add our solution as a prerequisite, "Add file path to arguments in run.sh." At this stage, it is easy to succumb to the temptation to just hack the next step from this broken state, and then the next, and the next; but, we won't do that, we just add the next step to the graph. Remember that what the Mikado Method helps you do is fix what needs to be changed without breaking the codebase, where if you were to hack the next step you are building on assumptions and guesswork.


Figure 8 Prerequisite added

Reverting the code


Figure 9 The most important, and yet unintuitive, step of the process: fix the broken system by reverting

Before we go any further we revert our changes because we don't know how much of the application we have affected. Remember, we don't want to build on assumptions. In this small example it was only one line of code and we can see the impact of our change. For a larger, or more complex, change, stacking uncontrolled changes on top of each other is a big no-no. After our revert, the only visible trace from our change is some ink on a piece of paper.

Selecting the next leaf prerequisite to work with


Figure 10 Zoom out and look at the graph. What is the next sensible leaf to work on?

After our revert, we now take a look at our graph where we can see that our only leaf is the update of run.sh. Remember the leaf is the only place where a change can be performed without possibly breaking code. The Mikado Goal isn't a leaf because it depends on "Add filepath...", but "Add filepath..." is a leaf since it has no further prerequisites. When implemented you can see the slight change to run.sh below:

#!/bin/sh
java -cp app.jar com.ssem.app.Launcher '/opt/local/app/db.txt'

After adding the filepath to run.sh we make sure that the application still works, and it does!

Does the change make sense?


Figure 11 Checking in cohesive changes will make your coworkers much happier.

The small change didn't actually do anything to our app, the added parameter is not used in the application, so the change just prepared us for the next step. In that way it doesn't make sense. On the contrary, adding an unused argument to a command will probably confuse anyone reading that piece of code. We have to add more to the solution before we check in, and we should pick the next leaf prerequisite to work on. Since the "Add file path..." is implemented, the only leaf now is the actual Mikado Goal, and once again we make the naïve change we initially tried. We compile and run the application with run.sh again to find any errors. This time it all works, and we are satisfied with our change, it makes sense!

Check in!

Figure 12 Add the changes to your repository and make a small celebration.

java -cp app.jar com.ssem.app.Launcher '/opt/local/app/db.txt'

Lets add the changes we have made to our versioning system. When the change makes sense, you want to share it with your teammates as quickly as possible. This is also a good time to check off the completed work in the Mikado Graph.


Figure 13 Marking the completed work with checkmarks gives a sense of progress and closure.

Is the goal met? Are we done?

Figure 14 Are all the nodes taken care of? Is the goal met?

Our goal is met; we can change the database location from the start script. We're done!

When problems are this easy you don't really need to use this much process. As the problems grow and become more complex they become harder and harder to analyze and fit in your head.

Summary

By showing you a somewhat realistic example we now hope you have begun to get a grip on how to perform changes the Mikado way. This hands on, empirical technique mixes small safe experiment with notetaking and the notes create your refactoring map. Your big takeaway should however be that the code remains in a working condition at all times, except during the brief moments when you experiment and try things.


Here are some other Manning titles you might be interested in:

Unit Testing in Java

Unit Testing in Java
Lasse Koskela

Dependency Injection in .NET

Dependency Injection in .NET
Mark Seeman

Continuous Integration in .NET

Continuous Integration in .NET
Marcin Kawalerowicz and Craig Berntson


AttachmentSize
mikado_007.png5.07 KB
mikado_011.png5.67 KB
mikado_012.png4.26 KB
mikado_014.png5.61 KB
mikado_015.png4.34 KB
mikado_016.png4.12 KB
mikado_017.png3.96 KB
mikado_001.png27.3 KB
mikado_002.png4.21 KB
mikado_003.png11.95 KB
mikado_004.png5.39 KB
mikado_005.png5.22 KB
mikado_011.png5.67 KB
mikado_012.png4.26 KB
mikado_006.png5.41 KB
mikado_008.png16.26 KB
mikado_009.png3.85 KB
mikado_010.png4.98 KB
mikado_011.png5.67 KB
mikado_013.png18.47 KB
cueball1.png1.19 KB
cueball01.png1.19 KB
Related Topics >>