How did you plan your application architecture? How do you split your focus between ease of management and ultimate scalability? Are they mutually exclusive? Microservices have demonstrated an ability to deliver flexibility at scale, but controversy continues as teams weigh their benefits against the traditional monolithic structure that has reliably proven to enable rapid development.
Monolithic applications are relatively quick to stand up, easier to deploy and are well-known entities that most tools, frameworks, and IDEs are designed to deal with. The weaknesses of a monolithic application tend to show as it matures. Even the most carefully modularized application has the potential to stifle continued development and onboarding of new engineers as it increases in size and age. In this industry engineers come and go and each wave will add new interdependencies and then leave; taking critical and intimate system knowledge with them. Frameworks and style guides are built specifically to help address this, however the pressure to deliver inevitably leads to technical debt. Large applications can become a burden for deployment, as one change anywhere in the application requires a full rebuild and size impacts time to deploy. At the point where the team identifies a new technology that can provide features and functionality more efficiently, it can be a seemingly impossible task to take your mature product and migrate to a new platform. Despite these downsides, many companies continue to rely on the monolithic model and successfully extend their applications with varying levels of efficiency.
Microservices are exciting and seemingly fresh, but not new as a design concept. Opinions vary on how big a service should be, ranging from 100 lines of code to mini monoliths. General practice has shown each service should be specific to a single feature or function of the business. By separating functionality it is easier to understand how each individual service operates and new developers can quickly come up to speed on any particular one. Deployments become less cumbersome as a code change means only deploying the appropriate service(s) and each service can be scaled independently as required. The team can also leverage the technology appropriate for that specific function and migrations between technologies or frameworks are much less complex than monoliths. With the proper approach, another major benefit is improved fault isolation since a failure in a single service will not bring the entire application down.
Microservices require a different kind of commitment from the engineering team in how they orchestrate, maintain, and extend an ecosystem of services. By breaking an application down into components you have more moving parts and this complexity requires additional monitoring and management. Each service will require a monitoring and logging solution that will enable the team to assess performance and proactively address issues. Tools need to be built or configured to handle service replication as well as discovery when individual services scale up and down. Messaging layers will likely be required to address network latency and allow for true independence.This means more work for the devops team, as they have to manage a much larger ecosystem. If you are a smaller startup that doesn’t have a devops team this additional overhead will slow delivery. Good engineers are good at delivering code, but documentation and testing are organizational behaviors that are harder to uphold and maintain. Microservices necessitate high quality documentation and sufficient testing or implementing features that require communication between services becomes complex and fragile. All applications should be properly documented, but monolithic applications leverage frameworks that at least provide a base layer of documentation.
Like many technology decisions, the choice is not clear-cut. The architecture appropriate for an organization comes down to assessing the application, the team and even the organization itself. Your stakeholders and product team will be looking for the fastest path to delivered features. This generally encourages a monolith-first approach given the reduced application overhead for documentation, testing, infrastructure and orchestration.
Most articles provide answers for well-established teams on how to move to a service based application. The general assumption is the engineering team is of appropriate size to assign cross-functional teams as owners of each service. The application can be decomposed by identifying features that are not mission-critical and building them out into services first. From there, application capabilities can then be broken out one at a time. Organizations that support several mature applications can also implement microservices to handle shared functionality and cross-monolith communication or events as a first step. The intent is to DRY your codebase at the application level.
What should an engineer do for a project that is just beginning? How can you prepare for a microservice-based architecture without taking on the additional overhead up front? One strategy that has worked well is to start by decoupling the application from the database. By creating an API service that interfaces with the model a developer can become comfortable writing APIs and properly documenting them, while assessing the latency inherent with a network service. Additional advantages include having the foundation prepared for additional clients (i.e. native mobile), maintaining a relatively small footprint early in the product lifecycle, and retaining a quick development cycle.
As your application, funding, and team grow you can build larger features as fully independent services. Depending on the application they can be injected between the data service and the main app or built with their own database. With further development you can break the remaining capabilities out as additional services that independently scale and be placed behind an API gateway to centralize communication between clients and services.
There is no single best strategy for architecting an environment of microservices as requirements of the application will determine the ideal infrastructure. Defining best practices early in the application lifecycle is extremely beneficial to smooth the switch from the MVP monolith to microservices. Onboarding engineers into a team with a culture that supports the care and feeding of microservices is easier than shifting the thinking and operation of a well-established team. At each phase of growth, careful consideration should be made to ensure the scale of the application does not exceed the scale of the team.
About the Author
Tom Overton has leveraged full-stack technical experience to run engineering teams for companies including Technicolor, VMware, and VentureBeat.