Hacker News new | past | comments | ask | show | jobs | submit login

Microservices are for companies with 500+ engineers.

They help teams manage complex interdependencies by creating strong ownership boundaries, strong product definition, loose coupling, and allow teams to work at their own velocities and deployment cadences.

If you're doing microservices with 20 people, you're doing it wrong.

It's when you have 500 people that the average engineer doesn't need to know about the eventually consistent data model of your active-active session system. They just need your service API.




>If you're doing microservices with 20 people, you're doing it wrong.

Wrong for the project and your employer most likely, but for you personally? Your CV for the next job will massively benefit from lines about leading initiative to reimplement a monolith as microservices, compared to old boring reliable technologies. I'm only half joking.


CV-driven development, the worst methodology.


> If you're doing microservices with 20 people, you're doing it wrong.

Like all generalisations, this rule will be wrong sometimes. I think you're imagining a single coherent "product" that all those people (presumably engineers) are working together on.

But imagine a consultancy (which I have witnessed) where projects are just one person, or occasionally two people, and only last a few months. Project outputs usually involving building a new component and slinging it together with one or two existing ones. In practice relatively few of these components get reused in future projects, but it's very hard to predict up front which ones will turn out to be key. In this case building microservices makes a lot of sense because the abandoned components don't take up mental bandwidth to maintain but they're always available if they do turn out to be useful later (even if in they need some updating, being self contained with a clear interface gives a head start). The multi process aspect is certainly a pain, and in principle it could be done with carefully curated libraries instead, but then the temptation for tangled interdependencies would be there and you'd be more tied to a specific language.


I think this is a good use case for libraries.


>They help teams manage complex interdependencies by creating strong ownership boundaries, strong product definition, loose coupling, and allow teams to work at their own velocities and deployment cadences.

You just described library boundaries. You did not describe why the interface has to be a socket.

>the average engineer doesn't need to know about the eventually consistent data model of your active-active session system

People can create unnecessarily connections between microservices too. It's only slightly harder than punching an extra hole through the public interface of an in-process API. Is that what we're spending so much time and money on.


Yeah. I think services are mostly needed when you need to scale things independently. If you just need to develop things independently and deploy them on different schedules, you can use a plugin architecture (hotswap dlls or jars into your big app) instead of services. That lets you avoid sockets and use local synchronous APIs for communication. I've worked on a large app with many teams building their own plugins, it was nice.


I like the rubric of, "If you have a people scaling problem or a performance scaling problem then you maybe need a microservice" Meaning, if there are too many cooks in the codebase and it's slowing down delivery, maybe use a microservice (which this reply is effectively calling out). The latter, if there is a hot place(s) in the code that needs to scale independently, maybe use a microservice. Any microservice can be represented as a library or dependency, design the code well and breaking things out shouldn't be terrible. See Moduliths https://github.com/odrotbohm/moduliths


What do you do if you have ~20 people and a decade old million line monolith that you're now bumping up against the limits of?


You could consider splitting certain large, distinct pieces into separate services, maybe. You don't need a dichotomy of one monolith vs. dozens of microservices. You could have a monolith plus 3 or 4 services.

Or only do that going forward; e.g. for future feature requests that would otherwise require adding another huge chunk to the monolith.


This is pretty much my thinking.


Common sense, no?


Seems the solution would be to improve the thing rather than reimplement it as microservices.


I think there are some things we can legitimately improve quite a lot under the current structure, and it seems there are other things that make certain improvements just untenable.

Upgrading a major version of an ORM for instance. It seems there are certain key improvements that boil down to "the only way this can be improved would result in a big-bang release affecting the entire of the system and take a dedicated team quite a long time to do". It's just not palatable for anyone.

For the parts we carve out as microservices we are essentially able to erase that technical debt. That comes at the cost of increased ops complexity. We're not ready or capable of having 50 microservices from an ops perspective, but we are capable of handling more on the order of ~5.


Every additional microservice adds complexity to the system, due to having to factor in many failure situations not present if everything is instead in one process.

There are other options in your case, like isolating any dependencies in a module with a well defined API instead of having it all in a separate (micro-)service.


Or split out an individual domain, particularly one you’re having pain with, into its own “micro”service.


I think this is going to be the approach we wind up taking.

In reality there are maybe 1 ~ 3 hotpots in the domain that reveice many more transactions than the rest.


It's still possible, and arguably good design, to write a monolith as a cluster of separate cooperating internal logical services. That way you can keep the benefits of decoupling and ownership boundaries without all the deployment complexity.

In Java, OSGi was particularly good for this, but you could easily build it around a fairly simple DI framework in pretty much every major language.

The most important thing though is to keep your interfaces well-designed and well-communicated. They are the principal source of bugs and misunderstandings.


Probably keep it as a monolith.

You don't need to define rigid ownership.

Though some pieces will move slower than others (and some things will never change), a single deploy cadence is probably fine.

There's also the danger of an incomplete microservice migration where you've now got hobbled together half-service weirdware that you have to support forever.

Don't do microservices until engineers working on their thing break your thing.


> an incomplete microservice migration where you've now got hobbled together half-service weirdware that you have to support forever.

Ugh I have to deal with this at my current job, except it's not an incomplete microservice migration it's by design and comes with a weird hobbled-together event queue implemented as a table in the DB and that has a known race condition that pops up every couple of weeks.


I'd say you'd first need to see if the limits can be fixed in the current setup.

If you really need to build a microservice to replace a part of your monolith. You need to hire a few devs that can act as a dedicated team.


I definitely think a reasonable degree of performance could be gained under the current structure by streamlining data access and maybe getting a bit smarter with caching. That still leaves us with non-scaling related problems of being unable to upgrade some very core dependencies which are stuck on some quite outdated versions.

