Sonntag, 19. Februar 2012

Legacy Code Retreat

Yesterday I attended the first german Legacy Code Retreat in Bretten. The event was organized by Softwerkskammer, the german software craftsmanship community.

A legacy code retreat doesn't work like a common code retreat where you implement a certain functionality again and again. It instead starts with some really flawed code and the participants apply different refactoring steps to make it more testable and maintainable. There are six iterations of 45 minutes with different tasks or aims. For each iteration you work with a different partner and after a short retrospective with all participants you mostly start again from the original code.

The github repository for the legacy code contains the code in several languages among which are Java, C++, C# and Ruby.

Iteration 1

The first iteration was used to get to know the functionality of the code. There were no real rules so the participants were free to explore the code in any way they liked.

I paired with Heiko Seebach who I already knew to be a Ruby guy. We were looking at the code with a standard text editor, already quite unfamiliar to standard Java IDE work. I already got enough Ruby knowledge to understand code when I see it so this was no problem. For quite some time we tried to understand a certain aspect that was happening when running the code. It turned out that this was a bug in the Ruby-version of the code. Next we tried to setup RSpec and get starting with some tests.

During this iteration I didn't learn that much about the legacy code but more about some Ruby stuff.

Iteration 2

The target of the second iteration was to prepare a golden master test that could be used during all of the following iterations. The original legacy code is triggered by random input (in the Java version using java.util.Random) and writes all its state to System.out. We should capture the output for a certain input sequence and write it to a file. This can then automatically be compared to the output of a modified version. If both files are the same there are likely no regresions in the code.

I paired with another Java guy and we were working on my machine in Netbeans. I noticed how unfamiliar I am with standard Netbeans project setup as I am using Maven most of the time. We were doing the test and started some refactorings, all in all a quite productive iteration. Things I learned: java.util.Random really only uses the seed for its number generation so if you are using the same seed again and again you always get the same result. Also, when doing file stuff in plain Java I really miss commons-io.

Iteration 3

In Iteration 3 we were supposed to use an antipattern for testing: Subclass to Test. You take the original class and overwrite some methods in it that are called from the method to test.

It turned out that the original code is not suited well for this approach. There are only few methods that really rely on other methods. Most of the methods are accessing the state via the fields directly. Me and my partner therefore didn't really overwrite the methods but instead use an initializer block for prepareing the state of method calls. This is similar to an approach for Map-initialization that I started to apply only just recently:

Map data = new HashMap() {
{
put("key", "value");
}
};

The approach worked quite fine for the given code but it's probably true that the tests won't stay maintainable.

Iteration 4

Iteration 4 was based on the previous iteration. All the methods that have been subclassed for testing should be moved to delegates and passed into the original class using a dependency injection approach.

I paired with a C++ guy who is doing embedded stuff during his day job on his C++ code. It turned out that we had quite different opinions and experiences. He was really focused on performance and couldn't understand why you would want to move methods to another class just to delegate to them as you are introducing overhead with the method call.

I haven't done any C++ programming since University. Eclipse seems to be suited well for development but compared to Java it still seems to lack a lot of convenience functionality.

Iteration 5


On Iteration 5 I paired with Tilman, a Clean Code Aficionado who I already knew from our local Java User Group. We were supposed to change as many methods as possible to real functions that don't work on fields but on parameter values only.

A lot of people were struggling with this approach at first. But it turns out if you are doing this you have a really good starting position for doing further refactorings more easily.

My partner was doing most of the coding with some input from me. We were taking some directions I wouldn't have taken by myself but the resulting code was really well structured and could be reduced in size. Also we worked with an interesting Eclipse plugin I had seen before already: Infinitest always runs the tests in the background, no need to run the tests manually. Have to check if there's something like this available for Netbeans as well.

Iteration 6

To be honest, I don't know what the goal of the sixth iteration really was. I was pairing with a developer that was still fighting with the failing tests from the previous iteration. Most of the iteration we tried to get these running again. In the last few minutes we managed to extract some clases and clean up some code.

Conclusion

The first german legacy code retreat really was a great experience. I learned a lot and, probably even more important, had a lot of fun.

The food and the location both were excellent. Thanks to the organizers Nicole and Andreas as well as the sponsors for making it possible. It's great to be able to attend a high quality event totally for free.