banner
Home / Blog / Don’t Build Microservices, Pursue Loose Coupling
Blog

Don’t Build Microservices, Pursue Loose Coupling

Sep 01, 2023Sep 01, 2023

By: Chris Bertinato on August 29, 2023

Given how demand for reliable application performance is always growing, it’s no surprise that many firms build their systems with future expansion in mind. Many industry pundits have pointed to microservices as the best way to future-proof a system—but it might be better to use the term “right-sized services” instead, as there’s no single strategy that works for every firm at every point in time. Rather than thinking about which of the latest trends to adopt as a strategy, teams should instead recognize and focus on the underlying factor of coupling. Systems are more flexible and dynamic if there is loose coupling between the components that are mostly likely to change.

Coupling, in the systems context, refers to how components of a system are connected. Interfaces are necessary for loose coupling. For electrical systems, those interfaces are implemented as physical connectors with pins and sockets and protocols described in terms of voltage levels. The software systems analogy to an electrical connector is an application programming interface (API), which could be implemented as a set of functions or HTTP-based resources, among others. But an interface alone is not enough for loose coupling. That interface must also be somewhat stable, which is to say that a user can count on that interface to be there, with its inputs remaining the same for a long time or, at least, changing predictably.

When components of a system are loosely coupled, those components can be changed internally without significantly disrupting the rest of the system. Consider a quotidian example: The light bulb. Light bulb sockets have a standard size with standard threads and a standard voltage. The light bulb itself has evolved through various types of materials for the filament to LEDs, all without requiring a change to lamps and light fixtures. Now consider a simple example in software systems: An HTTP server and a datastore, such as a cache, queue or database. In most cases, it would be beneficial to place an interface between the server and datastore that would make changing the implementation of the datastore easy. Once these two components are only loosely coupled, things that are likely to change can do so without requiring significant changes to the other things.

If the components of your system are too closely intertwined, then even the smallest change could wreak havoc somewhere else in the system. You might compare this structure to a group of plants that were grown in proximity. Because all of the stems are intertwined, trying to swap out one plant for a new one will be challenging, as one false move could easily uproot many other plants.

In contrast, in a loosely coupled system, changes can be made with the confidence that when one component experiences an issue, the rest of the system will remain resilient. This confidence results in greater flexibility to make changes, enables shorter time-to-market for new features and products, and potentially affords a competitive advantage.

A corollary benefit of loose coupling is evolvability: When interfaces are located in the places most likely to change, it will be easier for the system to evolve. Interfaces are meant to be stable and opaque so that anything that goes on behind them is generally unknown and irrelevant to a user of that interface. So, as long as interfaces remain stable, the systems behind them are free to change and, therefore, evolve.

While it is true that microservices strategies do support loose coupling, they’re not the only way. Simpler architectural strategies can afford smaller or newer projects the benefits of loose coupling in a more sustainable way, generating less overhead than building up microservices-focused infrastructure.

Architectural choices are as much about the human component of building and operating software systems as they are about technical concerns like scalability and performance. And the human component is where microservices can fall short.

When designing a system, one should distinguish between intentional complexity (where a complex problem rightfully demands a complex solution) and unintentional complexity (where an overly complex solution creates unnecessary challenges). It’s true that firms like Netflix have greatly benefited from microservices-based architectures with intentional complexity. But an up-and-coming startup is not Netflix, and trying to follow in the streaming titan’s footsteps can introduce a great degree of unintentional complexity.

The microservices approach is a way to manage the complexity of a system once it has gotten too large to handle safely. Still, there is a trade-off between complexity and overhead. There is substantial overhead associated with managing a number of services. If adopted prematurely, a microservices approach can result in staff members spending all of their time supporting the tools that support the services and neglecting the actual product.

There are at least two options that may be worth considering for any team that wishes to stay nimble by fostering loose coupling and to make progress by avoiding the overhead of maintaining a microservices-based infrastructure.

The first option is to embrace monolithic architecture. While monoliths are often maligned as inflexible and outdated, a properly planned one can support loose coupling and require much less support overhead. The key is to build a code-based interface that can take one component’s inputs and relay them to another. Even if one component of the monolith is altered, such an interface minimizes the impact on any other component, and the system can evolve more easily where interfaces exist.

The second option is to employ a serverless platform provided by a major cloud provider. This approach is much closer to microservices in spirit, but the overhead cost is assumed by the cloud provider. As a result, serverless architecture can afford the benefits of loose coupling with minimal unintentional complexity. It may turn out that serverless is not for you because of other constraints, but as long as you construct your interfaces carefully by focusing on the interactions that are most likely to change in the future, it doesn’t hurt to try serverless first.

Even if you decide to pursue one of these two strategies for loose coupling, it may not be clear which one to choose. Here are a few factors that may help you determine whether to opt for monolithic or serverless cloud-based architectures:

The objective is not to decouple everywhere, nor is it for everything to be loosely coupled. Rather, it is to make the system evolvable by strategically choosing where and by how much components are loosely coupled. This is a game of educated guesses and trade-offs. There is no best way, just a least-worst one.

When analysts and industry leaders talk about “microservices” like it is the universal answer for software systems, this only applies to teams building the most mature systems that have the capacity to support the associated overhead. The term “right-sized services” better conveys that, even within a company, each team has different constraints and, therefore, different ways of achieving loose coupling. This may entail the use of microservices, monoliths, serverless platforms or a mixture of these things; but, in any case, teams that embrace loose coupling are better equipped to respond to challenges, gain a competitive advantage and adapt to a constantly changing tech landscape.

Filed Under: API, Blogs, DevOps Practice, DevOps Toolbox Tagged With: application development, architecture, cloud-native applications, microservices