Microservices from a Startup Perspective
infoq.com
infoq.com
Microservices are great. They are also complex, expensive and require much higher expertise.
Start with a well groomed monolith that is well modularized then split it when scale (user scale, team scale) makes sense.
I would say the complexity is largely in deployment. Otherwise, I find microservices much simpler to reason about.
As an example, keeping a bunch of small, isolated microservices behind queues means that I can reason about them very locally. I know what data they own, I know their interfaces, and I don't really need to think 'outside' fo them - they just send other asynchronous messages out. I don't need to think about dependencies very much - if a downstream or upstream server fails it is irrelevant to the microservices, so long as the queueing system is up.
It's a bit more annoying to get infrastructure and whatnot set up, but not a ton.
> expensive
Maybe? Shouldn't finger grained scaling be cheaper in some cases?
> require much higher expertise
I don't really agree. Again, by keeping things small and by maintaining the ability to reason about components locally, I think that it's easier to work with microservices than monoliths.
> Due to the monolithic software architecture, it was difficult to add new features without affecting the entire system and it was quite complex to release new changes, since we had to rebuild and redeploy the entire product, even though we changed only a few lines of code. That resulted in high risk deployments which happened less frequently - new features got released slowly.
From the article. For a startup the ability to add features quickly is pretty important - especially early on.
I'm not advocating microservices first for every situation, but I've built systems microservice-first and it's been fine.
https://en.wikipedia.org/wiki/Fallacies_of_distributed_compu...
The important part is to get instructure working well first. Things like request-id propagation and good automation of deployment.
As an afterthought, it may bury you, but if you think about those first, then you'll probably gonna have a good time.
> Maybe? Shouldn't finger grained scaling be cheaper in some cases?
While not OP, I'm assuming they think it's more expensive from a development POV, not (necessarily) operationally.
So we're sending a new data model in the form of JSON over TCP using AMQP? Or are we using STOMP over websockets? Who maintains the broker? Do we use a direct exchange or topic exchange? How do we replicate messages across exchange nodes to ensure uptime? How do we upgrade the broker?
> Shouldn't finger grained scaling be cheaper in some cases?
Monolith, basic high availability is 3 servers, minimum. Now every single microservice needs 3x servers, plus autoscaling, plus we need things like distributed logs, a resilient broker, etc. Even simple things like uptime pinging are more expensive because we're tracking more hosts.
Sure, if you ignore everything outside of your system then it's really easy to work on a single microservice, and if your startup has several million dollars and hundreds of engineers - great, use microservices.
Uh? I don't know? Like, I know how I've done it, idk how you want to do it, and I won't prescribe implementation details as if they're universal.
> Monolith, basic high availability is 3 servers, minimum. Now every single microservice needs 3x servers, plus autoscaling, plus we need things like distributed logs, a resilient broker, etc. Even simple things like uptime pinging are more expensive because we're tracking more hosts.
If you can run your entire project on 3 systems, yeah, sounds like a case where splitting out would be more expensive. Though you can do microservices without having separate hardware - just stick them on the same box. You still get software fault isolation.
> Sure, if you ignore everything outside of your system then it's really easy to work on a single microservice, and if your startup has several million dollars and hundreds of engineers - great, use microservices.
That seems like an exaggeration.
I don't. Or rather, unlike that of transactional designs used with RDBMs or services deployed into traditional/commercial SOA stacks with distributed transactions, the scope of "reasoning" about microservices is very limited/developer-focused.
But maybe you're using durable queues with exactly-once delivery guarantees, and "idempotent" work requests, which sure is neat if the app is a good fit.
A counter-argument can be made on this point. You can implement the same isolation levels (modules) but as a monolith using message passing. How is it harder to reason about it then?
In other words, the hard part is how to split your program complexity in modules that are as simple and as orthogonal as possible.
The worse you do this split:
- In a monolithic system, the system becomes exponentially more complex and hard to maintain. You get more bugs, and developers running away from this project as fast as they can, but the thing may fulfill its goals to some degree.
- In a microservice-based system, you may end up either with a handful of huge services (a monolith that uses network-based messages instead of function calls/inproc message passing), or with gross inconsistency problems if you force splits in the wrong places.
Given the operational overhead and that most startups will never get to large scale, I'd say go with the monolith and try to gain traction with your startup. If/when you finally require scale you should have plenty of resources to either split the monolith (if it was well modularized) or just rewrite chunks as services until you get to a fully microservice-based architecture.
Microservice architecture tells you more about how and where to split - introducing bounded contexts and various patterns you might use.
> - In a microservice-based system, you may end up either with a handful of huge services (a monolith that uses network-based messages instead of function calls/inproc message passing), or with gross inconsistency problems if you force splits in the wrong places.
Sure. You can also write really gross, convoluted monoliths.
> Given the operational overhead and that most startups will never get to large scale, I'd say go with the monolith and try to gain traction with your startup.
As the article states, they found that microservices allowed them to move faster on features, which is something that'll matter a lot to a startup. They do, of course, state that there were upfront costs.
The architecture doesn't tell you anything: it is you who decide how to split. Yes, there are guides and what not to help you make this decision, but again you could do exactly the same splits in a monolithic system.
> Sure. You can also write really gross, convoluted monoliths.
Totally. That's what I was implying when I said "complexity grows exponentially and developers want to run away from the project like hell".
> they found that microservices allowed them to move faster on features
While the article is fine, I don't agree with this affirmation at all. They rewrote the entire system (bit by bit) using better engineering principles, better internal organization, and with a lot of experience about their domain that they didn't have before. As I see it, the outcome you see here is not "microservices!", it is just "doing a better job".
I don't really agree. The major difference between SOA and Microservice is the guiding principals, such as the bounded context.
> As I see it, the outcome you see here is not "microservices!", it is just "doing a better job".
Maybe so.
The elephant in the room is the problem domain. If your domain doesn’t decompose cleanly into services then your base maintenance costs skyrocket. I hate these discussions on HN because there’s so much generalization and opinionated arrogance, when in reality the particulars of a given situation matter far more than high level principles or dev ideology.
Well, the post that I was responding to stated the tradeoffs (complexity). And I was responding by saying that 'complexity' is a bit nebulous - and, in my experience, is reduced in some areas (very easy to reason about independent microservices without having to consider dependencies).
> Debugging systemic issues across distributed systems is automatically one or more orders of magnitude more difficult.
Sure, I guess. I haven't really run into issues that good correlation ids + instrumentation can't help with. Also, I find it really easy to target metrics and 'smoke alerts' to small services. Ideally, in a distributed system, you aren't worrying too much about distributed debugging so much as a single service failure.
> I hate these discussions on HN because there’s so much generalization and opinionated arrogance
Whoof. Let's take a step back here. I shared my experience and opinion, I don't think I'm being arrogant by doing so. I stated that microservice-first is not always the way to go, but that it can work - and I pointed to an area in the article that I thought really highlighted this point (the need to build features rapidly).
Yeah, sure, knives are useful and you can always find exceptions for a few 2 years old.
But still. Pretty good rule.
* That service would work well enough on its own as a startup - it's internally complicated but isolated and requires a very simple abstraction (e.g. doing a postcode lookup service).
* As a facade to create a clean interface to something that is not clean (e.g. a nice REST API that talks to a crazy weird industrial control system over a parallel port which only runs on windows).
* The operational requirements of the code are entirely different (e.g. moving data processing from the webserver to a task server and feeding it via a queue).
* As a means of linking components in a large scale system run by a large organization that has multiple teams that often write code in different languages and have real trouble coordinating with one another but are nonetheless overseen by a crazy smart architect.
The latter was basically where Martin Fowler thought microservices worked well, and it probably did, but he kind of forgot that A) the absence of a crazy smart architect is more common than the presence and B) this was a hack which involved a shit ton of extra operational overhead that was only worth it because it solved a gnarly people problem.
When startups of 7 people run more than 3 services I start looking for the microservice kool aid.
Like staticassertion already mentioned, it depends on the real project and problem to solve.
I've pitched a startup which started directly with an microservice like architecture. It was the best possible approach for the given problem.
How much did the team improve shipping features for customers? Is the app more reliable? Does the engineering team spend way more time on CI/CD and all the other “overhead”?
Most teams will never need this type of solution, even though theyre tons of fun to build and operate. I just wish we had a better way of sharing the non-engineering results of these types of efforts.
CI/CD is required regardless of microservice or monolith. Once you have it working for one deployment, it should be trivial for 1+N.
Maybe you are thinking about how do you deploy inter-service dependencies? In that case, you don't. I'm not joking. When you move to microservices you have to treat other internal services like they are something you do not control. This means maintaining backwards compatibility, versioned APIs, etc...
At first it seems like it is harder, but once you get in the mindset that this always must run and serve existing clients it is rather freeing. Plus, being able to treat each service on its own makes it easy to focus and do large changes - thus move faster.
Compare this to friends who tell me about logging in at 11PM to deploy because they have to take the system down. Completely taking down the system to deploy should almost never happen today.
If you choose Elixir you get OTP which does lots of excellent things to be able to scale out your app, the data passing is transparent between nodes (i.e. no protobuf or GRPC here). Excellent ways to monitor and deploy your app (and inspect messages as they are passed). I really need to write up the huge amount of work we’ve put into node and go microservices that are already part of the Erlang/Elixir ecosystem...
For startups don’t bother, if you do it right it will literally kill your startup before you see the benefits.
For a startup - all this may feel OK like you're cheating the system and beating others to the punch - but damn, if you actually make it - good luck scaling that up.
Personally, I don't see faas a competitive market advantage. It's a tool and i'd be a hell of a lot more selective in how I used it.
Why is that a problem? I am speaking in C# terms...
Phase 1 Monolith: all of your code is one solution, with separate domain specific projects (assemblies) with separate namespaces with a logical use of access modifiers exposed as an interface. All of the other code uses interfaces as dependencies defined with a DI framework.
Phase 2: We need scale now. Bring up a lot more VMs at the click of a button behind a load balancer and you get scale quickly.
Phase 3: Take your project that you need to scale independently out of the monolithic solution, expose it as API, put that one API behind a load balancer. Then have a proxy class in your client and use your DI to map your interface to your proxy. If you integrate something like Swagger into your API, it can create the proxy class for you.
If you can deal with eventual consistency, use a queueing mechanism.
Rinse and repeat and keep taking projects out of the monolith as needed.
There will be some shared code you don’t want to run out of process. For that, create Nugget packages and you won’t have to change your client code at all.
Forgive me if my understanding of that architecture is wrong. I only have read parts of the book so far.
It's then mechanically trivial to split the Customer module and the Orders module both the code and the data store.
actually you can keep your solution and just have many projects in one solution (monorepo)
Why? Just because the code sit's in one repo, doesn't mean that everything needs to be deployed the same time.
It's foolish to think that the initial version you make of any system is going to be the same thing that you end up scaling to a million users and it's foolish to think that you're going to understand where your actual pinch points are going to be before that system is under any sort of pressure. Let alone before you really know what your product is really going to be (which is the situation most startups find themselves in).
The slices you decide to make with the knife early on more often than not look stupid and burdensome once you're actually under pressure.
Most people also underestimate how far it's possible to push monoliths. My favourite example is the OpenStreetMap infrastructure, which has to support thousands of concurrent users editing a global map that is ~105G when compressed. It's largely monolithic. And that doesn't really seem to be a problem. Works well.
I suspect most people have never actually seen the sort of "heavy load" they talk about so much running on a sensibly designed monolith to the point that it's not coping due to its architecture. There's a lot of conceit around how big a load people really deal with.