In part 1, I described how my experiences using both Groovy and Java delivered business value to the customer by letting the project team work with code that closely reflected the domain. We found the new system didn’t result in a “maintenance nightmare”, but rather made maintenance much easier than if only Java had been used.
In this post, I’ll address the “refactoring nightmare” as described by Bill Burke in “Polyglotism is the worst idea I ever heard”. In that post, he states:
I’ve seen these type of statements in blogs dating back to 2004. Let’s take the hypothetical one step further: What happens when I use Java to build out an extension to my Java app and the library doesn’t live in the same project or project workspace as my extension? OOPS!!! There are all kinds of hypothetical scenarios that we can imagine to scare ourselves into not using this or that technology. It is more insightful to see how actual project experience can substantiate or remove the fear, uncertainty and doubt.
I do think it is reasonable to expect refactoring could be different with a dynamic language in play. The lack of tooling was one of the major concerns introducing Groovy into our project. Personally, I was used to coding in Eclipse and was very comfortable with the tools it provides. I couldn’t imagine not working in Eclipse so I expected the worst. However, reality was different from expectations in ways I never anticpated.
When the project started out, I made sure all of my Groovy code always explicitly declared types. I also created interfaces following Java best practices. As a programmer who had been using Java since 1998, I was willing to accept Groovy but I was going to make sure our team followed “good” programming practices. I didn’t want our team to get too carried away with this dynamic stuff. I was quite pleased as the project proceeded quite well for almost nine months without any major refactoring required. Then, the inevitable unforeseen change in requirements appeared that was going to rip its way through most of our existing code. At the time, I only had JEdit, Ant and JUnit at my disposal. This change resulted in my spending a long, exhausting weekend refactoring the code. Luckily, our team had almost 100% code coverage between our unit tests and integration tests. Without those tests, I’m sure it would have taken weeks to make sure the new changes were correct.
The insight gained from this long refactoring weekend was completely unexpected. If I had not coded to interfaces and had not included types on all the method signatures, the coding changes would have been minimal. Our team did a really good job of writing lots of small methods that operated on a few properties at a time. It became obvious that the successful execution of these methods didn’t depend on the objects containing the properties.
Consider the following example: A method might look up a rate adjustment in an insurance rate table based on the gender and age of the insured. The only things that mattered were the the gender, the age and the lookup table. It didn’t really matter if age and gender were part of a Person object, an InsuredPerson object or a hash map. With Groovy and duck-typing, as long the object passed into the method had the properties “age” and “gender”, the method could do its job.
I kept my eye on this going forward and realized that as objects with properties flowed through the system, it didn’t really matter what the objects were. As long as the objects passed into the business calculations had the expected properties, we got the behavior we expected from the system. Thus, my experience for our system over the next two years was that we had an easy time of refactoring when we avoided specifying types in our system. Never again did I waste a whole weekend trying to refactor our system to accommodate a change in our object model.
I have given presentations about this project many times during the last two and half years and this concept is always the hardest thing for the audience to comprehend. I don’t think its something that a person can craft an argument for and expect to have everyone nod in an agreement. There are some things that just have be experienced to be understood. I am certainly willing to grant that I may be the lucky recipient of the perfect problem domain, programming languages, and programming team for this to happen. However, I have had similar experiences doing other, smaller projects in Groovy and Java.
In summary, on a large polyglot project using Groovy and Java, I didn’t experience a “refactoring nightmare”. I found that within a given coarse-grained component, if I (a) removed all types, (b) only used interfaces sparingly, and (c) had a dependable test suite, the time spent refactoring became a non-issue for my project. How does this relate to delivering business value? Time spent refactoring is time spent focusing on technical issues, not business issues. In our regular meetings with our business experts and project manager, the team stayed focused on working through the business logic and not getting sidetracked on technical issues that don’t deliver business value.
In the next segment, I will focus on my experiences as they relate to the “installation nightmare”.