Keith Donald reports that Spring’s architecture contains not a single dependency cycle. I’ve looked at the structure of many open source projects and generally found them riddled with dependency tangles (junit is an exception – perhaps unsurprising given its small size). So I loaded Spring into structure101 (structure101.com) for a closer look. And sure enough, no package-level tangles dispite it’s reasonable size of about 70KLOC (estimated from bytecode) divided into 139 packages.

I was half expecting to find tangles introduced at the meta-package level since often only dependencies between leaf packages (i.e. the ones that contain classes) are monitored. But no tangles here either.

So then I looked for any package-level over-complexity. Just one package comes on the radar – package org.springframework contains 95 inter-package dependencies:

Springframework_package_dependencies_3To me this is a bit on the complex side – we usually recommend keeping the complexity of packages to 60 dependencies, so personally I would divide this package into 2 more-or-less equal sized ones for a "perfect" package structure.

Finally I took a look at the complexity of the code below the package level. The rationale here is that it’s no good having a simple, acyclic package structure if the packages contain excessively complex classes or methods – this just moves a complexity problem to a different place. Here I did find some over-complexity – this is an exerpt from the strucure101 summary report, with the class complexity threshold set to 120 (inter-method dependencies) and the method complexity (cyclomatic) set to 15:

Fat (class): Top 5 (of 7)

Item Value
org.springframework.orm.hibernate3.HibernateTemplate 188
org.springframework.orm.hibernate.HibernateTemplate 157
org.springframework.beans.factory.support.AbstractBeanDefinition 141
org.springframework.jdbc.core.JdbcTemplate 136
org.springframework.jms.core.JmsTemplate 134

Fat (method): Top 5 (of 26)

Item Value
org.springframework.jdbc.core.SingleColumnRowMapper.getColumnValue(ResultSet, int, Class):Object 38
org.springframework.beans.PropertyEditorRegistrySupport.registerDefaultEditors():void 37
org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.createCustomException(String, String, SQLException, Class):DataAccessException 37
org.springframework.orm.hibernate3.LocalSessionFactoryBean.afterPropertiesSet():void 34
org.springframework.beans.BeanWrapperImpl.setPropertyValue(PropertyTokenHolder, Object):void 30

(Again, the amount of over-complexity ("Fat")at these levels is way less than would be usual for projects of this size).

Refactoring to remove the over-complexity for these items will most likely have a ripple effect. Some other packages may now become overly-complex and will necessitate adjustment to the package design. But once completed, the structure would be fully balanced and to my mind "perfect".

Serious credit to the Spring guys for building possibly the best structured code-base in the world!(Other than the Structure101 code-base of course :-)). Now, a question to the Spring developers and users – what benefits do you get from this great structure?

