Microservices Thoughts: Babies and Bathwater

Tom Winans
Better Programming
Published in
6 min readMay 12, 2023

--

Photo by Zoltan Tasi on Unsplash

I found recent posts by Amazon[1] and David Hanson[2] regarding microservices vs. monoliths to be quite interesting. My initial and admittedly superficial read of them, whether right, wrong or otherwise, was that they argued microservices at scale are bad. While I agree that they can be, I think that particular generalization can throw babies out with bathwater… also a bad thing.

When thinking about microservice-based architectures first emerged, it included rationale supporting use of polyglot technology stacks, independence of development teams defended with API-driven design, i.e., interface-driven design, independent deployments and scalability of microservices, and reduced downtime of a whole platform due to localized faults. One author wrote that: “Microservices make it easier to test, understand, and maintain application builds with the combination of independent components. It is an excellent solution for building large-scale products and improving workflows and productivity[3].”

While the rationale sounds good, I personally disagree with it for at least the following reasons:

— Polyglot stack independence interpreted as “use the right language for the right task” makes sense, but only by exception at a function level. Certainly not as carte blanche and taken to an extreme, which results in incredible cost of ownership over time.

— Granularity of services matters. Microservices == functions introduces complexity into an architecture and challenges any team to understand the overall application context, the context in which microservices are applied.

— Independence of development teams defended with API-driven design, i.e., interface-driven design, is only part of the picture that needs to include development, testing, problem solving and maintenance over time. Testing and maintenance over time are not easy when microservice thinking is allowed to be taken to an extreme:

  • Tool support for distributed testing and debugging aren’t plentiful — they don’t really exist as far as I can tell.
  • Maintenance over time means ability to maintain through staff transitions, etc. Going to polyglot extremes makes this challenging, at best.
  • Deployment at a function level isn’t often the way deployments work… i.e., microservices exist within a larger context and are not truly discrete or unrelated to other functionality in an application architecture. Microservice pundits might call foul — I’d only agree insofar as common utility functions are concerned. Besides, the point here is deployment, and polyglot Infrastructure as Code introduces cost of ownership and complexity as well.

As a technology diligence consultant, I’ve seen microservices taken to an extreme — with geographically distributed teams each accountable for specific microservices (multiple time zones outside of the US), different technology stacks, and the orchestration layers like the Amazon Step Functions orchestration method that the AWS Prime Video team used. Suffice it to say that both performance and debugging problems were very problematic for all the reasons that probably come immediately to mind, and then some. And don’t get started talking of total ownership costs…

The AWS team and David Hanson make excellent points in their essays. But is the argument against microservices gone amuck really a strong argument for monolithic architectures?

The short answer, in my opinion, is “no”. Of course short answers doth not a reasonable essay make…

Monolithic platforms can be challenging to test… requiring a full regression that simply takes time to perform and is challenging to automate. Monolithic platforms also can be found with poor separation of concerns, e.g., a UI implementation that poorly separates rendering code and the code that marshals data to and from cleanly separated service APIs surrounding business logic. They also frequently can be found to not cleanly separate independent database components or surround them with well-formed service APIs… so sometimes database access is through an API invocation and others it is more direct, using database primitives — both impacting software-to-database couplings. And, with respect to Ruby on Rails, we often see all the above in diligence in platforms having Ruby on Rails technology stacks.

So is Ruby on Rails the problem?

Not at all. And please note the use of the phrase “can be” repetitively used above, because the problem is one of overall architecture.

One could architect distinct services with Ruby on Rails and find it meets architecture requirements of good separation of concerns in spades. One could nicely separate UI concerns from service concerns, as well. But architects need to think (big picture) through what they’re doing vis-à-vis all the architectural views of couplings, modularity, service vs. UI APIs, separation of concerns, scalability and performance, deployment, maintainability, cost, and more.

So is the serverless architecture offered in public clouds, and with Apache’s Open Whisk, a bad architecture?

Not if it is used wisely.

And by that, I mean:

— Don’t use cloud orchestration functionality, e.g., AWS Step Functions, without understanding cloud quotas and costs. It is easy to leverage the technical capabilities of Step Functions in proof of concept. But don’t leave that orchestration in place until functional and performance testing at scale and the appropriate cost modeling are performed. There are numerous ways to orchestrate so that each and every state transition does not map to a unit of cost.

— Experience the process of debugging whole workflows — seeing firsthand what it means to have microservices be equivalent to technology functions (think technical software decomposition) vs. business functions (think top-level business function decomposition to subordinate yet still business functions).

  • Service granularity matters. Public services usually need to be more coarse than simple functions to be cohesive from a business perspective. You may be able to logically justify fine-grained services, but the implementation of the same may not hold together in aggregate when adding network chattiness, the need to have a full application context (sometimes called state), etc.
  • Measure the total cost of ownership — and remember that software developers don’t just instantaneously learn new programming language skills or become fluent with code bases overnight, irrespective of their talent.

— Think scale from the start. When architecting something new, it is tempting to not pay attention to scale at the start… implement the functionality and deal with scale over time. But that is quite often not a luxury we have… if we’re in the slightest bit successful with the functional capabilities that we put out, then we quickly have far more functional development work to do that seems to take priority over performance and re-architecture.

If all these guidelines are taken to heart (disclaimer: there probably are more, but they are more common sense than rocket science), and for the final time, is a serverless architecture bad?

No!

And that is a good thing because, were it bad, we’d not be able to take good advantage of cloud providers’ cost models for serverless — which are very attractive. “Pennies on dollars” attractive (serverless vs. container clusters and always on infrastructure). The simplicity of deploying service dockers into serverless infrastructure is high. Maintenance of right-grained services is like maintaining a small number of applications rather than many functions, lowering the total cost of ownership, and helping in understanding an architecture in aggregate. Proper separation of concerns allows team federation and independence… which always has been the case, with or without microservices.

In the face of rising cloud OPEX, it is appealing to be able to host serverless functions and minimize always-on infrastructure. Once you have a nicely optimized platform architecture in place, then reserve those always-on instances, etc.

Monoliths and microservices (serverless functions) aren’t bad except when the right architecture considerations aren’t taken. I’m not concluding that monoliths necessarily are bad by default. But please don’t superficially read the AWS Case Study or Dave Hanson’s essay as arguments for monoliths over microservices, as that seemingly would throw babies out with bathwater.

[1] Scaling up the Prime Video audio/video monitoring service and reducing costs by 90% — Prime Video Tech

[2] Even Amazon can’t make sense of serverless or microservices (hey.com)

[3] 6 Key Benefits of Microservices Architecture (stackify.com)

--

--