“But they weren’t doing true microservices” he droned on, while my train of thought came grinding a halt on the assertion. In my experience, many software developers apply all sorts of purity tests to the world around them, especially when it comes to “legacy” code. In most of my experiences, it has been delivered more subtly than this textbook example of the No true Scotsman fallacy. “Microservice” is already a silly term, one which many people defend by evoking the mythic status of “the unix philosophy.” Composition of components is definitely a valuable trait in a system, especially as an organization scales with new people and projects, but the microservice purity test fails in many cases.
This misguided notion that everything in a service-oriented infrastructure should do “one thing and one thing well” is at complete odds with the realities of data affinity and network latency. After trying to break up a few monoliths “into services” in recent years where I have frequently encountered this microservice-all-the-things desire, my focus has shifted away from what serves the HTTP requests. Instead, I’m much more focused how the data is stored and accessed. Taking a monolithic application and merely swapping function calls for remote procedure calls is not actually solving anything. Optimising around what parts of the system require data makes more sense to me as a guideline.
The “test” that I apply to the “no true microservices” approach is this: should authentication and authorization be handled by one service, or two? I think the question gets at the heart of many service-oriented architecture designs. Authentication is “easy”, compared to authorization which can bring in a whole carton-load of business logic and data requirements. That said, are functions should probably be performed by two separate services. But if you don’t have any need for authentication without authorization coming alongside it, then two separate services introduces another network call, another service-dependency to manage, and unnecessary complexity. “But we might need it some day!” a microservice aficionado might retort. I certainly agree, we might need lots of things. Building and delivering software requires trade-offs, and if the business’ requirements for the next 6-12 months are not going to be addressed by pre-optimizing the split of these two concerns into separate services, then it is likely best to say You Aren’t Going To Need It and move on.
It is always better to refactor a production system in the future, than to try to design for imagined requirements in the present.
The availability of “Functions as a Service” does bring us closer to a “one thing and one thing well” world. It also exaggerates the myriad of management, monitoring, and debugging concerns which already affect service-oriented architectures. This is not to say that Functions as a Service (FaaS) are a bad thing, quite the contrary. I love the model for simple and stateless API endpoints, like handling inbound requests, enriching them, and putting their contents directly into a commit log/queue system for later processing/warehousing. To suggest that all services fit the FaaS model however is patently absurd. Unsurprisingly, I feel similarly about a universal application of the “one thing and one thing well” microservice model.
Avoiding the No True Microservice fallacy, I recommend considering service-oriented architecture design with the following, intentionally ordered concerns in mind:
- Debug-ability: if we can’t debug it, we fail.
- Deliverability: if we don’t ship it, the team fails.
- Manageability: if we cannot maintain this code, evolving it to meet the needs of the business, then the product will fail.
- Scalability: if we cannot scale it to meet customer demand, the business will (probably) fail.
Service design is always about trade-offs. Whether attempting to build something from scratch, or replacing a monolith, it is important to prioritize the concerns appropriately. Always keeping in mind that our first priority is to deliver a functioning service, not to come up with the “best” whiteboard design possible.