September 2005


There is a controller class that I’ll now call FooController. This was a Spring Controller that is used for several controller beans that could be called foo, fooBar and fooBaz:

  <bean name="foo" class="com.foo.web.FooController.class"/>
  <bean name="fooBar" class="com.foo.web.FooController.class"/>
  <bean name="fooBaz" class="com.foo.web.FooController.class"/>

Now for one of the beans, foo, we needed some other functionality, that we injected to the bean:

  <bean name="foo" class="com.foo.web.FooController.class">
    <property name="baz"><value><ref bean="theBaz"/></value></property>
  </bean>
  <bean name="fooBar" class="com.foo.web.FooController.class"/>
  <bean name="fooBaz" class="com.foo.web.FooController.class"/>

And we made changes accordingly to the FooController class, for the case that was needed in foo.

Then we started getting NullPointerExceptions from fooBar and fooBaz when they were executing the code of FooController that required baz, even though the views of fooBar and fooBaz did not need it.

So, even though Spring beans are by default singletons in the Spring sense, the Spring singleton sense is rather like Uncle Bob Martin’s Just Create One. Different beans having “singleton” instances of the same class will have their own instances, so providing the dependencies to the foo instance does not help the other instances.

Noticing this kind of errors earlier in the development is the reason why I prefer initialising the crucial dependencies of an object in the constructor, preferably making them final members. This way you cannot create an instance in a crippled state in the first place. Speaking fancy, this would be the “type 3” or constructor-based dependency injection. And for more complex dependencies I like context IoC as well.

Anyway, for a little simple problem like ours, we wanted to have a quick, simple solution. Here is the list of solutions we came up with. If you have more ideas I would be thrilled to hear them!

  1. Separate (sub)classes for each controller. Some day they will surely grow separate, but at this moment I felt awkward about littering the class space because of what is basically a configuration or dependency wiring issue.
  2. Making FooController a traditional singleton by putting factory-method="getInstance" to always return the same, statically held instance of the class and making the default constructor private. But this would have been both verbose in the Spring configuration XML and again littered our java code because of a wiring issue.
  3. Adding the dependecy wiring to each controller bean entry in the Spring XML. But this would have caused duplication in the XML.
  4. Making foo the parent bean of fooBar and fooBaz.

For now, we settled for the last solution that seems OK to me. Sometimes I have thought that the parent-child-hierarchy in the Spring configuration XML might make it too complicated, but in this case it feels justified and the configuration keeps looking fairly simple.

  <bean name="foo" class="com.foo.web.FooController.class">
    <property name="baz"><value><ref bean="theBaz"/></value></property>
  </bean>
  <bean name="fooBar" class="com.foo.web.FooController.class" parent="foo"/>
  <bean name="fooBaz" class="com.foo.web.FooController.class" parent="foo"/>
Advertisement

I was a fan of the stories of Thor Heyerdahl — I read Kon-Tiki, Amon-Ra and also a book on the mystery of the huge statues on Easter Island. The scientific-adventurous style of the books is great. But what I will write about now is a little curiosity that stuck to my mind in the Ra story.

Ra was the Papyrus reed boat that served in one of Heyerdahl’s famous feats of experimental archaeology. He wanted to test the hypothesis that the ancient people would have had vessels capable of crossing the oceans. Earlier, with the balsa raft Kon-Tiki he could show that the ancient South Americans could have travelled all the way to Easter Island. And with Ra he intended to show that the old Egyptian reed boat design was capable of crossing the Atlantic ocean.

Heyerdahl had the reed boat built following the designs from the old Egyptian paintings. Many of the paintings showed a rope between the bottom and the upward-tilted back tip of the boat. Heyerdahl thought that the function of the rope was to force the back end of the boat curve upwards, but when he observed that the boat stayed in good shape even without the rope, he decided to leave it out. This proved to be a bad idea:

A decision to cut what proved to be a crucial line between the prow and the deck caused the stern to sink and the ship to increasingly list to one side. After almost two months afloat, and with a hurricane approaching, the Ra was abandoned at sea frustratingly short close to its goal, still floating but nearly uninhabitable.

(Donald P. Ryan, THE RA EXPEDITIONS REVISITED.)

Heyerdahl learned valuable lessons, built a new reed boat Ra 2, and crossed the Atlantic with it. In the realm of commercial software production, this might be equivalent for example with a quick and easy bug fix, or with a major financial failure, depending on various circumstances.

Obviously you would not be reading even this far if you did not find some interest in fabricated parallels between software and the real world, such as this: the mistake of Heyerdahl is like leaving the programmer testing out of XP. More generally, even good advice can turn against you, if you make a misinterpretation or false assumption, and end up not doing quite what the advisor was thinking about.

