Welcome to the third edition of “Station Wagon Full of Tapes”. In this article I will focus on a different aspect of scaling for organizations; scaling teams.
The discussion around using service-oriented architecture vs. monolithic architecture has been around for a while now. Most teams do choose the microservices path since that’s the “industry standard” these days. However, monolithic designs still have their use and space, especially at an early stage of an idea or a product.
I had the luxury of working in codebases where both approaches were the standard. I am leaning for microservices. I have my reasons as to why in which I’ll share below. First, let’s talk about both architectural models.
Are they extinct? No, and they shouldn’t be. If you are working on an application codebase that can be grouped into a one package, deployed once, and can be duplicated behind a load balancer (horizontal scaling), then there is no need to introduce complexity of microservices design.
Monolithic design does not mean having single responsible service design is not possible, theoretically speaking of course. In reality, since all modules are easily accessible, the lines get blurry quickly enough over time, making it harder and harder to break the system into smaller pieces if need be.
In my experience, monolithic architectures have been faster to iterate early, getting slower and slower over time in terms of iteration speed of changes. This caveat makes monolithic architectures still a very valuable application development approach today for startups, smaller sized teams.
If all goes well, and now you are in need of serving high multitudes of requests per second (because you have so many new customers of your product), accurately, with %99.9 uptime the limitations of monolithic design start to show.
Airbnb had to go through this change — The Great Migration: from Monolith to Service-Oriented, talk by Jessica Tai, 2014, Airbnb Engineering.
There’s a common pattern of issues many teams face when they reach to a certain state;
Continuous deployment is painfully slow, since each change requires whole package build and new deployment.
Slow continuous deployment leads to slow continuous integration, leading to a lesser amount of tests being run after each change.
What once was a fast code base is now a minefield to make any small change, because no way for engineering to know their change’s impact.
Impossible to abstract out services that are specific to managing infrastructure, Database connections, management, schema changes are all coupled.
No way to use a scratch-like container image when deploying. (Although this is lower on the list of issues, it is dear to my heart considering my past work at Docker.)
So far in the article, I’ve been using service-oriented and microservices as interchangeable terms. They are the same thing, I believe, however microservices term does lead some to believe each service to be micro in size, which is not a requirement for an architecture of this style.
Most of the advantages are on the opposite side of the spectrum for what are the limitations of monoliths. Which is not a coincidence. This style of design of course is not just positives, there is an increased need for infrastructure design. Distributed systems are not easy. However pluses outweigh with service-oriented architecture;
Faster deployments, higher rate of tests being run after each commit.
Blue-green updates are a breeze (relatively speaking), limiting the down-time per each service.
Engineers are more confident as to what is the blast radius of their change since they are aware of the dependency graph of their modules.
Scaling is not limited to throwing more machines with duplicated monolith, but possible vertically.
One could already make their call by looking at the positives and negatives of each approach listed above. However, as I’ve mentioned at the top of the article, service-oriented architecture unlocks a scaling opportunity that monoliths don’t. An organizational scaling that is.
A good problem to have is when a product needs more than hundreds engineers to work on it. Blessing and a curse. Keeping all of your organization confidently nimble to innovate is really challenging with increased size of people touching the same code base. This is not to be confused with mono-repos. A mono-repo does not require monolithic architecture.
In monolithic architecture; teams are blocked frequently during code reviews, since it is easy to touch parts of the code that is owned by other teams. Any change done requires a full build, coupling teams with each other. If Team A has a failing Selenium test, why is Team B blocked from landing their unrelated service change? (They shouldn’t be.)
Each service owned by a team that solely focuses on that service and its consumers, can also have a bigger impact when it comes to building a robust testing infrastructure, and integrations with metrics and logging. Teams will feel more empowered to confidently deploy new changes because their boundaries are set clearly, the blast radius of something going sideways is measured as teams will also be able to measure everything.
This type of architectural design conversations are generally prefaced with back-end software development targets.
Front-end development also had a “recent” seismic change as to how they are architectured. At the core of it, just like microservices, they unlock an organizational scaling opportunity. That change was “component-based architecture” that became mainstream with React. Companies building their design systems not only are gaining speed on product development speed, they are also scaling their organization to be less coupled as a side effect.
When I am asked about which way to go with these two approaches, I generally tend to answer with an “it depends”, and get an unsatisfied look. With that said, the advantages of service-oriented architecture on your organizational scale is not something to overlook.
Thank you for reading “Station Wagon Full of Tapes”.