“Design” is a verb, not a noun. If I want to create a good program, studying the process of getting there is much more important than the resulting software. This is why I use coding katas as a form of study. I find an interesting problem problem and then solve the same problem over and over again. In this blog post, I will focus on six principles of software design. I will illustrate each with a screencast from a kata.
One of my favorite problems is that of creating a Java EE application from scratch. I call this kata “The Java EE Spike Kata”. In order to understand the role of frameworks, I use no web frameworks in the process of creating this application. I’ve completed this particular exercise about forty times on my own and more than ten times with various pair-programming partners. The whole exercise takes me about 90 minutes, and I still learn new things.
The total time of the screencasts is around 40 minutes, so you may want to pick and choose. Each section provides a link to the starting point for the source code if you want to follow along.
Please notice: The videos are accompanied by loud, pounding music. Keep you headphones on or your volume down if you share offices with someone else. Or mute the videos if you dislike the music.
Idea 1: Build your software from the outside-in
(10 minutes, github starting point)
I start building the application by writing tests that access the application over HTTP and looks at the resulting HTML. As you might have gathered, when I start this test, there is no web application. Only when the tests require a web application to continue do I start creating it. In this example, I had created a basic sketch of the interaction between the three web pages in the application before I started coding. No further design was necessary.
Idea 2: Specify behavior rather than implementation
(6 minutes, github starting point)
I don’t make much of a distinction between different types of tests. All good tests try to describe what the software should do at some level, rather than how the software does it. But the how at some level may be the what at another level. My first test specified the interaction between the web browser and the server. In this test, one step may be to fill in the form element of a web page. This second video shows how this form works in terms of actual HTML. But the details of what framework (if any) is used, is not visible in the test.
The second thing you’ll notice in the video is that I run the tests more frequently. And each test run is much quicker. As our tests move close to the code, the rate of feedback improves.
Idea 3: Increase the rate of feedback
(5 minutes, github starting point)
This video illustrates the frequency of feedback. The example test-drives creating an
equals-method. This task is often not worth test-driving. The resulting method is usually simple and/or generated by your IDE. But it is a good example to of how quick the cycle between test and production code can be when you’re writing tests that are close to the problem at hand. When I pair program this part of the kata, we usually use a technique called ping-pong programming: One programmer writes a failing test (or failing assertion) and hands the keyboard to his partner, the other programmer makes the test pass and writes another failing test before passing the keyboard back. On a good run, we will switch who’s got the keyboard more often than once per minute.
Notice that I also focus on the behavior of the
equals-method in this test.
Idea 4: Grow the API rather than designing it up front
(8 minutes, github starting point)
As the web application grows under my fingers, I discover the need for a Data Access Object (DAO). However, as this represents a major internal interface in my application, I use Mockito to mock the implementation until I’m done with the behavior of my servlet. When this is done, I test-drive the implementation of the DAO in a separate test class.
The video also illustrates another important lesson: The code is getting ripe for a refactoring. But it’s important to resist the urge to refactor until the tests are green. If you refactor on red tests, you have much higher chances of running down a dead-end road and you’ll have to throw away your progress, wondering what went wrong.
The example uses Mockito to mock out the DAO API.
If you want to see how I implement the DAO with Hibernate, you can see the video on blip.tv.
Idea 5: Grow the design rather than speculating
(3 minutes, github starting point)
The video is only a partial example of this principle. Throughout the whole application, I’ve been refactoring, gradually pulling out structure to more well-structured methods and classes. The video illustrates some of the power of IDE’s when it comes to refactoring. Using the IDE to massage your code into a better design makes evolutionary design much easier to do in practice. Make sure to learn your IDE’s most useful refactorings!
The kata may seem like a non-realistic example at this time, but I’ve actually grown a very successful architecture on my current project using much the same approach. If you want to explore where to go next, the next step needed for this application is to factor out the views into separate classes and then use either a View Template language (like Velocity) or a View Transformer (using, for example dom4j) to generate the HTML. (Let me know if you’d like to see the screencast of this as well).
Idea 6: It’s supposed to work the first time around
(5 minutes, github starting point)
In this video, I return to the first test,
PersonWebTest, to finish the configuration of the application. I discover a few mistakes I made in the web test as I complete the exercise. Then I try out the code in the browser. And all the scenarios I had planned for work out of the box.
When you try out your code for the first time, it should work. When you master test-driven development, you will probably forget how you programs didn’t use to work the first time. Only when you occasionally run into an unexpected error during manual testing it becomes clear how test-driven development changes you life.
A big thanks to Trond, Thomas, Ram and Christian who helped improve this post. Thank you to Finn-Robert, Øistein, Mats, Anders, Siv, Peyman, Ivar, Øystein, Cecilia, Nicolay, and Karianne who have pair-programmed this exercise with me. I especially appreciate how Ivar and Karianne both helped influence the way the application is wired together and showed me that even after 30 iterations, I still had things to learn; and how Øistein showed me how two trained developers could complete the exercise faster with pair-programming than alone. And thank you to Nicolay who graciously brought food to our pair-programming exercise.