14 Comments

  1. Christopher Sahnwaldt

    I read quite a bit of Spring source code in the last few months, and I must say it’s magnificent. At first I thought it was over-engineered (some class hierarchies are very deep), but it isn’t. Whenever I think ‘well, Spring does ALMOST what I need, but not quite’, there’s usually a class in these deep hierarchies that I can extend, override one method – and voila. With most other projects, stuff like that can only be done with a good deal of copy and paste.

  2. Richard Rodger

    I guess I’m going to have to sit down and review the Spring source now as a learning exercise! I’ll add that to my to-do list… thanks Chris! 🙂

  3. Chris

    Christopher, that is what we find also – it’s basically “agility”.

  4. Davide Baroncelli

    Amen. This is the kind of feeling that good open source projects give: I had it also with freemarker, which is very well thought out and gives very smart extension points.

  5. Chris

    Now that’s interesting. I took a quick look at freemarker and it is structurally very complex! For example there is a single tangle of 176 classes spread out over 10 packages. This means that every class or package in the tangle is directly or indirectly dependent on every other.

    I’m guessing that the extension points are well designed and localized which is why you find it easy to customize (can you confirm this?). However I would expect a team updating the whole code-base to find it very hard to modify, since they would constantly need the latest versions of code that others were working on.

  6. Antranig Basman

    Yes to Andrew’s comments. This code analysis of Spring is excellent, although it tends to miss complexity at the actual source-file level. I agree that Spring is the best open source codebase I have ever worked with, but it has a number of “fat wodges” of bulky stateful code, in particular AbstractAutowireCapableBeanFactory is a particularly bad offender, and its tight relationship with BeanWrapperImpl is exactly what prevented us from working with Spring directly when we needed a *much* faster implementation – RSAC attaches to Spring after having read the context definition but after this switches to a ground-up rewrite to actually operate the context. Naturally it sacrifices some of Spring’s flexibility but most of this is typically only required in the longer-lived, enterprise-scale contexts at which Spring is based.

    I would criticise the validity of some of the complexity metrics, since for example the Spring Hibernate classes are not “complex” in the conceptual sense since they are actually fairly “wide and flat”. Whereas AACBF *is* really quite complex and coupled to the general “Spring internal workflow” in all sorts of ways, but has not appeared on the metrics radar at all.

    Spring’s isolation at the BeanDefinition, BeanFactory and ApplicationContext level is exemplary.

    You may want to look at the RSF source code base for comparison – its unit of composition is even more finegrained that Spring’s, and naturally will have no dependency cycles at the class level since it is configured using Spring.
    In particular the average source file size in RSF at 56 lines is *massively* lower than in any other framework I have seen (compared with 155 for Spring, and ~200 for JSF and Wicket).

    RSF is much worse at the package level since I have made very little effort to remove cycles at that level. Any suggestions for refactoring would be most welcome 😛

  7. Andrew Thornton

    There are only a few problem’s I’ve ever had with Spring’s internal structure:

    1. If you want to understand what ApplicationContext is doing, you’ll hit lots of calls to self-referential Interfaces and a long tree of inheritance.

    2. BeanWrapperImpl: One of these is created per-property/construction setting. Just look at what happens during the creation of one of these objects. There simply has to be a better way…

    Apart from the above, Spring is easily one of the best written pieces — if not the best written piece — of software I’ve ever looked at.

    (Note to the above: We only spotted the above problems when we were looking at creating RSAC (a request scope AC))

  8. Chris

    Controlling the structural complexity of a project won’t in itself guarantee that the class/code-level decisions are optimal. Experience and Fowler-esque refactoring are still just as relevant. For a given implementation approach, however, a better structure will make the code-base easier to understand, change impact will be more localized, and development, test and release processes more efficient.

  9. Chris Murphy

    If you are interested in analysing open source code bases generally then take a look at http://www.strandz.org. You won’t find any dependency cycles in it – JDepend has always been used to ensure this. It would be interesting to know if someone could relatively easily pinpoint some areas that need improvement. I must admit I have never really paid too much attention to the more detailed JDepend metrics (although I have read Robert C Martin’s book), although perhaps I should!

    https://sourceforge.net/projects/strandz/

  10. Chris

    Ok Chris, I’ll put that on my list ;-). I have worked with Bob Martin’s DMS metrics a fair bit and I’d say that they are often good indicators, especially at the macro level (like “components” rather than “packages”). In fact I’ve tried to use most “quality” metrics, and I keep coming back to the view that, for the vast majority of code-bases, the highest priority is to reign in the structural complexity. Once you’ve balanced the complexity, then it may make sense to consider Abstractness vs. Instablility or look at the OO metrics or afferent and efferent coupling or whatever. But until your structure is under control, you’re just rearanging deck chairs.

  11. Paul Browne - TIPE

    It’s good to see that the ‘gut feeling’ I had about the quality of the Spring Framework being validated by some hard numbers.

    As an aside , is there a community version of your product (maybe without all the graphical reports, but enough to give an indication of the code complexity)?

  12. Chris

    Likewise, it’s good that your experience matches the complexity numbers!

    No community version at the moment. There is a separate publisher however. With just one of these, you can “measure” as many projects as you like and then compare projects and track complexity over time through a web browser…

  13. Slava Pestov

    When evaluating a piece of code, you have to consider the language it is written in. Nothing written in Java can qualify as elegant or well-structured, because of Java’s grave design flaws. The best you can say about a Java project is that it uses standardized copy and paste boilerplate (“design patterns”) rather than something ad-hoc.

  14. Chris

    Slava – I take your point, though my post refers to a different aspect of a code-base. The “structure” I talk about is a level above the language specifics. How is the code recursively organized into higher-level components, and are the inter-component dependencies nice and orderly? A clean structure in this sense does not imply elegant code-level design, nor vice versa.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.