Revenge of the Top-Down Thinkers
I don’t have anything particularly profound to add to the theory or practice of refactoring, but I do have a few observations worth throwing out there for consideration.
First, I do think there are two kinds of engineers in the world – bottom-up (detailed) and top-down (conceptual) thinkers. For the most part, early developers were bottom-up thinkers. Top-down thinkers were not willing to master the magical incantations required to make a computer do what you want when all you have are command lines and assembly language. After all, they had big ideas and all those trivial engineering details only confused the picture.
But over time measured in decades, something magical occurred. We learned from our mistakes and started inventing languages that were inherently easier to use and opened up the world of programming to many more people. Computing power grew to the point that we could afford to use managed runtimes, portability improved, and IDE’s became very powerful. Like it or not, Visual Basic was the hallmark of this new generation, and while we probably all agree that VB enabled people that shouldn’t program to think that they could, it also liberated many more top-down thinkers to begin to create solutions that started with the most obvious manifestation of the product – its user interface.
I believe that the many early attempts at mastering system development through up front design were invented by bottom-up thinkers. To them, this is simply the most natural way to operate – you begin by building libraries and low level classes as you construct a hierarchy of inheritance and abstraction. While not every bottom-up thinker has this issue, I have heard this process described as “building an edifice to the gods”. Weeks and months go by with little demonstrable product available and the business starts to get frustrated wondering if engineering has lost touch. In many cases, they have – significant amounts of effort go into solving the most generalized form of the problem because they still don’t really understand exactly how it will be used. Architecture without purpose does not have intrinsic value – - it’s a beautiful work of art in a dark room.
Compare this to how Agile would tell you to behave – build demonstrable features with the simplest possible design needed to deliver them. Have confidence in your ability to rewrite and restructure as needed to fulfill the new requirement. Introduce architecture when it is evident that it has business value – either through enabling scalable deployment models or to support the mass customization of your solutions in the market. Build the solution from the top down and create the classes you need when it is clear you need them. Introduce base classes and inheritance when it is obvious that the alternative is to have a significant amount of common code in multiple places. Build interfaces when it is clear you need them to support specific extensibility goals or to enable good testing.
Thus my first conclusion – Agile is a methodology for top-down thinkers. As a top down thinker, I like both that it allows me to engineer in a way that is natural and that it supports running projects in a way that leads to shipping products and business success.
Second, I wanted to mention my recent experiences building a mobile application using Agile technical practices. Mobile apps can be a little challenging for doing Test Driven Development as the tools are less mature and it requires more effort on your part to implement mocking strategies to enable automated unit tests. One approach is to try to use a JUnit-derivative such as JMUnit or it’s BlackBerry derivative called BUnit, but both of these require running the unit tests on either an actual device or a simulator. As a result, the coupling of these tests to continuous integration (CI) tools is, shall we say, less than straightforward. JUnit itself will not deploy and run on a BlackBerry as it depends on classes that do not exist on the J2ME platform. An alternative (and the one we chose) is to build mocks for device services such as communication services and storage and to use these mocks from JUnit in a traditional J2SE project. With this approach you also get the nice JUnit integration into Eclipse (or your IDE of choice) and easy integration to CI tools. Yes, it takes extra effort and creativity, but it’s worth the result. Abstracting your underlying services into interfaces on a mobile platform is a good idea anyway as you may find that you need to have carrier or phone model specific implementations of these services – your mock implementation becomes just another available plugin for your architecture.
This experience has really solidified my commitment to TDD and refactoring – I’m very happy with the state of the code produced and the speed with which we can react to changes and add functionality. There have been a few instances where it made sense to consolidate functionality into base classes, but we waited until duplication of code made the need evident. I’ve also gained a better appreciation of how inheritance and overrides tend to maintain a good balance between reuse of a third party API while still allowing you to extend the classes to get functionality different than intended by the designers. Managed OO languages like Java and C# sure make this easy – I rarely feel like the language or tools are making it harder than needed to accomplish what we want.
I’m also happy with the presence of these unit tests as documentation for how the classes should be used – this is a contract engagement and the responsibility for supporting and extending this codebase will fall to others after I’m gone, so it feels good to leave behind good examples for how the objects should be used. The classes that could be generalized enough to unit test are also the classes most likely to be re-used in other Java based mobile platforms.
One final note – I’ve been obsessing about how to best meet the goal of building with no warnings because there are instances where I have methods required to comply with other interfaces that are not used or mock classes that are not used in a normal production build, yet the presence of these unused methods throws warnings during the compile. I know that the rules say I should rip these out, but somehow that just doesn’t seem like the right thing to do. In some cases, I have settled for commenting them out for now knowing that makes it easier to put them back if I need them later – and that exact thing has already happened a couple of times.