The extreme programming example goes to the heart of what I want to convey. If you take such a broad and complex holity as the extreme programming, and do not realise how the separate parts need the support of each other, you might inadvertently cut off a crucial line. This might well be provoked by an illusion of resource scarcity or other types of laziness, for which we have some great expressions in the Finnish language: rusinat pullasta (“only the raisins from the sweet buns“) and yli siitä, mistä aita on matalin (“crossing the fence from where it’s lowest”). We Finns pride ourselves on eating the boring dry doughy (domestic) part as well as the sweet (imported) raisins, and on crossing the fences at their highest parts to not look like slackers. The kind of slackery of not jumping that high when it’s not exactly necessary for getting on the other side is nowadays fashionable as effectiveness, and I think that it is exactly the pursuit of this kind of effectiveness in which you might go astray.

Already in the first edition of the “white” extreme programming book, Kent Beck underlined the fact that the different legs of XP support each other and that the process would fall down like a house cards if you left one wall out. (Without still having gotten around to read the second edition, I should mention that it’s most probably well worth a read.) Obviously he did not describe his process like a house of cards, but that’s the feeling that stuck in me. Later on, I think that Beck pretty much confessed that of the XP practices, at least test-driven development could stand alone, so even if it might not save your project, you might be able to do good work practicing TDD even in a project that does not embrace other XP practices. Though if refactoring would be limited politically, this might make it very diccicult to do TDD.

Of the other XP practices, I think that perhaps pair programming might fly alone as well. Without proper first-hand experience, my guess is not educated. But something I have been through is what happens when you take the XP path of leaving out the big design up front or indeed almost any design up front (which is already getting too extreme to be proper XP), but fail to implement automatic testing, merciless refactoring and pair programming. If this doesn’t result in a mess for you, you better play a round of lottery. It is exactly this kind of faulty shortcuts that give “XP” the bad reputation as glorified hacking.

Another, more subtle example of the intersupport of XP practices, is the crucial but easily overlooked third step of test-driven development: red – green – refactor. If you leave in the repository the first kludge that seems to work, you’re not actually doing TDD but causing trouble:

Once the tests pass the next step is to start over (you may first need to refactor any duplication out of your design as needed, turning TFD into TDD).

Scott W. Ambler

To keep your TDD boat afloat, you need to refactor both the production and test code continuously. It’s not enough to just write a test always before writing code. Another necessity is that all tests are run often and showing green, as maintaining an exhaustive suite of tests is a part of TDD.

Another example still from the XP / agile realm, but from a bit higher point of view, would be overconfidence on developer tests as a measure of progress. For me so far, customer-defined automatic tests or indeed any predefined customer tests have been but a dream. And to measure whether the software is close to complete, it is exactly the customer / user tests that matter. Even if you have good code coverage by programmer tests and you think you have implemented a good part of the required features, you must fight to get the feedback from the customer, or whoever in your environment would be closest to the real user of the system.

What I still saw, even with TDD, are misunderstandings between the project’s customers (also known as business experts or product owners) and the software developers. Even if the deployed code was almost bug-free, it didn’t do everything the customer had expected. We can enlarge on the concept of TDD and reduce the risk of delivering the ‘wrong’ code with what I call customer test-driven development.

— Lisa Crispin: “Using Customer Tests to Drive Development”

To make all these long stories one short one, I claim that you must not adopt a process or a method, and then leave out a crucial part, because then you might fail horribly. Am I then saying that you should adopt any good-looking process blindly and without criticism?

If you got this far and think so, I hope that you will come up with a general counterexample in the next few seconds 🙂

Well, of course I don’t advocate following processes blindly, because cargo-cult programming can be just as bad a problem as taking the faulty shortcuts. In fact I think that the problems are related — omitting crucial parts of a method can be seen as doing cargo-cult on the remaining parts, without fully understanding their dependencies and interactions.

So in the end my point here is not really about being lazy in adapting practices, but being lazy in thinking about your work. The steps on the journey of adapting a method or a process effectively can be seen as the stages in reading texts on a subject with which one becomes more familiar, and can thus move from restatement through description and interpretation to critical reading. To master a skill, you must have the passion, perseverance and ability to learn, so that you will build a couple more reed boats before you get one to cross an ocean with.

We have gradually introduced a little Spring in the system I’m working on, and the results have been good. It’s getting easier to write testable code that can be read and modified efficiently, which is saving our customer money. (My account manager told me to end every paragraph of whatever I say or write in “saving money” or something such.)

However, the silver bullet of Spring does not come without feeding problems. A while back I was making a simple Spring singleton service that stored state that was periodically updated. The problem is that the state did not refresh. In despair, I fell to the despised resort of debug logging. This showed me that the internal state of the service was indeed refreshed, but it did not seem so when calling the service from outside. What?

At some point it occurred to me that maybe my singleton was not really a singleton, and that maybe I was refreshing something else than the object that the rest of the software was looking. And indeed a simple test showed that this was the case.

The Spring ApplicationContext was being refreshed in the bootup process, but unfortunately this was after non-Spring legacy code had acquired a reference to the original “singleton” service. Obviously the context refresh could not do anything to the references from outside the Spring application context, so we ended up with two copies.

Fortunately this one was easy to fix. I also added to the application context a simple bean that will throw an exception if it is initialized twice. This causes context reloads to fail with the exception, which might be a good thing as long as we have non-Spring code that will hold on to stale instances of Spring beans on refresh.