It reeks of inexperience and being highly opinionated followers of popular opinion instead of playing with both personally.
So few projects actually scale to benefit from microservices where its a benefit.
One’s interpretation and benefit for microservices is fine. It’s just not guaranteed to be factual in many used cases.
First, the line of where scaling benefits from microservices is also moving higher each year due to the same underlying reason:
Whether Monolith or Microservices, the increased performance of Linux networking, availability of inexpensive ram and cpu along with much more optimized databases like Postgres or mysql from the past 10-15 years ago seems to have been missed by many people who seem to have set an interpretation in place and never looked back.
When horsepower gets faster and cheaper each year the architectural best practices of microservices adoption was likely relative to its year of adoption.
What to do?
Keep your stack unapologetically simple. Don’t handcuff your development speed when the complexity arrives to rearchitect things anyways. Because the first version will have the wrong assumptions about what customers want.
How?
Consider monorepos to let you start as monolith for velocity and move to microservices as needed
Monolithic apps can have a service based architecture internally (monorepos can be pretty handy), which in turn lets startups ship much more regularly than they ought think for monoliths.
You can design folders in a monorepo at the start to fuel a monolith and keep a path to go to micro services, where and if needed.
To know this enough experience (both good and bad) is needed with both microservices and monoliths.
Not only that, but if you absolutely must (for example different scaling requirements), forking out a service from a monolith and deploying it as microservice is relatively straightforward. Provided source code interdependencies have been maintained cleanly before, which can be done with support from tools.
When you start with a monolith that isn't just a ball of spaghetti (e.g. clear module boundaries), you don't have to put as much work into the whole big picture and it's easier to evolve the internal architecture of one larger service than N smaller services. And if you do discover that one piece of it is appropriate to split off (hotspot, reasonably transactional instead of interactive calling, etc), then you just replace the guts of that module with whatever RPC mechanism you're using and the rest of the application doesn't really need to care about it.
I often end up viewing the start of apps without a problem to solve might benefit from being a dynamically generated interface set by from a combination of feature flags from subscription to a plan.
That degree of flexibility can seem extreme, but until the patterns from the chaos don't emerge, it can be pretty tough.
As a total aside - Is there/why isn't there a discourse forum for architecture folks to chat about things like this?
This has been one of the most enjoyable threads I've been a part of in HN in recent memory.
Occasionally I see good discussions on Twitter. I just tried to find a couple of names of people and sadly I can't seem to remember any of them. Sorry.
Otherwise... the discourse generally seems to be "this is The One True Way of doing things and if you're not you should feel bad". And that's not helpful :)
> To know this enough experience (both good and bad) is needed with both microservices and monoliths.
Over the years I have worked with a huge number of people who are looking for Silver Bullets and Best Practices, generally for one (or both) of these reasons:
- To be blameless if the architecture doesn't work out (building with microservices is a Best Practice, it's not my fault it got very expensive)
- To cut down on the number of deep-thinking decisions they have to make
And to be fair on the second point, I don't necessarily disagree with the premise; if these kinds of decisions allow you to get going quicker, it's not necessarily the wrong choice at the moment. What everyone needs to keep in mind, though, is that if you don't do deep architectural thinking and planning at the start of a project (or even if you do sometimes...), you'll have to revisit it later. Often the same forces and pressures that led to taking shortcuts at the early architectural stages of a project will continue to be present down the road, and it'll be more work to fix it later than to do it right the first time.
It's a tough balance. If there's a high risk that the project won't be alive in 6 months it probably doesn't matter. If it's a prototype that's very likely to get into and stay in production, then it really does matter.
Doing deep enough architectural thinking and planning at the start to focus on maintaining flexibility (which can be achieved in many ways) without premature optimization or engineering can go a long way. Basically, don't work yourself into a corner or concrete too quickly by trying to do too much, or too little is an approach I try to keep in mind. Technical and architectural debt comes with interest as well.
One way to reduce the architectural risk is to make the first version a throwaway. It can burn through assumptions. Similar to design thinking, going broad and converging twice can help get a better sense of vector,
The problem comes when you try to start a new project and begin to realize that except in simple cases, it's exceptionally hard to know ahead of time how you would best be served to split an application up into services. Once there's a network boundary between two pieces of code, there's now an API that needs to be managed carefully: deployments of the two pieces of code may not be in lockstep, rolling one back may be necessary, etc. If you got the split wrong, the refactoring to fix that could be extremely hard.
By comparison, splitting logical services out of a monolith isn't always a cakewalk, but it's usually more straightforward, especially since as you mentioned, it's totally possible to just share the same codebase between services. It could even be the same binary invoked differently.
My feeling is that it's easier to split a monolith than to join microservices that were developed separately together; they might have different codebases and possibly even be written in different programming languages. Therefore, it's probably better to start with a monolith even if you're pretty damn sure you will need some services split off later to scale.
Serverless on the other hand I am more skeptical of. I like certain products that provide generic-ish abstractions as a service: Cloud Run for example seems like a good idea, at least on paper, and Amazon Aurora seems relatively reasonable, too. But I think that they are often too expensive and do not provide enough value to justify it, especially since a lot of serverless tools don't provide terribly generic APIs or services, leading to substantial lock-in.
Both microservices and serverless APIs were sold as near-panaceas to the woes of traditional monoliths, but it's a lot more complicated than that.
edit: When I typed this response, the message I was replying to was different/much shorter.
Splitting a monolith, in a monorepo, can be conspired to break into microservices a bit easier than going the other way. There's a lot more tooling becoming available.
The committment to cloud, serverless, devops has occured when the complexity of administering servers was high. Banking on progress, the administering servers has become exponentially simpler. The recent project from the basecamp guys (mrsk?) is an example of this.
You could almost roll your own "serverless" with enough postgres exposing itself as a queue and/api. Once you outgrow it, so be it. Tools like Hasura are quite interesting.
Someone needs to make a programming language that encourages you to write code that can be easily split and recombined. Something that can do remoting well.
It took me a while to compile a structure I thought was good to start with but also (hopefully) flexible.