Why developing complex applications using monolith architecture is not a good idea
It is a known fact that the customers have high expectations from businesses, and they expect the businesses to accommodate their high demands, while remaining flexible. Businesses need to be very competitive to accommodate these requests, and not just that but are the first to market to get that unique advantage! We need software organizations to be agile, user-friendly, innovative, competitive, listen closely to respond quickly, able to conceptualize different solutions and deliver them to meet the reality, drive value and results for the businesses. Basically, the customers want the businesses to be on steroids! This is no longer a wish for the businesses, but a necessity today.
Being in software development organizations for over a decade, I have understood that not everything can be done at once or there is no magic pill. But, we must take an incremental approach in embedding this competitive steadfastness in the design/development teams. Once we know that we are building the right thing that the customer needs, Agile and DevOps framework offers a lot that can be done process wise and to instill a mindset of continuous delivery. Another aspect of software development which doesn’t get as much focus is the architecture of the product. We cannot deliver value frequently to delight the customers unless the underlying architecture enables us to do so.
Once “you” are convinced that a new architecture, in this case, microservices-driven-architecture addresses most of the concerns posed by the current architecture, the most important stakeholders who need to buy into this decision for using or changing the architecture are the design/development teams. Because on an average, teams make a better decision than any individual or the executive. Because the teams think as a collective wearing many hats. Their diverse skills and perspective make them very effective at making decisions. So, based on this discussion with your team you might consider moving on to the new microservices-driven-architecture or be prepared, for you may revisit your decisions
How would you get a team buy-in to change and adapt to the new microservices-driven-architecture?
- Expose the problems with the current architecture your teams are facing
- Educate them on the advantages that microservices architecture can bring
- Do not forget to go through the trade-offs you would have to make with microservices architecture
- Most importantly, let them make the decision!
In this article, I would like to go through the problems associated with developing applications using monolithic architecture.
Firstly, go through some of the basic assumptions. We did have a strict compliance of technologies/3PPs that can be used, so that helped (maybe a bit). Some of the other assumptions include: The framework that was decided was JEE and the database choices made, that must be adhered to by every application.
Figure 1: Simple layered monolithic architecture
The products that my teams were working on was built on a monolithic architecture. Though it was built as a strictly layered architecture as depicted above in the figure, the interaction between the layers was not always smooth.
These were some of the major problems that the architecture exposed:
1. Code- Duplication
Though we started off as a nice small product with the best spirit of software craftsmanship, the architecture let us down. Because as the product started gaining momentum, there was more demand for more features and obviously the executive management decided to add more design teams to roll out the features. This led to overlapping requirements and duplication, which led to teams stepping on other components boundaries. At times we could see the same feature developed by two different teams that essentially did the same thing but invoking different APIs. This was more evident when the teams needed access to the same service for their features, for instance: when teams wanted a query to fetch something from the database and the database layer didn’t provide a common API yet, this led to teams developing this local API themselves.
Such invocations made it difficult for the database team to optimize the queries or work on providing common APIs for other features because there would almost always be a huge inflow of support tickets/defects/query improvement requests on the team.
With more teams and more features came a variety of defects that had to be handled by the existing teams. And often the ownership of the code led to a lot of resistance from the teams: to own the defect and fix the issue, let alone implementing certain preventive measures. This led to our defect backlog slowly started to increase. We couldn’t meet the SLA (Service Level Agreement) for the defects, which made the customers unhappy. So, we had to lower the number of features delivered to enable the teams to work on the defects. Which further made the customers unhappy. The team's metrics took a hit, because of the defects. To enable teams to debug, more logging was added which consumed space, which meant that we could hold the logs only for a small time before it was cleaned up. So, the teams had issues trying to understand the underlying problem and diagnose. Most of the defects started to get closed for not being able to reproduce. We were just waiting for the problem to reoccur again before we could do anything else.
Because of more context switching between the different defects, and between the defects and the MR. the defects were overwhelming, and this eventually led to the delay in the project release.
The Continuous integration/Continuous delivery loops posed a problem primarily because it was not envisaged to be a machinery for the magnum of the product that it grew to become. So as the number of build jobs increased, the hardware gave up and the load balancing became a regular problem. And most of the jobs failed because the dependencies were not grouped together, or the jobs were not triggered in the right order. Since the entire application was packaged as one unit, the built times started to increase. So, by the time design teams got feedback, it was too late to debug. A massive amount of time was spent on troubleshooting why the build failed, and most of the times, it turned out the problem was the dependency on another application which had introduced a backward incompatible 3PP or an API. The governance of the features lined up for the build took a lot of time.
4. Non-Functional requirements:
The product my teams developed were deployed on JBoss containers and the product itself was built on JEE architecture. As the size of the product grew, there were persistent issues with robustness, scalability, stability, and the availability of the product. We could no longer just accept the default tunings that we could have leveraged from JBoss, but each application had to do tuning and profiling. And when they looked independently at their feature, they would overutilize memory, for instance, depriving other applications on the same instance of memory and the application failed with an out-of-memory exception. Each time there was delivery, the product became more susceptible to failure in the non-functional aspects.
5. Development and Test environments:
The development environments could no longer keep with the product growth, and it became a limitation for the development in terms of availability and stability. Every step towards scalability meant a few steps back in terms of development and testing. There was no way the development environment could accommodate running the entire product to enable development. So that meant the designers had to rely on a centralized hardware for testing and ensuring their code didn’t have a negative consequence on the system. This meant more lead time. The user story cycle time and the SLA of the defects extended from days to weeks as the dependency on external hardware resources increased. That implied that the resources had to be contained in the team to help them be autonomous. The lack of having a good development environment led to poor quality design code and tests. Because in most cases, the unit tests had a lot of mocking and this led to an increased time in test loops and a lot of manual testing efforts. Sometimes the integration tests became complex too to accommodate huge dependencies.
6. Adopting new technologies:
With monolithic architecture, we always had to rethink when upgrading the 3PP or the platform to a newer and a better version. Though the upgrade meant that most of our problems would go away or at least be reduced, the cost involved in migrating or upgrading was huge. This would take sprints/months together to just to a small upgrade of the JDK. The major upgrades of the databases or the Linux platform were unthinkable. This was a disappointment to the designers because though they solution at hand and was well tested in the software industry they couldn’t do anything about it. Everything had to be done right the first time, there was no scope or very limited scope to do it again the second time.
7. Communication & Coordination:
As the number of design teams increases, the number of communication channels increase.
The total number of communication channels is calculated by n (n – 1) /2, where n represents the number of stakeholders, in this case, team members or teams. For example, for a team of size 8, the number of potential communication channels is 8 (8 – 1) /2 = 28.
This tells us the importance of maintaining a consistent and a uniform communication across all the stakeholders/ teams. This becomes increasingly difficult when the product is developed across geographically distributed teams. To enable this and early prevention of duplication, a lot of meetings must be embedded in the process of development, the likes of technical coordination meetings, Product owner coordination meetings, dependency coordination meetings, scrums, a scrum of scrums, executive summary meetings etc. So, the productive time for development or doing actual work does get shorter.
A lot of effort should be spent in getting communication correct and consistent over the geographical and cultural borders. A better way of working need to be formalized and established for all teams to follow to ensure a common way of working.
Well, the monolithic architecture so far exposed:
- The problem of duplication of code
- Core component teams becoming the bottleneck
- Lack of communication: Of course, the new addition of teams warranted a need for a different structure to handle communication, manage projects. Having a new structure in place is a partial fix. The underlying problem had to be addressed
- Increased frustration in the team.
- Backward incompatibility between APIs and the 3PPs
- Failure to keep up with the non-functional requirements.
- Inability to adopt new and better technologies
This had further knock-on effects like:
- Delay in delivery
- Lack of customer satisfaction
- More micro-management trying to help- but doing otherwise
- Lack of pride in the product which was demonstrated by the lack of ownership and leadership
- Blame culture
- Not enough sharing of knowledge
- Waiting on last-minute fixes
- Lack of quality
- Delay in the release
- Governance/gatekeeping of the applications/features
- The complex way of working
Basically, it was a downward spiral. We gave rise to a new problem every time we wanted to solve an existing problem.
So, if any of the above points seem familiar in your development experience or in your teams, the major issue the underlying architecture.
I don’t imply to say that these problems will not arise if we are to embrace microservices-driven architecture, but the extent of the problem is on a smaller scale as compared to a growing monolithic product. The aspects like bringing in a uniform Way-of-working, governance should still be tackled irrespective of the architecture, but not at the level to which you would have to if on a monolithic. To clearly understand and make an informed evaluated decision, we need to know the advantages or the pros of developing applications using microservices architecture and the disadvantages associated with that.