Your Monolith Is Not the Problem
Organizations want to move fast, and they are often told that they need to “modernize their architecture” as a solution; and that usually means converting their server-based product to a microservice architecture.
But what does that actually achieve?
A microservice architecture enables server software to scale enormously—to hundreds of millions of users. That is why that approach was created in the first place. But it is a lot more complex to build correctly, and it will not necessarily give you agility. In fact, it could make you less agile.
Here’s Why
It all comes down to feature lead time: the time to conceptualize a product feature, create that feature, and then get that feature to users so that they can try it out and hopefully like it. That is technical agility, and it is a really important component of overall business agility.
But technical agility does not rely on having microservices. It relies on (1) the ability of people to work on separate things in parallel, and (2) the ability of people to independently verify that their respective changes work together. If they can do that, they can build and deliver entire new features very rapidly.
And there is no reason why one cannot achieve that with a monolithic software system.
Being able to work on things in parallel requires that you have access to the whole thing, so that you can change whatever you need to change without waiting for someone else. If developers have access to all of the product’s git repositories, they have that access.
The next requirement is that they can independently verify that things that they change work in the product as a whole. That’s a little more challenging because there are separate aspects to that:
A. Anyone can run any set of integration tests at any time.
B. Anyone can privately merge their changes with the most recent code, and then quickly do A to make sure everything still works.
C. The tests have enough coverage so that if the tests pass, one can be confident that everything still works.
And if one has on-demand provisioning of resources, they can do these things—even with a monolith.
Requirements for Technical Agility
A. Anyone can run any set of integration tests at any time.
B. Anyone can privately merge their changes with the most recent code, and then quickly do A to make sure everything still works.
C. The tests have enough coverage so that if the tests pass, one can be confident that everything still works.
D. Technical debt is not too high.
With a monolith, all the code is linked together and deployed as one runtime. But that does not mean that someone cannot do A, B, and C. Even monolithic code has components—they are just not deployed separately. But that does not mean that they cannot be tested separately (they can, and usually are), and it does not mean that one cannot test everything together before merging one’s changes into the shared copy: one can, and one should.
What DevOps Says
The DevOps community strongly advocates making the many components of an application independently deployable. But that is mainly for operational reasons: if the components are independently deployable, one can update them live more easily. If they are all part of one runtime, one has to deploy the entire application at once.
In a traditional environment, that meant taking the system offline, deploying the new version, testing it, and then bringing it back online. But with today’s cloud environments, in which one can deploy different versions of things and then switch traffic from one to another with one command, it is not necessary to take the system offline to deploy a new version—even for a monolith.
DevOps also tells us to make separate components independently testable. Each component should have a “contract” that defines what that component does, so that it can be tested against its contract. But the components of a monolith can—and should—have contracts too. So that really has little bearing on testability.
Monolithic applications do not scale as well, but they can scale pretty big. They can scale to millions of users. Not hundreds of millions, but millions. Your cost of operations will be higher than for a distributed application architecture, but not enormously so: think a factor of two to ten, depending on many factors.
What About Technical Debt?
It is not just about testability though. Over time, large software products tend to accumulate internal inconsistencies and loose ends. It is kind of like a house in which rooms have been added in odd places, and wiring changes were done in a quick-and-dirty manner. Over time, as these inconsistencies and loose ends accumulate, making changes is increasingly difficult: it is like the house, in which you now want to enlarge the windows of the family room, but there are now hallways attached at various odd points, and so you would have to move those, but they connect to rooms in unexpected places like the yard immediately beyond the family room. It is strange and bizarre, but that is indeed what happens to software over time when changes are hastily made. The result is that it is increasingly hard to add new features or make any changes at all. Programmers call this “technical debt”, and it can have a huge impact on agility.
So while your monolithic architecture might be the cause of decreased agility, technical debt might be. The only solution is to go back in and change things, to move those odd rooms to where they should be, and create a more open and flexible architecture. And while you are doing it, make sure that everything is highly testable.
Be Clear On Your Goals
If you expect to scale to tens or hundreds of millions of users for a server-based product, you will likely need to have a microservice-based architecture. A microservice architecture is a compromise: it sacrifices ease of programming for scalability. A monolithic application can utilize something called transactional integrity to greatly simplify things internally: microservices abandon that, and instead must have code to deal with many ways that things can get out of whack. But that compromise is necessary for huge numbers of users, because databases simply cannot provide transactional integrity at massive scale, especially when one factors in the latency of global operation.
But if you don’t expect to have tens or hundreds of millions of users, and your goal is agility—that is, being able to rapidly turn around new features—the architecture of an online product has little to do with it. Much more important is the coverage of your test suites and the automation of your deployment. Invest in those things, and focus on whether your developers can do A, B, and C above. And continually invest in keeping the technical debt low—don’t paint yourself into a corner from which you cannot get out.