Macroservices vs. Microservices vs. Serverless: the story of a modern solution architect
3 words. That's all they are, but they pretty much sum up the world of modern software development. It wasn’t very long ago that Service Oriented Architecture (SOA) was an upstart architectural pattern threatening to shake the established foundations of software development. Then, practically as soon as it’s advantages became widely understood, the services being designed were getting smaller. With every iteration we moved to a world where hardware was less important (since the workload was less intensive), until we came up with a paradigm where the hardware can be practically ignored! Welcome to the world of serverless.
It’s great to see the number of options we now have to create our solutions, but it does introduce a problem that is fairly new in the software development world: how should you design and develop your solution. I don’t want to try and explain the technical virtues of each patten, smarter people than me have already done this. I would however like to explain why I would choose one architecture over another.
I hope you’ll come with me on a little trip through my brain (excuse the mess) to see how I make the tradeoffs — because at the end of the day that’s what these 3 options are — that lead me to choosing a design pattern, how I avoid the pitfalls and try to build for the future without losing sight of the now.
Your choice of design pattern will (almost) never be because of a functional concern. In most cases your user just doesn’t care! Whether building a video streaming service, a trading platform or an eCommerce solution, your user wants a consistently good experience. They want responsiveness, immersion and ease of use. Striking that balance is an incredibly tricky thing to do, and while a poor choice may hurt your ability to deliver that balance a good choice will not implicitly deliver it. This is an unwelcome paradox of systems design. The decisions you toil over may have minimal impact on a positive functional experience, but the wrong choice can still kill your project stone-dead through non-functional problem.
So, lets role play this. You be you, and I’ll be me (not the most imaginative role playing, but bear with me). You want me to design a system, and you want me to take you along as I move towards a design. first is the 10,000 foot view: I find it essential to focus on the following points:
- Development velocity: the product needs to get to market as quickly as possible without sacrificing quality. Your users or sponsors may not care about the underlying architecture but they will absolutely care about the speed of initial delivery, speed of subsequent change and the overall experience. Rapid delivery results in faster ROI. Businesses (usually) love this. Good experience pleases users, and keeps them with you.
- Maintainability: can the team deliver what I’m designing? Can the support team (if there is one) support it. What happens if a component fails? How do we know if it fails, and how badly it has failed? Each of the 3 paradigms bring their own problems you will need to mitigate. How you are able to deal with them will affect how successful your application is.
- Flexibility: a good architecture solves the immediate problem being presented to you without constraining future choices. Problems change, users change, business priorities change. None of these changes will be halted by your choice of software architecture but an inflexible architecture can introduce friction when change inevitably happens. Embrace the fact that change will happen, but friction will make adapting to the change unpleasant. Friction should always be avoided. The path of least resistance is almost always the best way to go.
- Politics: this is a bit of a hot potato but it can’t be ignored. You need to understand the world that you are living in. If the business does not like the idea of loose control or dependencies on the cloud then microservices or serverless architecture are unlikely to get much traction. If the business wants rapid delivery and embraces a ‘fail fast’ mentality then microservices are likely to get greenlit without much ‘sales effort’ on your part.
Based on these points I can’t honestly tell you when to use one architecture over another. As you can see, the choice is a tradeoff between the business, the team, and the users. These 3 groups each have their own hopes and fears. If they align closely then the choice is made easier. A team that is happy running N-Tier applications on distinct hardware, combined with a business that plans its projects 12 months in advance will see little benefit from microservices, but would still feel the pain that microservices bring with them. This team may use some serverless functions for the areas of the project where big hardware would be overkill (usually event driven components such as scheduled jobs) but otherwise their desires align best with macroservices.
So as an architect, what should you do? Always aim for the path of least resistance, and forge that path towards the objectives of the business culture. For example, if the business exists in a domain where stability is paramount and projects are closely controlled then look towards macroservices. They are not easy to change and deployment can be tricky (remembering to design for flexibility will help to manage this) but they bring stability and predictability with them. These systems are often so stable that engineers who have worked with them for a while may even be able to preempt failures under certain situations. This is still an example of stability in macroservice architecture. It’s unlikely to throw you a curveball and workarounds become ‘business as usual’.
On the other hand, your business may exist in a domain where disruption is common place. Web 2.0 startups are a great example where a wily disruptor can shake the established players within the space, and they can do it quickly. This fosters a culture of ‘disrupt or be disrupted’. Microservice architecture sprung up from this need to pivot a business and its application stack in a different direction quickly. This ability to change direction is paid for by reduced maintainability. Microservices are hard to look after! The best microservices are self configuring, self healing, self discovering automatons but they still need to be modified by an engineer when the business changes, and that change must be done in a way that does not cause cascading failure.
DevOps is the cultural response to this ‘change quickly’ business model and the paradox that a business may need to pivot quickly but people do not embrace change easily. DevOps espouses high visibility of parts combined with low communications overhead within the team to ensure that changes are understood early, implemented quickly and maintained by the creator. Without this culture a microservices architecture is likely to be difficult to maintain and may even become a major source of friction between the business and team, and when that happens you can practically guarantee it will end up hurting the user experience.
So what about serverless? This is a tricky one and I can only give you my opinion. I very much like serverless architecture. The idea of palming off most/all maintenance of your components to the provider is like a dream come true! No maintenance overheads! Huzzah!
Unfortunately it’s not so clear cut. You still have to maintain the API to the serverless provider, the runtime versions and all of your dependencies. Traditionally these things change more frequently than the underlying infrastructure would, so you need to ask yourself a question: have you truly removed your biggest bottleneck?
Some people decry vender lock-in as a reason not to use serverless but I would offer a different opinion. Most commonly used technologies start off as proprietary and move into the open as time progresses. Things like the Serverless Foundation are pushing to make entry points more standard compliant, and where they can’t then tooling is being developed to minimise the overhead. I would argue that before you start using serverless functions you have already settled on a cloud provider, lock-in has already happened. Working against becomes another source of friction.
Once your past the entry point of your serverless application you’re getting into your application’ dependencies. Chances are that if you’re building on AWS Lambda then you are also using DynamoDB, Elasticache or S3. The choice to use AWS components inside your app has nothing to do with Serverless. The offerings of cloud providers compliment each other, trying to abstract them away incase the business pivots their entire model to another supplier is extremely superstitious and highly unlikely to happen without you hearing about it months in advance. Take the path of least resistance!
That being said, I am divided on whether a full application stack could be built on top of serverless technology. While practically every solution you design can be pivoted (with a little ingenuity) to an event-driven architecture there are some major limitations inherent to serverless frameworks:
- Timeboxing: Serverless functions run in strict timeboxes. Run out of execution time and progress is lost. You have a little control over this, but delivering a long-running workload as a serverless function may prove unstable.
- Concurrency Limits: You can only run a finite number of functions concurrently. This limit is unlikely to become a problem if you are using serverless functions to handle the edge cases in your Micro/Macroservice architecture but in an environment where the entire stack is serverless then a limit on concurrency works against the event-driven paradigm you have created.
- Cost: It is well documented that serverless functions (at the time of writing) cost more per second of use than an equivalent piece of commodity hardware utilised to the same amount over a consistent time period. This is something that can be absorbed if the serverless functions are patching the edge cases in existing architecture, but a ‘full stack’ serverless application could get expensive…
These 3 architectures are prevalent in software engineering right now, but it is worth adding that just 15 years ago the fundamental idea behind microservices was an anti pattern (‘extreme SOA’ was frowned upon as excessive fragmentation of the problem domain) and serverless wasn’t anywhere even on the horizon.
The speed of technology is only increasing — today’s fad is tomorrow’s de facto choice. As an architect its important to rise above the technical choices you will be asked to make and instead try to see the big picture. Avoid closing a door by choosing an architecture that is hard to walk away from or difficult to maintain in the long term.
Make every effort to understand the people — the business, the engineers and the users. People want software, people build it and people use it. Without people you don’t have a role so keeping them central in your mind is core to your success or failure. It is incredibly difficult to make all 3 groups truly happy but it is possible to strike a good balance of contentedness between them.
I’d like to leave you with one final piece of advice. Don’t burn bridges, be they technical or political. Without the business behind you you won’t know where the value is, without the team you won’t deliver any value and without the users there is no value to be delivered.
Don’t rule out a choice because you don’t like it on non-technical grounds or simply don’t understand it. Tech suffers from entropy more than many fields, todays goto tech is tomorrow’s roadkill.
In the words of Uncle Bob:
Good architecture makes the system easy to understand, easy to develop, easy to maintain, and easy to deploy. The ultimate goal is to minimize the lifetime cost of the system and to maximize programmer productivity
‘Easy’ is loaded with context, but if you can deliver it then you will succeed.