Perhaps the strangler application approach could work to resolve that aspect over time.


Enumerating those limits and doing a boring risk benefit analysis of approaches to remove them. Sure microservices could fit in, but you need to get things pretty clean to split off a chunk in the first place


The whole monolith won't be a bottleneck. Work out what parts need to scale and factor those out.


20 people wouldn't make a million line in a decade. That's more like the output of a few hundreds people.


I work on a monolith. It was written by never more than ten developers, mostly fewer, in maybe 15 years. It contains just more than a million lines of code (1,074,010).

This figure excludes other code bases those developers worked on (for instance, at the peak of those ten developers includes several who were working on other discontinued products part time - even for most of their time).

It also excludes test data, schemas etc.

And there are several developers who took great pride in deleting code - I know I used to have more - lines than + lines in my few years.


I work on it every day. The earliest commit to the repo is 2010. The engineering team on average over that time is circa ~20 people.

So... Yeah. Thanks for denying my reality I live every day.


Haha, I love comments like this.

We've written over 10 million lines as 20-40 people in the past 15 years. I'll be sure to tell everyone we're not supposed to be moving so fast.


Funny enough, you wouldn't be able to work fast at all in a 10 MLOC codebase. Working with and navigating that amount of code is a bummer.

Imagine for a minute, if there were 50 developers (ignoring managers and administrative overhead) trying to understand 500 lines a day (a medium sized module in most languages), they still wouldn't be able to go through half the codebase in a whole year. So basically most of codebase can't be read or maintained.


We have a 4 million LOC ~15 year old monolith made by about 10 people that would like to have a word with you.


Tell me more about it? What industry is it, what does the software do?

Does it have a dictionary of 3M words to support spell check? :D


But what are the limits that warrant even considering microservices (a bit more detail please)? (Serious question)


We need to support an order of magnitude more daily active users than we currently do. I'm not exactly sure how close the current system would actually get to that, but my gut feel is it wouldn't hold up. It does OK as is, but only OK.

It's a combination of three different problems working against us in concert.

1) compute layer is multitenant but the databases are single tenant (so one physical DB server can hold several hundred tenant database with each customer having their own).

2) We're locked into some very old dependencies we cannot upgrade because upgrading one thing Cascades into needing to upgrade everything. This holds us back from leveraging some benefits of more modern tech.

3) certain entities in the system have known limits whereby when a customer exceeds a certain threshold the performance on loading certain screens or reports becomes unacceptable. Most customers don't come near those limits but a few do. The few that do sometimes wind up blowing up a database server from time to time affecting other clients.

For most of the domain stuff, to be honest I'd like to fix the performance problems and deadlocks by just making data access as efficient as possible in those spots. I think that could get us quite a bit more mileage if we took it seriously and pushed it.

For the single tenant database situation, I don't really know how to approach fixing that. I don't see us having enough resource to ever reengineer it as is. Maybe it's possible for us as a team, maybe it's not. The thinking is that for the parts of the domain we're able to split out, we could make those datastores multitenant.

There's also a bunch of integration code stuck in the monolith that causes various noisy neighbor problems that we are trying to carve out. I think that's a legitimate thing to do and will be quite beneficial.

But yeah... It's a path we're dipping our toes into this year in an effort to address all of these problems which are too big for us to tackle one by one.


Sounds like you would do best solving the problem you have clearly identified first - the need for a multi-tenant database solution with decent performance. To limit the re-engineering work necessary you could look to separate a coherent functional area as a service where query performance analysis shows heaviest contention, create a microservice for just that, then use best practice like adding a tenant_id key and a system that shards intelligently on that, e.g. citus.


That's reasonable. Also consider going the other way: keeping per-tenant logical databases, and splitting up some or all of the compute layer to have single tenancy or bounded tenancy. For example, if your compute layer is a web server, making multiple sets of webservers with something in front of them routing requests to a given set of servers based on a tenant identifier can chunk up your multiple-noisy-neighbors problem into at least multiple noisy "neighborhoods", with the (expensive) extreme of server-per-tenant. If your compute layer is e.g. a service bus/queue/whatnot worker, the same principles apply: multiple sets of workers deciding what to work on based on a tenant ID or per-tenant/group topics or queues. You can put the cross-cutting/weird workloads onto their own areas of hardware, as well.

I propose this because I think having database instances split up by tenant (even if multiple DBs share the same physical server) is actually a pretty good place to be, especially if you can shuffle per-tenant databases around onto new hardware and play "tetris" with the noisiest tenants' DBs. Moving back to multitenant-everything seems like a regression, and using (message|web|request) routing to break the compute layer up into per-tenant or per-domain clusters of hardware can often unlock some of the main benefits of microservices without a massive engineering effort.


>That's reasonable. Also consider going the other way: keeping per-tenant logical databases, and splitting up some or all of the compute layer to have single tenancy or bounded tenancy. For example, if your compute layer is a web server, making multiple sets of webservers with something in front of them routing requests to a given set of servers based on a tenant identifier can chunk up your multiple-noisy-neighbors problem into at least multiple noisy "neighborhoods", with the (expensive) extreme of server-per-tenant. If your compute layer is e.g. a service bus/queue/whatnot worker, the same principles apply: multiple sets of workers deciding what to work on based on a tenant ID or per-tenant/group topics or queues. You can put the cross-cutting/weird workloads onto their own areas of hardware, as well

This pretty much describes exactly where we are right now. We've been able to migrate the big customers to a new, less overloaded database server. We could continue to do that. I believe it's what you call a "bridge" architecture, so the compute layer is stateless and can serve any tenant. It's also got a queue/service bus to offload a lot of stuff that the web servers shouldn't be doing. That stuff is all on autoscaling but even that's not a panacea.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: