List of Figures
Chapter 1. Meet the Mikado Method
Figure 1.1. A Mikado Graph with a goal and two prerequisites
Figure 1.2. Being able to change the shape of things is a highly desirable skill
Figure 1.3. A process chart describing the Mikado Method
Figure 1.4. A user story
Figure 1.5. Start with the goal, replacing “Mikado Goal” with your task or story.
Figure 1.6. Note the immediate solution, or prerequisite.
Figure 1.7. Repeat the steps for each prerequisite to build knowledge about the dependencies.
Figure 1.8. Check off the solutions as they are completed.
Chapter 2. Hello, Mikado Method!
Figure 2.1. The Mikado Method process
Figure 2.2. Drawing the Mikado Goal will help us focus on the task at hand.
Figure 2.3. A clearly written goal makes it easy to know when it’s accomplished.
Figure 2.4. Trying things can provide more feedback than hours of analysis.
Figure 2.5. The compiler, tests, or running the system will help us find the errors quickly.
Figure 2.6. Don’t think too hard about the consequences; just pick a solution that will pull the system in a good direction.
Figure 2.7. We add the solutions to the graph as we come up with them.
Figure 2.8. Prerequisite added
Figure 2.9. The most important, and yet unintuitive, step of the process: fix the broken system by reverting
Figure 2.10. Zoom out and look at the graph. What’s the next sensible prerequisite, or leaf, to work on?
Figure 2.11. Checking in cohesive changes will make coworkers much happier.
Figure 2.12. Add the changes to the repository and have a small celebration.
Figure 2.13. Marking the completed work with check marks provides a sense of progress and closure.
Figure 2.14. Are all the chart nodes taken care of? Is the goal met?
Figure 2.15. A schematic of the current system
Figure 2.16. After we figure out what our goal is, we write it down as our Mikado Goal.
Figure 2.17. Adding a prerequisite that’s a bit more explicit in its implementation details than the goal
Figure 2.18. The information we’ve gathered from a naive edit
Figure 2.19. One leaf, or prerequisite, checked off and checked in
Figure 2.20. Database interface in use
Figure 2.21. More prerequisites for our goal
Figure 2.22. We’re done!!
Chapter 3. Goals, graphs, and guidelines
Figure 3.1. Notice that the dependency arrow points in one direction and that the graph mandates the opposite order of execution.
Figure 3.2. Simple things to the rescue
Chapter 4. Organizing your work
Figure 4.1. Third-level tactic
Figure 4.2. Selecting a goal
Chapter 5. Breaking up a monolith
Figure 5.1. The LoanServer creates a LoanHandler, which creates a LoanApplication, which is passed to the LoanRepository,
which returns a Ticket for the loan application.
Figure 5.2. This is the Mikado Goal. When we’ve achieved this, we’re done.
Figure 5.3. Graph representing the first steps for encapsulating the LoanRepository
Figure 5.4. Graph representing the final steps to encapsulate the LoanRepository
Figure 5.5. To finish separating the different responsibilities, we need an approve server.
Figure 5.6. We need to move isApproval and approveLoan to ApproveHandler, and make idSpecified and TICKET_ID available for
both LoanHandler and ApproveHandler.
Figure 5.7. idSpecified and TICKET_ID are shared, so we put them in a new RequestHelper class in the new loan core project.
idValid also has to move to the new class.
Figure 5.8. When we move approveLoan, we see that LoanRepository needs to be in the loan core project. When isApproval is
moved, we see that we need to move the APPROVE constant.
Figure 5.9. The LoanRepository and its companions Ticket, LoanApplication, and ApplicationException need to be in the loan
core project.
Figure 5.10. Partially checked-off graph
Figure 5.11. All nodes checked!
Figure 5.12. The result of restructuring the code. At the top are the server parts, and at the bottom is the shared loan core
project.
Chapter 6. Emergent design
Figure 6.1. A typical Mikado graph for extracting a responsibility
Figure 6.2. Adding the game of whist to the CardGameEngine
Figure 6.3. Create a codebase that’s easier to change by creating user-specific interfaces.
Figure 6.4. Process for creating segregated interfaces: 1) Initial situation with a big interface. 2) Create empty user-specific
interfaces. 3) Pull up one method at a time to the new interfaces. 4) When the new interfaces are defined, change the users’
references to the new interfaces.
Figure 6.5. By extracting an interface between a client and a provider, a troublesome dependency chain can be broken.
Figure 6.6. The simple life: a single loan application package deployed on a server
Figure 6.7. When you want to create the new approve server, you realize that there’s some loan core code you want to use in
both places.
Figure 6.8. In development, you have the apply package, the approve package, and the loan core package. In the deployment
step, they’re bundled in two deployables and each is placed on one server.
Figure 6.9. When moving the RateFeed class to the loan core package, the circular dependency must first be broken.
Figure 6.10. Cyclic dependencies are evil
Figure 6.11. Moving the RateFeed class to the loan core package creates a circular dependency because of the interaction with
the RateDisplay class. By breaking the circular dependency with an interface, the packages can comply with the Acyclic Dependencies
Principle.
Figure 6.12. The initial Mikado Graph for creating a plugin structure
Figure 6.13. The loan core accepts loan plugins that implement the contents of the loan interface package.
Chapter 7. Common restructuring patterns
Figure 7.1. Two equivalent graphs; the second one groups nodes with common dependencies
Figure 7.2. The template change is the big bubble, whose actions (nodes) are performed over every item with the same prerequisites.
Figure 7.3. Two goals with common prerequisites
Figure 7.4. Splitting a Mikado Graph
Figure 7.5. Exploring two different options for solving a problem
Figure 7.6. Merging code to split it out another way: 1) Code is scattered in a structure. 2) Merge all the code into a single
super-package. 3) Reorganize the code within the super-package and gather the scattered code into a new package. 4) Extract
the new package containing the formerly scattered code.
Figure 7.7. Extracting a library containing user functionality
Figure 7.8. Move code directly to a new package: 1) The initial situation with scattered code in several packages. 2) Create
a new package for the scattered code. 3) Move the scattered code into the new package, starting with the package with the
least number of outgoing dependencies. 4) The result with all scattered code gathered in one package.
Figure 7.9. Move code gradually to a new package: 1) Initial state with code scattered in dependent packages. 2) Move the
scattered code in the dependency direction to minimize problems. 3) Aggregate along the way; the A package acquired a dependency
to C, in order to be able to use the scattered code there. 4) Create the destination package and gather the scattered code
there; all packages depend on this new package because they all use the scattered code’s functionality.
Figure 7.10. Extracting a library with user functionality, including an interface and an implementation package
Figure 7.11. Move functionality to a temporary shelter before performing a change.
Appendix A. Technical debt
Figure A.1. Staff turnover
Figure A.2. A situation of stress
Figure A.3. Insufficient knowledge
Figure A.4. Not invented here
Figure A.5. Influence and type quadrant
Figure A.6. Phrases that hint where technical debt originates from
Appendix B. Setting the stage for improvements
Figure B.1. Ghost code
Figure B.2. Zombie code
Appendix C. Dealing with dynamically typed languages
Figure C.1. The goal and the test prerequisites
Figure C.2. Response (res) and request (req) are needed as parameters
Figure C.3. Request needs a URL
Figure C.4. end needs to be faked too
Figure C.5. Add repo to launch for easier testing
Figure C.6. Control the callback function
Figure C.7. An example of testing apply