WHAT IS A MONOLITH AND WHY IS IT IMPORTANT TO UNDERSTAND ITS IMPACT TO THE BUSINESS?
You may have noticed your engineering teams are too slow or your CTO has been complaining about the monolith and has recommended investing in breaking it up. He’s warned that it’s impeding progress towards the company’s desired outcomes. You likely have several questions, some of which may be:
- What exactly is a monolith and why should I care?
- Are all monoliths the same?
- What’s so bad about a monolith and are they all bad?
- Is a monolith ever a good thing?
The common monolith is simply a single codebase that represents the product all of your customers utilize. If you’re the CEO of a company producing technology products for revenue, then your product and its architecture are two of the most important things about which you should be concerned. Specifically, it’s important to understand the implications of a monolith on company growth, cost to develop, cost to operate and time to market. Despite the abundance of engineering opinions to the contrary, not all monoliths are bad and in some cases, they are the right solution. In other cases they can slow development, impact availability, negatively impact margins and in the worst cases negatively affect the growth of your userbase and business.
VARIATIONS OF MONOLITHS AND THEIR IMPACT EXPLAINED
What’s a monolith vs a distributed system?
Two key dimensions contribute to a monolith making it confusing to understand – physical and logical. Physical refers to the host or resource on which code runs and logical refers to the nature in which services (code or a codebase) interact to determine a result for your customers. Both contribute to the nature of the monolith. For simplicity, we’ll refer to a simple e-commerce example that has a storefront in a mobile app or on a browser along with account, inventory and shipping services.
Think of a monolith as a solution that has most of its functionality used by your customers all within a single codebase, executable (binary or interpreted code read by the computer) and process (a running instance) that runs on top of a compute resource (such as a computer or ephemeral compute solution). This definition of a monolith is deployed by your team as a single artifact and when it fails, it fails in its entirety with all functionality failing simultaneously for that one instance. Monoliths use a single database host and all communication is local. We refer to this as a typical monolith. (Fig 1) In Figure 1, Account, Ship and Inventory all are part of a single codebase deployed on a single host with one database that contains all of the data written and read by each service.
Fig 1 - TYPICAL MONOLITH
Monoliths of this type can be adequate especially in early-stage startups if boundaries between functions in the codebase are respected and great oversight prevents mashing everything together.
An early-stage healthy monolith (Figure 2) has boundaries for business domains that are explicit and result in aligned modules in the monolith seen below. It's still a single code base and is deployed by your engineering team as one. It is healthy because it's well organized with boundaries for account, inventory and shipping and it's much less complex to architect and operate for a single small team. Its healthy nature allows for an easier break up upon startup growth. The clear and maintained boundaries in the code base provide a shorter and much less complex path to separating different services or groupings of services for growth.
Fig 2 - HEALTHY MONOLITH - SINGLE CODEBASE WITH ORGANIZED BOUNDARIES
The opposite of a monolithic system is a distributed system. Oftentimes such an architecture is called a “services architecture”, a “services oriented architecture” or more recently a “microservices architecture”. These sorts of systems have independent components, separate codebases, potentially separate repositories (where the code is stored), and are each separately deployable. Ideally, they each interact with their own persistence tier or database, though that is not always the case. If developed properly such that the distributed components do not have dependencies upon each other, they will fail separately and allow the other components (aka services) to continue to operate. In Figure 3, each component or service is both logically and physically separated, they don’t depend on each other in any way, and each has its own datastore. Put another way, these services will have their own codebase, deployed on their own and run their own process on top of compute that includes their own dedicated datastore.
Fig 3 - DISTRIBUTED SOLUTION
Your CTO may have signaled that a microservices architecture (see other names s/he may have used above) will help address slow engineering velocity or system. This is true if and only if the implied services distribution strictly adheres to microservice architecture principles including:
- Each service or component is autonomous and does not rely upon other services or components. If there exist dependencies between components, then the failure of one component may cause other components to fail in the same fashion older “Christmas Tree” light strings would fail entirely with the failure of a single bulb.
- Each service uses its own database, and no two services share the same database. In the case of shared databases, the availability of all services connected to that database are subject to the operation of the database itself; when the database fails, so do all of the services.
Many times, engineering teams fall into the trap of either creating too many services or they wind up with a distributed monolith which can be devastating to availability and growth. Both scenarios are possible without skilled architectural oversight.
Distributed monoliths are physically distributed and logically services are tied together (represented by the blue lines) with no clear boundaries represented in Figure 4. Services call other services across boundaries and each service is deployed on its own own host. Distributed monoliths become difficult to troubleshoot and result in lower availability than a logical monolith. Both distributed and logical monoliths are bad and result in higher cost of goods sold and are error-prone.
Fig 4 - DISTRIBUTED MONOLITH
Transaction scale or engineering velocity is often constrained by a monolithic product architecture. Both negatively affect growth and both must be considered when breaking up the monolith.
Some common symptoms indicating a need to separate a monolith include:
- Difficulty achieving acceptable throughput (velocity) within an engineering team as the engineering team increases in size.
- Difficulty scaling a type of transaction (for instance ‘search’ or ‘checkout’) cost effectively as transaction volumes across the product grow.
- Difficulty adding functionality cost effectively or within short time intervals to an existing monolith.
When is it time to deconstruct a monolith and why must tradeoffs be considered?
We must break up the monolith before impact to business or engineering operations. Impact can slow growth and even stop growth. Often we see clients wait for impact to their business performance and then call us. Remember, breaking up a monolith provides resiliency, availability, and engineering velocity as growth of product usage and the engineering team grows. If done too early, costs may outweigh the benefits. If not done at all, growth will be capped and potentially reversed. Multiple indicators can be used to determine when it may be time to begin. Factors such as growth rate of customer-initiated events in the product, engineering team growth plans, or upcoming investment in feature expansion. All should be considered and inform design. In “CEO Guide To Microservices”, we further define when to separate a service. Developing and operating highly available systems that scale and are resilient come with a cost. The tradeoffs must be considered by your engineering team.
AKF helps companies at various stages – both those that are growing and those that are struggling to grow. Managing the architecture and monolithic states of your product are critical in both scenarios. Using our operational experience as CTOs and other engineering leadership roles we’ll apply our models that will help the engineering team navigate the planning and actions needed to appropriately break up a monolith. We would love to help. Contact us for help.