A couple of days ago on the Twittersphere I saw a blog post by Sam Atkinson called Why I hate Spring. This blog entry was written well over a year ago in 2014, but then DZone actually picked up on it and published it. Atkinson is currently travelling around the world, so he must have been very surprised at the social media attention.
Mr Atkinson's article touches on several interesting issues in current Java enterprise application design: XML configuration and compile time safety, magic incantations, imports of other Spring application context files and software complexity. I will briefly examine each of these in this blog post.
I was not surprised by XML configuration, but then J2EE 1.3 had horrible XML EJB mapping files as did Hibernate 1.x prior. Eventually, Java SE 5 annotations and the Java EE 5 specification helped to change things. The ideas of CONVENTION-OVER-CONFIGURATION from Ruby on Rails helped to move Java onto the next phase. Spring Framework was earlier to the OSS market with workable dependency injection container implementation in 2002 and 2003.
At the time J2EE 1.3/1.4 was a horrible mess of container managed session beans and the deranged concept of entity beans. There was no standard DI framework, and Spring’s competitors were PicoContainer, the much older Apache Avalon framework, and even Struts 1.x framework. After 2006, Java EE came along and provided Context and Dependency Injection (CDI) 1.0 with strong type safety at compile time. CDI was too late for thousands of businesses that adopted Spring Framework and at the time most of them where battling with migration from Java 1.4 (J2EE 1.4) to Java 5 and/or 6. The biggest issue ten years ago was keeping mission critical applications running in either a WebLogic Server 7/8 or IBM WebSphere 5/6 application server. So the "state of the art" was already broken for several years. Spring Framework had its own DI container, Java EE 6 also featured a DI container, and yet never the twain shall meet.
When it was first conceived, DI was already mind-twisted even way back in 2003 and it took a lot of thought to understand the common issues of object-oriented systems. Chief amongst them was the testability of applications and selection of alternative implementations for Java objects. Giving up control of the instantiation of objects to a foreign framework was very unusual at the time. After the water had broken the levy, every engineer (then) pushed classes and implementations over to the framework, which may be a mistake that we pay for now in the present. The magic incantation of which beans are injected (and at what application layer) was great when the software works, but it was a nightmare to figure out (as Sam Atkinson opined) when you were chasing a bug and refactoring some other team’s set of dependencies with Spring Framework. Add the usual PEOPLE-WARE issues of BIT-ROT and inside investment banking culture of software development with the problems of SILO DIVISION engineering, and Spring Framework and other application frameworks were always going to lose at some point.
Sam Atkinson alluded to the typical LAYERING issue of large application code bases, especially when the source code is split over hundreds or thousands of engineers, testers and architects inside organizations. Spring Framwework already had peculiar features since version 1.0, which modularises application context files by placing different bean definitions inside different Maven projects. The concept was great to keep bean definitions close to the module that defined and used them. Maybe this philosophy was great for applications with few Maven projects. Once the organization defined a hundred projects with bean definitions, this trick became a nightmare to control. [Let’s not forgot that is the state of the art now.] Combined with magic incantation, layering, and delegation of multiple Spring beans in scores of related application contexts, these issues probably did cause Sam Atkinson a massive cognitive overload. However, this is not strictly a Spring Framework issue, but rather the application of EVERYTHING IS A NAIL.
Finally, software complexity is the bane of many businesses and the effects of building a sufficiently large application and having to then to decipher it, distill it down, and eventually replace it might lead to people having a coronary. Perhaps the biggest complexity inside Java is the modularisation of the JDK itself by Oracle and Sun Microsystems, and that one does not use a dependency injection container at all. Atkinson talked about the probable fallacies with Spring Boot as a framework around a framework and being possibly dangerous. He is correct in this idea, because Java EE does not (yet) have a standard API or JSR for fully embedded application server infrastructure. [Antonio Gonclaves and others, including myself, have been pleading for such an API to be brought into existence.]
There are uncertain paths with the likes of WildFly Swarm, because your tool chain and development mechanics might not support you all the way. For example, your IDE may not be able to achieve Hot JVM class reloading or be able to contribute much to front-end page content changes. These so-called container-less solutions rely on the concept of applications that already are modularised into separate components. If your application is a humongous behemoth, then turning to an embedded application server application is not going to help in any way. You have serious work to do before reaching the first rung of the ladder of microservices, such as attempting to unknit the spaghetti projects and Maven dependencies within your own organization. The failure is to understand how a large scale Spring Framework application is just the symptom of the illness and not the diagnosis.
Why are we going to lose? Or maybe the question is why are we losing now? The hardest problem inside software engineering is figuring out what to do with legacy software and dreamscaping. Most engineers know something about the concept of legacy software and technical debt. It is tremendously hard to write applications that are bug free, flexible, agile, and robust with supreme maintainability. Most technical senior management have either forgotten or do not believe in the effects on legacy.
Then, there are the dreams that recruitment companies or businesses sell to us, the engineers, designers and architects. Most of the nine to ten million Java developers are the constant baits of the so-called greenfield. Unless you work in an early startup company, there really is no such thing as this humble green lawn. Even when an existing business spawns out a skunkworks team and promises that you will not have to work with legacy, in a couple of weeks or months, guess what? You will hit the wall of integration between the new fangled system and the older legacy system.
At the heart of Sam Atkinson's passionate cries of frustration with Spring Framework, the fault is in the architectural design of the applications. He, himself, said that he did not have time to train, coach and/or mentor other developers inside his organization, because of that older directive time-to-market. The moral of the story is that regardless of the technology we use, time pressures and good old human nature is going to seep into our application. We can attempt to achieve 100% code coverage, write the best functional tests with Cucumber, JBehave, and we will still have broken applications in the years to come. Software is software. We lose most of the time, and sometimes we win. It’s just extremely difficult when, where, and how we achieve this grand ambition.