The Mikado Method (2014)
Part 1. The basics of the Mikado Method
Chapter 3. Goals, graphs, and guidelines
This chapter covers
· The Mikado Method guidelines
· The components of the Mikado Graph
· Variations when using the method and creating the graph
· Important properties of the method
· Relation to other thinking models
So far we’ve covered basic Mikado Method concepts and mechanics with the intention of teaching you enough to solve basic problems. To take you to the next level of understanding, we’ll now get to the roots of the method and cover some new ground. It’s time talk about the underpinnings of the method and touch on the theoretical aspects. After you’ve read this chapter, you’ll be able to evolve and adapt the method and apply it to situations other than those described in this book.
3.1. The Mikado Method guidelines
There are a few core things you need to keep in mind when you use the Mikado Method, because without them the method will quickly deteriorate:
· Always start with the goal
· Use experiments
· Revert—the key to safe refactorings
· Focus on the leaves
We’ll discuss these guidelines in the following sections, and they’re here to remind you about the process if you decide to tweak it.
3.1.1. Always start with the goal
Whenever you work with complex problems, such as programming and systems design, you want to maximize your thinking capacity and direct it to finding a solution. By taking notes on a piece of paper or a whiteboard, you immediately reduce the cognitive load on your brain, and that capacity can now be used to solve the problem. There’s no longer any need to juggle obstacles and possible solutions in your mind, as they’re now recorded on a piece of paper.
If you formulate the goal as an ideal future, it’ll be easier to recognize when the goal is met. Writing down the goal forces you to put the idea, or feeling about what needs to be done, into words. This often serves as a reality check: once you formulate a goal, it’s easier to determine its importance and the reasons for achieving it. If you can’t present good reasons, maybe you really should be doing something else. A goal that has been written down can also serve as an external commitment, both to yourself and other interested parties; it’s something that can be discussed and scrutinized.
First and foremost, a good goal must carry value—the more value the better, and if stakeholders agree it constitutes value, you have a winner. Consider the difference between “Update third-party API to version X” and “Customer address is retrieved using the latest version of the third-party API.” They might mean the same thing to a person with a technical background, but the latter hints at some sort of business value.
Second, the goal should be concrete enough so that you know where to start and when you’re done, even after a long weekend. We prefer “When a session times out, a login screen is shown” over “Timeouts don’t affect the user experience.”
Finally, the goal needs to be specific. Vague goals like “Use a faster XML parser” aren’t going to help anyone. You should aim for something more specific, like “Loading <100 Mb XML configuration files can’t take longer than 2 seconds.”
3.1.2. Use experiments
When the goal is in place, it’s time to find out what’s preventing you from reaching it. In the previous chapters, we showed you how to do this by making small code changes. These small changes are micro experiments that manifest themselves as compilation errors. Any error or other problem, such as a test that doesn’t pass, gives you the feedback you need to explore the system. All these micro experiments in combination result in the restructuring map. Restructuring map is a term we use interchangeably with Mikado Graph, especially when we talk to non-technical people.
We could spend time analyzing this map, but we strongly prefer the rapid feedback from compilers and test suites over analysis, which is why we act as straightforwardly as we do. It’s our way of avoiding analysis paralysis. We take this course as long as we do our work in a safe environment. By safe, we mean that there are tests, compilers, or other means for quickly checking for errors. If you use a lot of reflective features or a dynamically typed language, you’ll need to be a bit more analytical, at least before you decide to integrate your changes with the rest of the code.
Languages and Type Systems
Instead of enduring long periods of analysis, we aim for the simplest possible thing we can come up with, and we’re often surprised by our progress and findings. Mucking around is normally more fruitful and produces results far more quickly than hours of analysis. This experimental style, the Naive Approach, was discussed in the first two chapters. Some trivial examples include removing methods, changing qualifiers (such public to private or static to nonstatic), and then recompiling the code or just running the application or tests to see what happens.
When our experiments don’t seem to contribute to the expected progress, we stop for a minute and analyze the situation. Our initial efforts still pay good interest because the information we’ve discovered up to that point is often very useful when the analysis starts.
The Naive Approach shouldn’t be confused with changing things at random and then committing code if it compiles. The value from this approach comes from its directness, and it stands in stark contrast to long periods of analysis that in effect don’t verify a thesis. The analytical approach can often feel safe and structured, but it’s only when the rubber meets the road that you see whether or not your analysis was correct. We’ve found that shortening the analysis phase and sometimes skipping it entirely produces the results we want a lot more quickly than thinking about a change.
There is, of course, room for both experimentation and analysis, but our experience tells us that the first is underutilized and the latter is overvalued.
3.1.3. Revert—the key to safe refactorings
The most fundamental concept or guideline of the Mikado Method, and of sensible software development as a whole, is to limit changes and additions to situations where the system is in a working and known state. Then, little by little, you can start tweaking the code and immediately confirm that it still works as expected. If it doesn’t, you revert to the last known working state.
Making improvements and additions to broken code is problematic because when you make another change and the code wasn’t working to start with, you don’t know if the broken state comes from the initially broken state, the new change, or a combination of the two. In addition, you don’t know if your change actually fixed the initial problem. Sometimes you end up lucky and fix everything in one sweep, but that’s a rare case indeed. If instead of depending on luck, you make a change that takes you from a working state to a nonworking state, you can be quite sure that it was your change that broke the code. In a complex system, other things can affect the state, but if you revert and the problem is gone, that supports the theory that it was your change that caused the problem. (If not, you have another problem you need to take care of before making your change.)
This is exactly the same as the red-green-refactor cycle in test-driven development. You write a test that fails (red). You write code that makes the test, and all other tests, pass (green). Then you can refactor, making changes to your code as long as all the tests pass. If a refactoring breaks any test (red), you back out of that change (green), and try another refactoring.
Your Mikado Graph isn’t only a refactoring map; it also works as a simple reminder that you should experiment and revert when things break. Look at it and update it frequently. If nothing happens in the graph, chances are you’ve been working in a broken state for too long.
If you’re using a dynamically typed language, it’s harder to know when the code is broken, and you need to be a lot more careful. The system must be executed in some way to verify its condition, preferably by using automated tests. If you’re working without automated tests, you need to be extra careful and verify some other way that the system works. An alternative to using compilers and automated tests for finding problems is to build the system after each change and test the system manually to make sure, to some reasonable degree, that nothing is broken. This is a tedious process, but if you can build, install, and launch your system with a single command, you’ll improve your situation.
It’s never too late to turn back
Every once in a while, you’ll be up against a change that doesn’t feel big at first, but then it snowballs, and before you know it your change has spiraled out of control. When we initially came up with the Mikado Method, we walked into a situation like that, but that happens less often these days. Knowing about the method not only makes it possible for us to perform big restructurings; we can also sense a big change earlier. Turning back at moments like that is what we recommend.
One way to avoid derailing your changes is having some sort of reminder that asks, “Is it time to check in, or is it time to revert?” That reminder could be a timer set for 25 minutes, an agreement with a colleague, or something else that happens at a given point. Decide how much time you feel you can lose, and set up your working environment accordingly.
3.1.4. Focus on the leaves
Your hard work will eventually lead you to code that can be changed without introducing additional errors, but you have to be patient in getting there. Big redesigns and restructurings can touch on hundreds or even thousands of files, depending on how bad the condition of the system is, and how things are coupled. Sometimes exploring the graph’s breadth first, one level at a time for all the nodes (leaves), feels like the natural choice. Sometimes going depth-first, where you explore all the levels in one branch, is best. Whichever you choose, exploration is always done by focusing on the leaves. The leaves are nodes that have no prerequisites yet, and they’re usually found at the periphery of the drawn graph.
The Mikado Method is a bit like a dance, and it often goes like this in exploration mode: start (with a goal), experiment, note the results, revert, experiment, note the results, revert, experiment, note the results, revert, and so on. If you go depth-first, you try an experiment, note the results, revert, and continue to explore the graph in one direction until you can achieve something. This is the preferred approach if you have an idea about where you want to take the code. If you don’t have a clear idea, or you want to get a sense of the size of the problem, try a breadth-first approach and make sure you find all the prerequisites for one leaf before moving on to the next interesting leaf. In either case, you’ll eventually find leaves that are true leaves, and then the dance changes to execution mode: implement a leaf, check it into your versioning system, check it off the graph, implement the next leaf, check it in, check it off, and so on. Depending on your problem and your depth tactics, the two dancing modes may be interleaved.
Two different modes
When you use the Mikado Method, there are two more or less distinct modes when it comes to the graph and adding nodes: the exploration mode and the checking-nodes (or execution) mode. There’s no clear definition of either, but you know you’re in the latter phase when you get the feeling of having crested the hill. You know you’re in the former when you’re adding more to the graph than you’re checking off.
3.2. The Mikado Graph
As you might have noticed, the Mikado Graph is a directed acyclic graph (DAG). All graphs consists of nodes (the “bubbles”) and of edges (the connections between the bubbles). A graph that is directed means that the edges have a direction—they go only in one way. Acyclic means that you can never get back to the same point by following the directed edges. In this book, we call the directed edges dependency arrows.
If Mikado Graphs weren’t DAGs and instead were directed cyclic graphs (DCGs), it would be possible to end up in a loop during the exploration phase. Fortunately, this isn’t the case. But sometimes when you explore the code, you might think you have a cycle in the graph—this is what we call a false cycle. There’s always a way to break a false cycle, usually by breaking the refactoring into smaller refactorings that can be executed step by step.
The full graph represents the body of knowledge you’ve gathered about a particular restructuring or redesign. Let’s take a look at the individual parts of the graph for a while and deepen your knowledge about each different component.
3.2.1. The goal—business or technical?
When you decide to make a change to your system, there’s an underlying assumption that this is necessary and that it must be done in order to move the system forward. Your goal could be a wish for a more flexible design, which in turn enables faster testing, just like the first example in this book. Or it could be a desire to partition the system in order to allow a new or changed feature; you’ll see this in the example in chapter 5. Faster testing is generally considered a technical goal, whereas preparation for a new feature is more of a business goal. There is, of course, a fuzzy line between the two.
Mikado Graphs with business goals can be used to communicate with non-technical people. You can, for instance, show a non-technical person the graph and simply say, “So far, this is how much we believe needs to change in order to accommodate this new feature you’re asking for.” If you use a graph to communicate the complexity of a change, it’s the number of nodes that conveys that information. The problem is that initially you don’t have all the nodes, and in that case the breadth is a better measure of complexity. Do keep in mind that this is tendentious information only, not an exact metric.
A technical goal is generally used within a team when a problem arises that isn’t directly tied to a business goal, such as “Introduce a Money type.” Both technical goals and business goals are important to pursue, but keep in mind that technical goals usually have an underlying business goal. Keep looking for the business goal, because it’s much easier to motivate people to spend time on something that’s directly connected to the interest of the stakeholders. Work to combine the technical goals with business value. Try to split them into smaller pieces that can be dealt with one at a time, in connection with a business goal.
3.2.2. The prerequisites
You already know that the prerequisites often manifest themselves as compilation errors or test failures, and that you shouldn’t try to fix those errors immediately. Instead, analyze the situation just enough to decide on immediate solutions that will resolve the errors. Often, you won’t know what the exact consequences of your change will be anyway, so choose a simple resolution to the problem to keep your momentum going. When you realize that there are things that aren’t immediately shown by the compiler or the tests, write these down as prerequisites as well. This way, you can put all your available tools and knowledge to use when you decide what to do.
You’ll save a lot of time by following the Naive Approach and avoiding unnecessary analysis. Be practical and direct when you can be, and smart and analytical when you must.
Don’t think too long
Our rule of thumb on analysis: try something straightforward and practical if you’ve been analyzing for more than a couple of minutes.
3.2.3. Prerequisite—step or decision?
As you explore and evolve the graph, you’ll discover more and more details that you’ll note on your piece of paper. Many notes are tasks or detailed descriptions of what should happen, but sometimes you need to make decisions. So far we’ve been fairly detailed in our notes, but there are times when you’ll want to postpone an implementation detail. In those cases, you can just write “Break circular dependency” or something that reminds you about what needs to happen instead of writing exactly how. You’ll still have the note, but also the flexibility of going in several directions.
A note like that is what we call a decision node, and if you pay close attention you’ll also notice that decisions look the same in all our Mikado Graphs. That’s because we don’t want the method to be encumbered with unnecessary ceremony or notation. If we introduce too many concepts or too much detail, we run the risk of creating a new Unbeatable Modeling Language with rules about everything. Or even worse, detailed rules about several versions of notation.
We want all our attention to be focused on solving the refactoring, not on deciding what symbols to draw in the graph. By reducing the number of concepts, we aim to minimize the risk of drawing the graph in the “wrong” way. In addition, this gives you, the Mikado Method practitioner, more flexibility to modify the graph notation to fit your current needs. If you have a strong desire to make the decisions or any other node stand out, feel free to draw them in a different shape or to spice up your graph with bright colors. From an execution perspective, it doesn’t matter much. You’ll have to implement all the prerequisites in due time.
And with that said, we do have a few informal variations we’ve used, and those can be seen in chapter 7.
3.2.4. How big should a step be?
So far, we’ve intentionally been very explicit in our note taking, and nothing has been left out of our Mikado Graphs because we believe it’s important to show you how the process works. But big redesigns and large restructurings often result in big graphs with many nodes. If you get the feeling that the graph is growing too quickly, with too much detail, you can try to combine several steps or tasks into one node. That is, you can decrease the granularity.
Like any other process or methodology, you probably want the Mikado Method to support you rather than to be in your hair when you’re working. If you can’t make out where you’re going because there are too many nodes, the process might be getting in your way. Whether it is getting in your way or supporting you, however, is highly dependent on the context.
Sometimes you’ll want to be a bit more detailed, such as when you need to convey information to fellow developers or other interested parties. Sometimes you’ll have a tricky problem and the graph gets big.
Other times, you won’t need the same granularity because you and the other developers already have a common language and the same idea of how things should be accomplished. At times like that, you can choose to be a little less detailed in order to speed progress.
3.2.5. More about the leaves
All nodes in a graph except the leaves are inhibited by their prerequisites. The leaves in the Mikado Graph are the only places where a code change can actually be performed. If a node isn’t a true leaf, the system will break when you execute the task in the node. Some nodes may looks like leaves at first, but they may reveal prerequisites when you try to implement them.
When all the prerequisites for a node have been taken care of, that node itself becomes a leaf, ready to be dealt with. Usually, you can start with any leaf, but sometimes multiple nodes point toward the same prerequisite. This happens rather often, as it did in our earlier example of removing the database dependency (in chapter 2), where almost everything depended on the creation of a FileDB at the top of the graph. Other times, the graph splits early on into streams and joins later. In all cases, start with a leaf that seems to have the potential to give the next complete committable change set, or a leaf that you think will give the best uninterrupted flow of refactorings. In our example of removing the database dependency, we had to explore the entire graph before we felt we could check in anything. That made sense in that case, but often you can check in changes that in retrospect turn out to be midgraph.
3.2.6. The dependency arrows
As you explore systems and expand graphs, you add new nodes and also the dependency arrows between them. The dependency arrows point from the original goal toward its prerequisites (see figure 3.1). You can compare the goal, your major change, to an explosion, where the force of the change is directed out from the center.
Figure 3.1. Notice that the dependency arrow points in one direction and that the graph mandates the opposite order of execution.
This might feel a bit awkward at first, because you often think of what needs to be done in a sequence and point the arrows in that order. In contrast, the arrows in the Mikado Graph represent dependencies. When you make the changes, you travel in the opposite direction of those dependencies, starting with the leaf prerequisites. You are trying to stop the explosion.
3.2.7. The check marks
To keep track of our progress, we check nodes as they’re finished. Sometimes, we put a check mark by the leaves one by one as we complete them, and sometimes we check them off as a batch.
If we check them off one by one, we risk having to remove a check mark if we discover that we’ve missed something and must revert our changes, such as if we discover that a leaf wasn’t a true leaf. A common strategy we use is to check off the nodes when a coherent change, a change that makes sense, is completed. When we check the nodes off the graph, we should also take the time to check our changes in to the versioning system.
Check in often
The simple mantra “check node, check in code” reminds us to check in code often. The VCS is there to help, so make good use of it.
We have few recommendations for tools other than pen and paper, but we do recommend using a VCS that supports short-lived branches, in which you can do several small commits, and do them really quickly. Distributed VCSs do this very well, because they enable you to make as many commits as you want, without affecting the main code repository. We can’t stress enough how much we encourage frequent commits within a branch, at least when a coherent change is ready. That way you get several points to go back to, and that simplifies reaching a known state.
Checking the leaves off isn’t just a good way of seeing what you’ve accomplished. It’s also rewarding to check off one leaf after another, knowing that they are steps leading toward your goal.
Another way to put a leaf in a done state is by simply erasing it as it’s completed. This frees up precious (whiteboard) space, and the finished nodes no longer clutter up your graph and thinking. The disadvantage of this approach manifests itself later if you want to analyze the path you took, or if you want to see each and every task you’ve finished. We suggest you take a photo, or copy your graph to a piece of paper if you believe you’ll need it later. Usually you won’t, though.
Erasing finished work from your graph is sometimes necessary if you have a limited amount of space, but putting a check mark beside a node as you finish a task is fast, and it also gives you a rough estimate of how much work you’ve completed and how much remains. Let your intuition guide you, and experiment with both approaches. You’ll soon discover which is appropriate when.
3.3. Tips for using the Mikado Method
If you’re going to use the Mikado Method in some novel situation or problem, the following tips we find helpful might also help you:
· Focus on the goal
· Follow the flow of the code
· Keep it simple
· Keep it visible
· Iterate, increment, and evolve
· Remember the state
· Reflect on your work
The Mikado Method is relatively easy to explain, and the body of knowledge needed to use it is fairly limited, but these points can help you when you’re explaining the method to someone else or tweaking the method for yourself.
3.3.1. Focus on the goal
When you write down the Mikado Goal and do things that are connected to that goal, which is often a direct or indirect business goal, you automatically tackle the most important tasks first. Because you’re trying to reach the goal by completing tasks in the leaf nodes, you’re either getting closer to the goal or learning more about the system, and both are equally valuable. But that focus is easy to lose when pressure is added or distraction comes from a boss, customer, or someone else.
The clear, goal-oriented approach also doubles as a good basis for dialogue with business people who don’t always understand how technical implementation details are tied to business changes. When you show them the goal and the graph, people who aren’t responsible for the technical implementation can see how that work is directly connected to business needs. This can help disarm a common situation where developers are accused of delving into technology for technology’s sake. It also avoids the reverse situation where business people are accused of not understanding the essential nature of technical improvements.
Defining a very specific and distinct goal makes the code easier to work with, not just because it can be discussed and related to, but also because it provides a clear focus and something tangible to work toward. At the same time, prerequisites are more easily identified and achieved along the way, and optional solutions to problems often present themselves in a totally different light when there’s a plan.
The bigger decisions are best made when the whole picture is visible (in a zoomed-out mode), whereas the technical decisions are best made as the code is being written (in a zoomed-in mode). The graph is the plan, and it serves as a high-level strategy for reaching the goal. The nodes contain more detailed information and serve you better on a tactical level. You can zoom out for strategy and zoom in for tactical matters.
3.3.2. Follow the flow of the code
Before we discovered the Mikado Method, we used to work with to-do lists, which worked fine for small restructurings but not with some larger problems. We realized that to-do lists miss a very important aspect: the transitive information, or how things are connected to each other. To-do lists work when you need to keep track of work in one dimension, such as the breadth or the depth of a change, but not both at the same time.
The Mikado Graph can’t be replaced with a simple list because the dependencies get lost and it’s hard to remember what you need to do first. Furthermore, a single new dependency arrow can change the order of the entire execution. A list works for linear problems, but it’s not a good model for the changes in a complex environment like a codebase.
The effectiveness of the Mikado Method comes from the fact that all the nodes in the graph are created from true prerequisites of the Mikado Goal, and that the goal has a transitive relationship to all the other nodes. That is, when the goal is met, all other prerequisite tasks have been taken care of. The reverse is also true: when all the prerequisites have been taken care of, the Mikado Goal is met.
Because you focus your attention on the leaves, there’s no, or very little, extra work, and the work flows naturally. This means that you don’t have to worry about not reaching your goal, because you always do, sooner or later. Sometimes, however, you reach a point where a decision has to be made. Should you go with solution A, or maybe choose solution B? Depending on which solution you choose, you might or might not run into other problems later.
Usually there’s more than one way to solve a particular programming problem. This means that if you redo the same restructuring, you could end up with two quite different solutions. If you have options but aren’t sure which one you should use, that’s a good indication that a short design discussion or session is in order. In addition to that, we recommend that you consider trying out possible options and seeing where they take you. Remember, experiment-and-revert is your friend.
3.3.3. Keep it simple
The exploratory Naive Approach of revealing what’s between you and your goal will point in the right general direction as you change code and try different solutions. It immediately shows you where the next problem is in the simplest way: an error. The graph is also the simplest possible tool you could come up with to record all the things you need to do in order to reach your goal.
The method isn’t just simple, it’s also pragmatic because it doesn’t look for the optimal solution, just one that’s sufficient. This is a great way to maintain focus and avoid gold-plating. You might think that sounds a bit rigid, but remember that the prerequisites allow you to fine-tune the solution to suit your needs.
Often engineers and developers want to be able to say, “This is the best way to do it!” The Mikado Method takes a different and more humble slant, saying, “This is one way to do it.” We try to remember that “perfect is the worst enemy of good enough.”
The goals of the examples in the previous chapters can actually be met in several different ways. Before we settled on the code that we presented in this book, we tried several different solutions, and they looked quite different. And our examples have been small; it’s easy to see that a production system could have even more ways to meet a goal. There are, however, different ways to understand simplicity here. We mean it’s simple to find a solution, not that the solution you find will be the simplest one to understand later. This is where your design skills come into play.
3.3.4. Keep it visible
Visual aids and information radiators such as whiteboards, flip charts, or a sheet of paper can provide an overall picture very quickly. These are what we use when we write down the goal and the nodes. This graph then guides us and reminds us where we’re going and what we’re currently working on. Using visible charts is also a great way to stimulate more parts of the brain, which is a good thing when it comes to problem solving.
3.3.5. Iterate, increment, and evolve
One step at a time is how we tackle big tasks, and big code changes are no different. When it comes to the process, the Mikado Method reminds us of an old joke:
· Q: How do you eat an elephant?
· A: One bite at a time.
Both in its exploration and execution phases, the Mikado Method is a one-step-at-a-time endeavor. This implies that the graph will continue to change as long as prerequisites and transient prerequisites are found on the journey to the goal.
Although the effects from the actual change effort come as small alterations and incremental improvements, the Mikado Method itself is an iterative process. This means that the system can evolve incrementally from work iterations, which makes it possible to select a good time for making critical changes or to spread out the work over time. It also means you can stop at any time, like at the end of an iteration, to make a release, if that’s appropriate.
As opposed to producing a Big Bang change, like merging half a year’s drastic refactoring work from a separate branch, the iterative nature of the Mikado Method and its incremental results produces an evolutionary, rather than a revolutionary, effect. No extensive merge work is needed; no long-lived branches with half-finished refactorings will be lying around with people wondering if they’ll ever be used. And most importantly, code can be delivered and deployed at any time.
A Mikado refactoring can be stretched out over a long period of time, but you constantly merge the leaf changes back to the main branch. When the work is spread out over longer periods of time, it’s likely that change will happen outside of the scope of the goal that was first addressed. If this happens, and it might if the goal is a major one, the graph needs to be revisited and updated. External changes could in extreme cases invalidate your refactoring and your graph, so try to coordinate and align other changes with yours to reap the synergistic benefits instead of bashing heads.
Figure 3.2. Simple things to the rescue
So, will I write better code?
The Mikado Method will not magically make anyone better at designing and writing software. Initially, it will just help you put existing skills to use in a more efficient and effective way. But in the long run, our experience tells us you’ll learn to spot design problems more quickly, thus giving you more learning opportunities. Harnessing these learning opportunities is up to you.
We’ve also seen a shift in developer approach, toward making more fine-grained changes and more frequent check-ins, even when not using the Mikado Method. We believe this is an improvement in working habits that leads to better code.
3.3.6. Remember the state
Imagine a man walking in the countryside. It’s a hot summer day, and he’s getting thirsty. In the far distance, there’s an old well, so he walks toward it.
The well is the old kind, with a crank and a bucket on a chain, but it looks like it’s still in working condition. The man tosses the bucket down and hears a distant splash. He starts cranking it up, but the bucket’s heavy and the well is deeper than he initially thought. The man’s arms start aching and he feels the need to take a short break.
The person who built this well knew that hauling water is hard work, and he put a ratchet on the crank, so the bucket wouldn’t fall back into the water when the crank is released. The man pauses, catches his breath, and is ready to go again. After some more cranking, the reward arrives, and the man tips the bucket and drinks the refreshing water.
Many big refactorings are like hauling water from a deep well; it’s really hard work, and pauses are needed. We’ve done restructurings that have taken days, weeks, and even months in some cases, so we know that large restructurings benefit from pausing and reflecting.
Without some sort of “ratchet,” this isn’t possible. The Mikado Graph serves as our ratchet. It allows us to take a pause and leave our work for a while. When we’re ready to pick up the work again, we can do so exactly where we left off. The graph keeps track of how far we’ve come, and it keeps us from having to backtrack and reexamine the codebase to figure out where we are. Just like a ratchet, the graph allows us to catch our breath, which in the end saves a lot of time and energy.
The graph is also a very fast way to refresh our memory. You can think of the graph as the saved state in a computer game, which can be loaded back into working memory. Just by looking at the graph, the priorities, thoughts, and goals are restored. The graph can lie untouched for a long time, but all it takes is a quick glance and work can be instantly picked up where it was left off. Compare this to reading a long list of to-dos hidden in the code, or a huge technical debt backlog. Not as compelling, right?
3.3.7. Reflect on your work
The Mikado Graph can also help when you reflect on previous refactorings, in discussions, and in learning. Even where a refactoring could be performed with a few small steps, the Mikado Graph can be useful. You can use it to reflect on the path taken to see if something could have been done differently, and what that might have been. Often just taking a short coffee break can put the Mikado Graph in a different light, and let you see it as if for the first time.
The graph is helpful if you need to practice and rehearse a refactoring, over and over, until it can be smoothly executed. This can be especially useful when you’re up against a change that has a lot of steps that all need to be done to make sense before checking in. An example would be moving from one version of a framework to another, or some other case where the undo phase is time consuming or expensive. There are cases where the last critical steps that actually do the job or switch can be harmful if the timing isn’t right. In cases like that, you can benefit a lot from rehearsing the steps and using the graph to reflect on the work ahead to maximize the odds of success.
3.4. Relation to other thinking models
No part of the Mikado Method is really new. Its novelty comes from the way it combines existing practices. We believe that as long as there’s been software, there’s been restructuring, starting over from the beginning, and visualizing dependencies. They’re all concepts familiar to every software developer, if not to everyone who has tried to accomplish anything remotely complex.
Initially, we never thought much about how these concepts related to each other and just did what we needed to do to get the job done. After a while, we realized that the Mikado Method relates to other thinking models, and it gets its flow and rhythm from some vital properties of these models.
3.4.1. Theory of Constraints
The Theory of Constraints (ToC) was introduced by Eliyahu M. Goldratt in his novel, The Goal: A Process of Ongoing Improvement (North River Press, 1984). One part of the ToC is the thinking processes, where one tool is the prerequisite tree. The Mikado Graph represents a prerequisite tree for any given Mikado Goal.
The prerequisite tree has a goal called the injection. To reach that goal, obstacles need to be overcome. For each obstacle, there’s an intermediate objective that overcomes each obstacle. The objectives can in turn have other obstacles that need to be overcome, in a transient manner.
The Mikado Graph contains the injection as the Mikado Goal, and the objectives, which are the prerequisites. The errors in the code that lead you to the prerequisites are the obstacles.
3.4.2. Empirical control systems
An empirical system can be defined as a system that’s based on experiments and observation rather than on an exactly defined process. A common example of this is driving a car from one place to another. You could plan the whole trip ahead of time with the help of a map. Then, given your speed and the knowledge you gained from the map, you’d know when to shift gears and when to turn the wheel. The problem with this approach is that there are a lot more unknown variables in driving than just the directions and your speed. Even if you could gather and use data about normal traffic and weather conditions, chances are that something else would disturb your carefully crafted plan. This is why you need an empirical process to drive a car, where you adjust and learn over time and feed that information back into the system.
There’s a whole body of science on how to control such systems, called Control Theory. Such empirical systems require feedback and corrective action in order to be controlled.
When the Mikado Graph is created using the Naive Approach, the compiler and the tests provide the feedback, and the Mikado Graph is then extended with the corrective action indicated. In Control Theory, fast feedback is important in order to minimize the errors in the process. With the Mikado Method, it’s important to perform small experiments to get feedback instead of doing long sessions of analysis. Instead of sitting home and thinking about driving a car, you need to get out on the road and see and feel for yourself what it’s like.
3.4.3. Pull systems
In Lean Software Development, one of the central concepts is that value should be pulled from the system, not pushed out of the system. Lean concepts originally come from the Toyota Production System and how they manufacture cars. In practice, this means that when a customer buys a car, the salesperson metaphorically pulls the car from the assembly line, which in turn pulls the engine, the chassis, the drive line, and the tires from their respective suppliers, and so on, all the way down to the nuts and bolts. As the nuts and bolts are delivered, the engine and chassis are assembled, and in turn the assembly line can put the car together and deliver it to the customer. This system has proven be very competitive in manufacturing and several other disciplines because it minimizes the amount of work in progress, storage costs, and the risk of not being sold.
The opposite, a push system, essentially creates something without establishing the need for it first. It could be a manager telling the employees in a factory to start manufacturing a certain number of cars, hoping that the sales staff can sell them. This approach introduces a greater risk into the system because you’re relying on forecasts and predictions, and such guesswork is inherently more risky. The manager could, for instance, get the number of cars right but the color of the interiors wrong, or vice versa, which makes the cars harder to sell. As the number of properties that can vary goes up, the bigger the risk becomes with a push system.
A “push” refactoring is where you plan the steps needed in advance, using extended analysis, and then hope that you’ve covered everything and that the pieces will come together in the end. This is definitely possible—it has been done—but the risk is that things that don’t need to change willbe changed, and that essential things will be missed. The amount of work involved is likely to be greater than when using a “pull” approach.
The Mikado Method is a pull system, an approach with very little overhead, where the needed code changes are pulled from the codebase. The compiler and test failures resulting from the Naive Approach pull the next change, and so on, until the leaves are reached, which are the “nuts and bolts” of a code change. From there, the refactoring can be assembled by taking care of the leaves, one after another.
In this chapter, we dug a bit deeper into the Mikado world and explored some thinking tools that will prepare you to handle situations where the standard Mikado formula needs to be tweaked. In the next chapter, you’ll get to know when it’s a good time to start using the method—this depends on your code, your staffing, and your experience with the method.
· Have you used any of the other thinking models described in this chapter? How did it go?
· What values are most aligned with your beliefs?
· What values would you have a problem with in your current work environment? What could you do about those impediments?