The Human Cost of Microservice Architecture
By: Hugh Blackall
Tags: Software Architecture, Software Development, Microservices

It’s decision time, and you’re considering adopting a microservice architecture for your tech platform. Microservices are pretty cool. Under the right circumstances they can provide some great benefits in scalability, flexibility and productivity. Like all things in technology however, there are downsides too, and it’s important to have a thorough understanding of as many pros and cons as you can before you make a given tech decision. This is of particular importance when making the potentially impactful decision to employ a given high level architecture for your system.

In the interest of helping to cover as many of those pros and cons as possible, I want to present a downside of microservice architecture that is perhaps paid less attention, and one you should be well aware of when making an architecture decision; the human cost of microservice architecture.

Programming is hard. It’s more than a technical challenge, it’s a mental and emotional challenge too. Development of software can be slow and sometimes deliver poor results. These shortcomings can often be attributed, at least in part, to the high mental effort that’s required on an engineer’s part to do software development well. Microservices can add to this mental engineering burden, exacerbating the pains of development and leading to the all-too-common outcome of a failed project.

The concept of microservices is designed to separate concerns. When implemented well, this can yield smaller code bases and actually simplify the context a software developer is required to absorb and reconstruct inside their head in order to understand a system and make meaningful changes. Indeed, a potentially low-mental-overhead development paradigm is to have separate teams working on separate microservices, allowing for a focused, finite depth of understanding required within those teams.

In most microservice systems I’ve encountered however, individual services aren’t as well isolated into a single responsibility as they theoretically could be. This ideal theoretical separation of concerns is a hard thing to do and so you can expect a certain amount of coupling of services will find its way into just about any system. After all, most systems are created to solve a single, or a closely related set of real world problems, as opposed to delivering a neatly separated set of services that each address an individual problem. Ultimately this means that developers can’t get away with knowing only individual services. Instead, they frequently need to work across microservice boundaries and may even need to know the whole system at some level.

With that in mind let’s consider that, at a system level, microservices add complexity. By design, a microservice architecture adds boundaries that data and processes must bridge in order for a system to do its job. In order to understand data flow and debug functionality — two activities that developers naturally already find themselves spending a high proportion of their time on — engineers now need to understand all the extra network code and boundary infrastructure in between services. Some good tools exist that help to abstract these boundaries into a simple function call for basic data flows, but when things go wrong — and they inevitably do — engineers have to lift the hood on these abstractions and add their inner workings to the growing context that must be maintained inside a developer’s head in order to work effectively on the system.

Abstraction of service boundaries can also be imperfect in other ways. There can be a reasonable amount of setup — definition of data structures, wiring up of functions, generation of code — that needs to be done in order to make these abstractions work. This infrastructure work scales well though, generally only needing to be done once before many developers can repeatedly benefit from it. That is, until the system has to change. Change is inevitable in a system, and indeed can be critical to the success of a software project. It is however often driven by external business factors and real world goals and can therefore be hard to cater for when laying out the more abstract structure of a software system.

For a microservice system, this means that changes in business requirements will often require changes across multiple services as well as changes to the infrastructure that links them together. This adds overhead to a developer’s workflow, as changes have to be made in more places to to keep service infrastructure in step with business functionality; and stretches a developer’s mental capacity to ensure they are across all of the places changes need to be made, and that their changes won’t break things, given more changes usually means a higher potential for doing so.

All of this adds up to more stuff that a developer has to keep in their head. It can slow down their speed of delivery and increase mental burden in worrying about breaking things. If you are a large organisation that has thought about how to separate your microservice concerns and has multiple teams that can work somewhat independently on individual services, then this might not be a problem at all, and all the well known benefits of a microservice architecture will help to drive a successful business. If you are a startup, perhaps with only a handful of developers whose mental and emotional energy is one of your most valuable resources, the mental impact of adopting a microservice architecture could spell disaster. If your situation resembles either of the above, or you’re somewhere in between, then the human cost of microservices should be an important consideration when making architecture decisions.