In this episode Eberhard Wolff speaks with Jürgen Höller, the co-founder of the Spring framework. Spring is a tremendously successful Java framework so they discuss the design of large frameworks and the issues that arise in the evolution. Jürgen explains the management of dependencies in the framework, how to structure such a framework, how to offer compatibility for the existing user base while evolving the framework, and the role of metrics during development.
I listened to the podcast and was intrigued by the discussion of “jazz” or “jass”, but see no links to it here and would like some more information.
Thanks!
-Ed
I think you refer to the Jazz project from the previous episode with Erich Gamma?
If you go to the previous episode, you’ll find a link there.
Cheers,
Markus
Juergen did not give much useful information on how to achieve backwards compatibility.
Perhaps the issue is hindered by java interfaces.
See what eclipse came up with =>
http://wiki.eclipse.org/FAQ_Why_do_the_names_of_some_interfaces_end_with_the_digit_2%3F
In the case where the modification to an existing contract is merely additional methods & NOT refactorings that would involve promiscuous removals/modifications of existing methods; don’t you think abstract super classes would be better? Will structural typing in scala help in this area ?
However I liked the part about using reflection to detect hibernate versions.
“If it quacks like a duck, it’s a duck”.
Regards,
Gavin Bong
http://raverun.com/jayway
Hello Jürgen, hello Eberhard,
I enjoyed listening to the episode especially after already having seen Jürgens presentation on that topic on infoq.com. I missed one question that we currently deal with. Spring heavily relies on 3rd party libraries, that of course require further libraries and so on. How do you manage to find a suitable version of transitive library Z if maybe library X relies on Z in version 1.6 and Y relies on Z in 1.8. You might drop in 1.8 and rely on backwards compatibility, but that does not feel quite comfortable to me.
Is there some managed process (Jürgenization – as I was told 😉 or at least some best practice?
Regards,
Ollie
Actually, Spring doesn’t really on third-party libraries that heavily: It’s only core dependency is commons-logging 🙂 That said, you’re of course right that Spring integrates with many third-party libraries; hence typical Spring setups do involve managing all those third-party jars.
Even if it sounds naive: Backwards compatibility is actually not that much of an issue with typical transitive third-party dependencies. Those transitive jars are usually Jakarta Commons libraries which actually do have a pretty good track record of backwards compatibility. So the simple rule would be to pick the highest required version of the transitive dependency and expect that to work for all callers.
It’s a bit harder with libraries like iText, used behind the scenes by e.g. JasperReports but maybe also used directly in the same application. However, in my experience, even those scenarios are not that big of a problem in practice. It usually just works. I warned you that it may sound naive 🙂
What we do in the Spring codebase in case of a non-backwards-compatible new version of a third-party library is a reflective check (yes, like the one for Hibernate!). This allows Spring to remain compatible with both the old and the new version of that library, despite maybe some API signature changes. We did that for some Quartz and iBATIS versions, for example. This is usually completely transparent to application developers; you’ll simply keep whatever version you picked. Other clients of such third-party libraries should show the same level of adaptability; however, that’s rare. Fortunately the need for it is rare too.
Juergen
Interfaces may indeed pose a problem with respect to backwards compatibility. What we usually do in Spring is to provide extended versions of established interfaces, with only those extended versions featuring the new methods. Take BeanPostProcessor and InstantiationAwareBeanPostProcessor, for example. Existing implementors do not have to care about the new callback methods then.
This only applies to interfaces that are typically implemented by application code, though. Framework APIs like BeanFactory and ApplicationContext do not suffer from the extensibility problem since new methods there only have to be implemented in the framework-provided implementation classes. Even if somebody customizes those, this usually happens through subclassing of framework-provided base classes.
So indeed, providing abstract base classes is technically a better choice for backwards compatibility purposes. However, providing extended versions of interfaces (as outlined above) may often be a viable alternative. And sticking with interfaces has a couple of positive side effects, like easier proxying (and decoration), clearer definition of individual roles, etc.
Juergen
[…] Episode 82: Jürgen on Organizing Large Java Code Bases […]