Microservices are a tax your startup probably can't afford
271 comments
·May 8, 2025asim
candiddevmike
Some resume driven developers will choose microservices for startups as a way to LARP a future megacorp job. Startup may fail, but they at least got some distributed system experience. It takes extremely savvy technical leadership to prevent this.
devin
In my experience, it seems the majority of folks know the pitfalls of microservices, and have since like... 2016? Maybe I'm just blessed to have been at places with good engineering, technical leadership, and places that took my advice seriously, but I feel like the majority of folks I've interacted with all have experienced some horror story with microservices that they don't want to repeat.
Espressosaurus
I feel like it's only in the last 5 years in the tech publicity sphere that I've seen pushback against microservices, only it feels like only the last year or two where I see it to the exclusion of influencers pushing microservices.
Things are different in the embedded space so I don't have personal experience with any of it.
jamesfinlayson
> In my experience, it seems the majority of folks know the pitfalls of microservices, and have since like... 2016?
Majority maybe, but not everyone. I was at a place in 2018 where a guy turned up declaring that microservices would solve all of the company's performance issues (he'd heard that AWS S3 was made up of over 500 microservices so we must do the same, but he obviously had no idea about the depth of features in S3 so hit take was more is better).
So we got like 30 microservices in the space of year which gave the company 10x complexity, complicated and fragile local development and... dubious wins like faster email sending, but no improvements to true bottle-necks of the system.
westurner
Does [self-hosted, multi-tenant] serverless achieve similar separation of concerns in comparison to microservices?
Should the URLs contain a version; like /api/v1/ ?
FWIU OpenAPI API schema enable e.g. MCP service discovery, but not multi-API workflows or orchestrations.
(Edit: "The Arazzo Specification - A Tapestry for Deterministic API Workflows" by OpenAPI; src: https://github.com/OAI/Arazzo-Specification .. spec: https://spec.openapis.org/arazzo/latest.html (TIL by using this comment as a prompt))
hnthrow90348765
Hiring will need to change to stop resume-driven development (can't eliminate it completely though), because you're likely to only get monolith roles if you only work on monoliths. Only being able to speak about microservices puts you in the "talk the talk, not walk the walk" category.
It would also nice to have less fear-driven career advice like "your skills go out of date" which drives people to try adopting the latest things.
Mountain_Skies
Keyword driven and filtered application processes also heavily incentivize adding into projects whatever is being posted on jobs sites. If microservices are part of a company's standard template for developer postings, people who want to work at that company will find a way to get it on their resume.
MDGeist
I've also seen the top down version where senior leadership like a CIO/CTO wants to put a huge "modernization" project on their resume and they don't care if it is impossible to maintain or falls over after they move on.
jamesfinlayson
Oh yeah, I've come to realise that the people at the top are just as bad. I was at a place where "consultants decided" that the company's two backbone systems should be replaced with off-the-shelf solutions. Very obviously a resume-padding project from the CIO who was in their first large company job, but which would have almost certainly crippled the company if it went ahead.
Thankfully the business and finance people at the company decided it would be an expensive and stupid idea it was shut down.
ang_cire
"Cloud Migration"
alaithea
And when it's your technical leadership leveraging buzzword-driven development to rise to the top, you're screwed.
wpollock
So true. It was in March that I saw on HN an advertisement for a vibe coder with 3 years experience. I believe the term "vibe coding" was invented a month before! Buzzword hiring is as bad as resume driven development.
bityard
It could also just be plain old overengineering. Like using Django and leaning on all of the magic contained within it just to implement a simple API that could instead be a very small Flask or FastAPI app.
about3fitty
Unforeseen scope creep is the reason to utilise Django over Flask, I feel.
Also, you can pick and choose what to use in Django similarly to Flask - it just has a higher initial learning curve.
Once you get to sufficient levels of complicated, leaning on established, documented, community supported design patterns and abstractions helps vs. sorting out your imports, making bespoke design choices, and doing a bunch of non-core value producing work.
dimal
I saw one startup with about fifty engineers, and dozens of services. They had all of the problems that the post describes. Getting anything done was nearly impossible until you were in the system for at least six months and knew how to work around all the issues.
Here’s the kicker: They only had a few hundred MAUs. Not hundreds of thousands. Hundreds of users. So all this complexity was for nothing. They burned through $50M in VC money then went under. It’s a shame because their core product was very innovative and well architected, but it didn’t matter.
jghn
> They only had a few hundred MAUs
Way too many companies believe they're really just temporarily embarrassed BigTech.
danielscrubs
Bad software dev. degrees that focus on fancy architecture that brings nothing to the table except overhead.
DanielHB
I have worked with a company with ~100k MAUs with ~4 teams, even then it often feels the system is over-microserviced (about two dozen services I think).
Definitely some stuff makes sense (especially since it has a lot of IoT stuff), but micro-service added was used mostly as a way to develop new stuff without having to deal with the legacy monolith. The core of the application could easily be a single service backed by one big RDBMS with a few ancillary services around it.
The legacy monolith is still there kicking and screaming, it didn't need "breaking up" it needed (and I assume still do) need a major refactoring.
dakiol
I’m not sure where’s the downside. The engineers got paid, they managed to put “founder” on their cvs, and enjoyed the ride. Now they are more prepared for their next adventure. The only ones who lost money were the investors, but nobody cares about them.
OutOfHere
Do you not care about the users either? They too will lose the service if the company shutters.
singron
> You'll have a monolith, it might break out into frontend, backend and a separate service for async background jobs
And when you break these out, you don't actually have to split your code at all. You can deploy your normal monolith with a flag telling it what role to play. The background worker can still run a webserver since it's useful for healthchecks and metrics and the loadbalancer will decide what "roles" get real traffic.
elevatedastalt
If you are building the same binary for all microservices you lose the dependency-reduction benefit microservices provide, since your build will still break because of some completely unrelated team's code.
roguecoder
If it is possible for that other team to merge a broken build, you are doing it wrong.
If you are concerned about someone else breaking your thing, good! You were going to eventually break it yourself. Write whatever testing gives you confidence that someone else's changes won't break your code, and, bonus, now you can make changes without breaking your code.
DanielHB
You have a point, but I wouldn't say this is a big deal unless there is a mammoth dependency somewhere that slow down things to a crawl. Then maybe that one part of the codebase can be broken into its own separate service.
But even then there are ways around this kind of problem with dynamic linking pre-built binaries and caching, but it is extra complexity that could be worse than managing multiple services. Docker cache can usually handle this pretty well though.
jounker
You’ll still get some isolation since not all pathways share the same code. It’s not all or nothing.
tstrimple
I put my team through this as an inexperienced lead about 15 years ago. We were a team of less than a dozen who had a nice single solution file that you could build and run the entire stack from. At the end we were looking at roughly a dozen services all which required orchestration to get them running and working together. First hand lessons in YAGNI and "do the simplest thing that works" which have stuck with me the rest of my career.
jayd16
it's weird that the your quote and your own explanation offer technical reasons for separate services but then you say it's not a technical pattern.
You'll need services. They're hard. If something is hard but it needs to be done, you should get good at it.
Like every fad, there a backlash from people seeing the fad fall apart when used poorly.
Services are a good pattern with trade offs. Weigh the trade offs, just don't do things to do them.
jimbokun
I thought the linked article about how Khan Academy eventually migrated to multiple services was a good example of when introducing micro services is a good idea:
https://blog.khanacademy.org/go-services-one-goliath-project...
They had already scaled the mono service about as far as it could go and had a good sense of what the service boundaries should be based on experience.
fallingknife
There are plenty of tech reasons for microservices. e.g. scaling high traffic services separately and separating low priority functionality from critical paths. I would agree that this is usually not a smart thing to do in a small org, but I have seen times where splitting out a high load path into a microservice has been very much worth it at a startup.
bluefirebrand
> scaling high traffic services separately
This is a great optimization once you have high traffic services
Building this way before you have any traffic at all is a great way to build the wrong abstractions because your assumptions about where your load will be might be wrong
gopher_space
Microservices are a technical solution to regional availability and pairing problems, and they start with a spreadsheet telling you when to make them based on requirements vs. cost. They're slow, expensive threads you should have a really good reason to use.
> Building this way before you have any traffic at all is a great way to build the wrong abstractions
These services only make sense to think about within specific traffic contexts. It'd be impossible to build the right abstraction.
motorest
> Microservices only pay off when you have (...) independently evolving domains.
I don't see any major epiphany in this. In fact, it reads like a tautology. The very definition of microservice is that it's an independently evolving domain. That's a basic requirement.
mindcrash
I know about a org with ~2-3 devs who decided microservices would be cool. I warned not to go that way because they would surely face delivery and other issues which they wouldn't have when building the solution based on a architecture archetype which could be a better fit for the team and solution, which I evidently decided should be a modular monolith. (the codebase at that point was already a monolith, in fact, but had a large amount of tech debt due to the breakneck speed in which features needed to be released)
They ignored me and went the microservices way.
Guess what?
2 years later the rebuild of the old codebase was done.
3 years later and they are still fighting delivery and other issues they would never have had if they didn't ignore me and just went for the "lame" monolith.
Moral of this short story: I can personally say everything this article says is pretty much true.
xnx
> 3 years later and they are still fighting delivery and other issues
Having added a fancy new technology and a "successful" project to their resume, they're supposed to move on to the next job before the consequences of their actions are fully obvious.
abirch
Microservices are GREAT when 1 team owns each service. I haven't seen a good use case when you have 1 team supporting multiple microservices.
eloisant
1 team supporting multiple services is not great, but a monolith with more than 50 developers working on it (no matter how you split your teams) isn't great either.
That's why I don't like the term "microservice", as it suggests each service should be very small. I don't think it's the case.
You can have a distributed system of multiple services of a decent size.
I know "services of a decent size" isn't as catchy as "go for one huge monolith!" or "microservices!" but that's the sensible way to approach things.
elktown
> but a monolith with more than 50 developers working on it (no matter how you split your teams) isn't great either.
Why can the game industry etc somehow manage this fine, but the only place where it's actually possible to adapt this kind of artificial separation over the network, it's somehow impossible not do it beyond an even lower number of devs than for a large game? Suggests confirmation bias to me.
The main problem with microservices is that it's preemptive, split whatever you want when it makes sense after-the-fact, but to intentionally split everything up before-the-fact is madness.
const_cast
I work on a monolith with ~1500 developers and it works pretty great.
The secret is that you're able to break a monolith apart, just like you can with microservices. You have APIs and modules of the monolith are responsible for their own thing. APIs are your contracts, just like in a microservice architecture.
The difference is that you can check if APIs are broken at compile time. In addition, you can view the API right in your IDE. In addition, your API isn't returning wishy-washy json with a half-assed OpenAPI spec - it's returning real types in a full-featured type system. And, cherry on top - you don't have to communicate over the network. Oh my god, you don't realize how many bugs and thousands of hours are wasted just working around that until you no longer have to. It's an immediate productivity boost.
But the best part is probably deployments. It's just so, so much more straightforward with one codebase.
monero-xmr
We solve the problem of 50 devs working in a single monolith with folder and file structure, separation of concerns, basic stuff like this
bcrosby95
I call them nanoservices.
__MatrixMan__
As long as that team built those microservices to solve whatever problem they're responsible for solving I think it's better to let the problem domain dictate how many you need. Better to have seams that make sense in terms of the surrounding code than to have them in arbitrary places based on the org chart.
The trouble comes when some political wind blows and reshuffles the org chart, and now you're responsible for some services that only made sense in the context of a political reality that no longer exists.
antonvs
I’m guessing you’re thinking of a certain kind of application (web apps perhaps?), where a monolith can make sense. But that’s but the only kind of application.
We have dozens of service components that are all largely independent of each other - combining them together would be purely a packaging decision, and wouldn’t really simplify much. In some cases, it wouldn’t make sense or even be possible at all.
An example is our execution agent, which executes customer workflows - that’s completely independent both conceptually and from a security perspective. Each agent instance executes a single flow at a time, for resource consumption and security reasons, which entails an ecosystem of services to manage that - messaging, data ingestion at scale (100K flows per day, multi-petabyte “hot” datastore for active data), orchestration, and other supporting services such as data access and network routing.
All of our teams support multiple services, and many of them qualify as microservices.
steveBK123
Every org I've tried to see push microservices did exactly the wrong version.
Rather than 1 micro service per team, which many devs.. it was some team that owns 20 services, generally way more services than developers.
It's probably just how non-lean Mag7 were in peak vs how lean most other orgs that try to ape them are.
jamesfinlayson
Yep, I remember working at a place where one team churned out a large majority of the microservices and the other three teams just kept on doing their thing.
The microservice team were especially terrible because the did all the initial work and basked in the "glory", but when it came to maintaining the services, they wanted nothing to do with it.
dec0dedab0de
I generally agree, but there are some decent use cases for one team to have multiple services micro or otherwise:
1. when the requirements are better served by a different language/location/environment/platform.. or by deploying a 3rd party app.
2. some of the services need to quickly scale up and down, and you have enough traffic for it to be worth it.
3. if you have a tight SLA for parts of the app but not all of it.
xingped
The best use case is promotion! Welcome to big tech, where all the teams get reshuffled every few months and every microservice exists because some dev needed a promotion. The greater the ratio of microservices to devs, the better your manager looks! (Dev work-life balance be damned, we pay you to ruin your life.)
roguecoder
I mean, "GREAT" until you need to do any kind of refactoring, or the company grows, or shrinks, or reorgs, or you have a feature that needs to change more than one service.
The "one team per microservice" makes code-enclosure style code ownership possible, but it is the least efficient way I have ever seen software written.
I've long wanted to hack an IDE so people are only allowed to change the Java objects they created, and then put six Java programmers in a room and make them write an application, yelling back and forth across the room. "CAN YOU ADD A VERSION OF THAT METHOD THAT ACCEPTS THIS NEW CLASS?" "SURE THING! TRY THAT?"
People discount the costs of microservices because they makes management's job easier, especially when companies have adopted global promotion processes. But unless they are solving a real technical constriant, they are a shitty way to work as an engineer.
Alupis
I suspect a lot of the issues teams encounter with microservices stem from a lack of cohesive understanding of microservices.
If people on the team continue to think about the "system" as a monolith (what they already know and are comfortable with), you'll hit friction ever step of the way from design all the way out to deployment. Microservices throw out a lot of traditional assumptions and designs, which can be hard for people to subscribe to.
I think there has to be adequate "buy-in" throughout the org for it to be successful. Turning an existing mono into microservices is very likely to meet lots of internal resistance as people have varying levels of being "with it", so-to-speak.
ellisv
> 2 years later the rebuild of the old codebase was done. > > 3 years later and they are still fighting delivery and other issues they would never have had if they didn't ignore me and just went for the "lame" monolith.
Sounds to me like every startup.
jeffwask
I had a similar experience setting up the Infra for an 8-12 microservice application. The project had been dragging and no one really understood what they were doing. When I started asking scale questions, the answer came back that this was an internal admin UI for 9-5 business that would have 5-10 users.
WHY? Just why?
ljm
One place I worked at got sold on microservices by Thoughtworks, along with a change to Java as the main language to be used.
As one would expect, they made bank from their consulting endeavor and rode off into the sunset while the rest of us wasted several years of our careers rewriting ugly but functional monolithic code into distributed Java based microservices. We could have been working on features and product but essentially were justifying a grift, adding new and novel bugs as we rebuilt stable APIs from scratch.
The company went under not long after the project was abandoned. Nobody, of course, would be held to account for it. I will no longer touch a tech consultancy like TW with a 10 foot barge pole.
wmf
What language was used before Java?
eddyfromtheblok
probably java, i doubt decision makers want a rewrite unless they want to bring in hungry young developers and accept some hiccoughs
dkkergoog
[dead]
jihadjihad
Microservices [0]
> grug wonder why big brain take hardest problem, factoring system correctly, and introduce network call too
> seem very confusing to grug
jayd16
The short answer is it adds monkey patching to languages that don't have it.
bunderbunder
Monkey patching is a great technique for hacking rudimentary testability into legacy software as part of your preparations for refactoring it for maintainability.
But when I see a plan to use it that doesn't include a plan for how to stop using it again ASAP, I get very worried.
actionfromafar
This is true, but monkey patching is scary. If you can switch over a monolith, and keep a rollback in case of trouble, do that.
Make small changes in the monolith a time, though.
jayd16
Btw, do any good, modern CI tools support incremental rollout of multiple in-flight changes on monoliths? As in patch A is live, team B wants to rollout A+B and team C wants to rollout A+C. Ideally, A+B+C will eventually go live.
Do cloud/paas providers deeply support this flow anymore? Every dashboard would need to compare across multiple live versions and I haven't tried that in a while.
BoardsOfCanada
Because the network call turns the rule into a law.
frollogaston
This is also why app backends don't really need statically typed languages, no matter how big the company is. You have a well-defined API on the front, and you have a well-defined DB schema on the back, that's good enough.
The static typing makes even less sense at finer code scopes, like I don't need to keep asserting that a for-loop counter is an int.
tauoverpi
Statically typed languages, when used correctly, save engineering time both as you extend your service and when thing go wrong as the compiler helps you check that the code you've written, to some degree, meets your specification of the problem domain. With a weak type system you can't specify much of the problem domain without increased labour but with a more expressive type system (and a team that understands how to use it) you can embed enough of the domain specification that implementing part of the business logic incorrectly or violating protocols turns into compile errors instantly rather than possibly leaking to production.
As for your comment on `any`, the reason why one doesn't want to fall back on such is that you throw out most of the gains of using static types with such a construct when your function likely doesn't work with `any` type (I've never seen a function that works on absolutely anything other than `id :: a -> a` and I argue there isn't one even with RTTI).
Instead you want to declare the subset of types valid for your function using some kind of discriminated union (in rust this is `enum`, zig `union(enum)`, haskell ADTs/GADTs, etc etc) where you set a static bound on the number of things it can be. You use the type system to model your actual problem instead of fighting against it or "lying" (by saying `any`) to the compiler.
The same applies to services, APIs, protocols, and similar. The more work the compiler can help you with staying on spec the less work you have to do later when you've shipped a P1-Critical bug by mistake and none of your tests caught it.
roguecoder
"Need"? Probably not. But unlike microservices they don't really have downsides (at least not with modern IDEs and the automatic refactorings they support) and they do offer some benefits.
Statically-types languages are a form of automatically-verified documentation, and an opportunity to name semantic properties different modules have in common. Both of those are great, but it is awkward that it is usually treated as an all-or-nothing matter.
Almost no language offers what I actually want: duck typing plus the ability to specify named interfaces for function inputs. Probably the closest I've found is Ruby with a linter to enforce RDoc comments on any public methods.
tauoverpi
I forgot to add that "like I don't need to keep asserting that a for-loop counter is an int." is exactly what is happening with a dynamically typed language that is exactly what the runtime ends up doing unless it has a built-in range type to avoid that overhead and the loop variable cannot change while looping. With a static type checker that can be eliminated upfront as the compiler knows that it's an int and not suddenly a string as it's impossible to change the variable's type once it's defined thus all of the overhead of RTTI can be erased.
Javascript has to check on each iteration that the item is an int and for arrays that the length of those arrays hasn't changed underneath along with a bunch of other things as the language doesn't guarantee that it can't change. Even the JIT compiler in use has to check "is this path still that I expect" as at any point the type of the variable used in the loop can change to something else which invalidates the specialization the JIT compiler emitted for the case of it being an int. When you don't use languages with static types you push all of this work on runtime which makes the program slower for every check it needs to do while offering none of the advantages you have with static types.
Thus with a for loop in say C you don't assert that it is an int each time you statically constrain it to be an int as the loop condition can well be based on something else even if int is the more common to use. For example in zig `for` only takes slices and integer ranges with any other type being a compile-error:
var example: [1 << 8]usize = @splat(0);
for (&example, 0..) |*num, int| num.* = int;
This is not more work than what you'd do in a dynamically typed language.cgannett
grug mention grug brain. grug also have grug brain. grug like grug. grugs together strong unless too many grugs then Overgrug think 9 grugs make baby grug in one month and grug not think it work like that
didip
Micro services show their benefits in a large organization.
It’s a tool to solve people issues. They can remove bureaucratic hurdles and allow devs to somewhat be autonomous again.
In a small startup, you really don’t gain much from them. Unless if the domain really necessitates them, eg. the company uses Elixir but all of the AI toolings are written in Python/Go.
echelon
If your application has different load or resource requirements, you should build separate services, even in a startup.
You can put most of your crud and domain logic in a monolith, but if you have a GPU workload or something that has very different requirements - that should be its own thing. That pattern shouldn't result in 100 services to maintain, but probably only a few boundaries.
Bias for monolith for everything, but know when you need to carve something out as its own.
At scale, you're 100% correct.
convolvatron
microservices can also cause organizational dependencies and coordination that wouldn't otherwise be necessary. i've seen it create at least as many people issues as solve them. one seemingly innocuous example is the policy of 'everybody just uses whatever services they want', which can hugely increase the ongoing maintenance requirements and seems to require that everyone learn everything in order to be functional. which never happens, which means you're always chasing people down.
hn_throwaway_99
I probably just haven't checked these comment threads enough yet because I'm surprised I haven't seen this posted, but even though this is a bit old now, https://youtu.be/y8OnoxKotPQ, there is a reason it resonated with so many. It's spot on with the downsides microservices can inflict.
I've certainly seen microservices be a total disaster in large (and small) organizations. I think it's especially important that larger organizations have standards around cross-cutting concerns (e.g. authorization, logging, service-to-service communication, etc.) before they just should "OK, microservices, and go!"
demarq
One of those teams need to go.
frollogaston
If they're doing two very different things, why?
demarq
At a larger organization this could be, but there is nothing elixir could possibly be doing for the startup that go would not do.
Remember the whole topic here is avoiding this tax
jerf
Microservices are the software architecture analog to Conway's Law. You can't help but introduce some sort of significant architecture boundary at the boundary between teams, and while that doesn't have to be "microservices" that's certainly a very attractive option. But on the flip side, introducing those heavier-weight boundaries on to yourself, internal to a team, can be very counterproductive.
I can't prove this scales up forever but I've been very happy with making sure that things are carefully abstracted out with dependency injection for anything that makes sense for it to be dependency-injected, and using module boundaries internally to a system as something very analogous to microservices, except that it doesn't go over a network. This goes especially well with using actors, even in a non-actor-focused language, because actors almost automatically have that clean boundary between them and the rest of the world, traversed by a clean concept of messages. This is sometimes called the Modular Monolith.
Done properly, should you later realize something needs to be a microservice, you get clean borders to cut along and clean places to deal with the consquences of turning it into a network service. It isn't perfect but it's a rather nice cost/benefit tradeoff. I've cut out, oh, 3 or 4 microservices out of monoliths in the past 5 years or so. It's not something I do everyday, and I'm not optimizing my modular monoliths for that purpose... I do modular monoliths because it is also just a good design methodology... but it is a nice bonus to harvest sometimes. It's one of the rare times when someone comes and quite reasonably expects that extracting something into a shared service will be months and you can be like "would you like a functioning prototype of it next week"?
roguecoder
Conway's law is about communication, not team boundaries. There is no requirement that we introduce a significant architectural boundary at the boundary between teams: companies choose to do so to avoid having cross-team communication.
The only way for significant architectural boundaries at team boundaries to not result in incredibly painful software, especially for a growing team, is to let the software organize the teams. Which means reorging the company whenever you need to refactor, and somehow guessing right about how many changes each component will need in the coming year.
It also means you can't have product and engineers explore a problem together, or manage by objective with OKRs since engineers aren't connected to business outcomes.
I know that all the ex-Amazonians are convinced this is the only way to build software, but it really, really isn't.
jerf
"Conway's law is about communication, not team boundaries."
I'm a spirit of the law sort of person, not the letter of the law. I don't care how you draw your internal organizational diagram; communication barriers are your team barriers.
It's for management to read the flow over time and keep the de jure boundaries somewhat sync'd with the de facto boundaries, but in the meantime the de facto ones are what will get written into your software.
xcskier56
Microservices make sense from a technical perspective in startups if:
- You need to use a different language than your core application. E.g. we build Rails apps but need to use R for a data pipeline and 100% could not build this in ruby.
- You have 1 service that has vastly different scaling requirements that the rest of your stack. Then splitting that part off into it's own service can help
- You have a portion of your data set that has vastly different security and lifecycle requirements. E.g. you're getting healthcare data from medicare.
Outside of those, and maybe a few other edge cases, I see basically no reason why a small startup should ever choose microservices... you're just setting yourself up for more work for little to no gain.
Scarblac
Splitting off a few services from an application is not the same as using microservices. With microservices you split off basically everything that would be a module in a normal application.
xcskier56
I think that really depends on your definition. But I will also contend that even splitting your system into 2 or 3 services if it's not for strong reasons will 100% slow you down and cause long term headaches.
One project that I helped design had to split out a segment of the system b/c the data was eligibility records coming from health plans. This data had very different security and lifecycle requirements (e.g. we have to keep it for 7 or 10 years). Splitting out this service simplified some parts but any time we need to cross the boundary between the 2 services, the work takes probably twice as long as it would if it were in a single service. I don't think it was the wrong decision, but it the service definitely did not come for free
Scarblac
> But I will also contend that even splitting your system into 2 or 3 services if it's not for strong reasons will 100% slow you down and cause long term headaches.
Even tiny backends usually have, from the start, a database server, some web server / proxy to handle incoming requests and load balance or cache them, and then something running your actual code that requests are passed on to. That's so normal that we don't even think of them as separate services anymore, but they are.
codr7
If you split off a small, isolated part of the application; that's pretty much the definition of a microservice.
Scarblac
Splitting off a few larger parts because they are self contained, have their own Ops characteristics and might be usable by other projects on their own, vs creating a service for every single entity in your domain modal (or whatever), that's quite a difference.
We have an auth service, a database service for raster geo data, a generic task runner. Those are services. They all have their own pretty unique architecture and stand on their own, they are used by but are separate from the project that they were made for.
Different services for customers, for orders, for invoices, for items in the shop, for items on an order, for promotions, that's microservices. They all look very similar and belong to the application.
(in my opinion this morning)
shooker435
In addition to having 1 service with vastly different scaling requirements, having 1 service with vastly different availability requirements may make sense to separate as well.
If you need to keep the lights or maintain an SLA and can do so by separating a concern, it can really reduce risk and increase speed when deploying new features on "less important" components.
Akronymus
I personally wouldnt even call those microservices, but rather treat them closer to how a DB server is usually separate from an application one.
hosh
One of the advantages of the BEAM / OTP ecosystem (Erlang, Elixir, and friends) is that you can construct "microservices" and think through what that means, all within a monolith. When it comes time to break it out, you can.
> Microservices only pay off when you have real scaling bottlenecks, large teams, or independently evolving domains.
The BEAM language platform can cover scaling bottlenecks (at least within certain ranges of scale) and independently evolving domains, but has many of the advantages of working with a monolith when the team is small and searching for product-fit.
Like anything there are tradeoffs. The main one being that you'd have to learn how to write code with immutable data structures, and you have to be more thoughtful on how concurrent processes talk to each other, and what kind of failure modes you want to design into things. Many teams don't know how to hire for more Erlang or Elixir developers.
siliconc0w
The biggest wins for microservices aren't really technical, they're organizational. They force you to break a problem down and allow each team to own a piece of it, including end to end delivery. This allows specialization of labor which is a key driver of productivity - including an ability to experiment and innovate. Every change is incremental by default, and well-documented external APIs are the only way to talk to other domains- no shared databases, filesystems, or internal APIs. It's not free and definitely takes some discipline and tooling to enforce shared standards (every service should have metrics, logging, tracing, discovery, testing, CI/CD, etc) but you'd need to build that muscle with a monolith as well.
utmb748
Could kept infra as a code, logging, auth and so on in packages, gRPC or message queues for communication, telemetry, monitoring/alerts and more stuff as a code too... got to the point creating new service was just new repo, name, port a resource utilization.
Agree with organizational win, also smaller merge requests in the team were superb.
Around 5-10 devs, monolith, we ran into conflicts more often, deployment, bigger merge requests, releasing by feature was problematic, microservices made team more productive, but rules about tests/docs/endpoints/code were important.
frollogaston
The DB part can also get technical as performance comes into play. Most startups are probably not encountering this problem, but they could.
no_wizard
This may explain some of the popularity resurgence of SQLite (including distributed SQLite)
It makes Database Per Customer type apps really easy, and that is something alot of SaaS products could benefit from.
frollogaston
Yeah, I keep telling people at work that we need to figure out how to make it easier for teams to manage their own DBs. There are so many teams trying to shove their data into some other team's DB.
utmb748
Was in startup which quit before hitting 1000 users in app at the same time, but performance was top priority so data layer stack was quiet big.
mikeocool
I pretty much agree with everything in this article — it’s next to impossible service boundaries right in a startup environment.
Though, if you’re on a small team and really want to use micro services two places I have found it to be somewhat advantageous:
* wrapping particularly bad third party APIs or integrations — you’re already forced into having a network boundary, so adding a service at the boundary doesn’t increase complexity all that much. Basically this lets you isolate the big chunk of crappy code involved in integrating with the 3rd party, and giving it a nice API your monolith can interact with.
* wrapping particularly hairy dependencies — if you’ve got a dependency with a complex build process that slows down deployments or dev setup — or the dependency relies on something that conflicts with another dependency — wrapping it in its own service and giving it a nice API can be a good way to simplify things for the monolith.
roguecoder
You only need microservices for massive scale or to enable micromanagement of teams, but that doesn't mean you have to give up on clear module boundaries.
You can get the architectural benefits of microservices by using message-passing-style Object-Oriented programming. It requires the discipline not to reach directly into the database, but assuming you just Don't Do That a well-encapsulated "object" is a microservice that runs in the same virtual machine as the other mircoservices.
Java is the most mainstream language that supports that: whenever you find yourself reaching for a microservice, instead create a module, namespace the database tables, and then expose only the smallest possible public interface to other modules. You can test them in isolation, monitor the connections between them, and bonus: it is trivial to deploy changes across multiple "services" at the same time.
DarkNova6
Or you have a good understanding of your logical boundaries and enforce them with ArchUnit.
no_wizard
They have their place. In my experience, a good rule of thumb[0] is if there are actual benefits from being a standalone service.
For example, we have a authentication microservice at work. It makes sense that it lives outside of the main application, because its used in a multiple different contexts and the service boundary allows for it to be more responsive to changes, upgrades and security fixes than having it be part of the main app, and it deploys differently than the application. It also adds enough intentional friction that we don't accidentally put logic where it doesn't belong as part of the user authentication process. It has helped keep the code focused on only primary concerns.
That said, you can't apply any of these patterns blindly, as is so often the case. A good technical leader should push back when the benefits don't actually exist. The real issue is lack of experience making technical decisions on merits.
This includes high level executive leaders in the organization. At a startup especially, they are still often involved in many technical decisions. You'd be surprised (well maybe not!) how the highest leadership in a company at a startup will mandate things like using microservices and refuse to listen to anything running counter to such things.
esafak
I don't think this merited a wiki link :)
no_wizard
Its an international forum, there may be at least 1 person who hasn't encountered this colloquialism before. It hinders nothing yet may be informative to someone who's unfamiliar.
Akronymus
And I dont see how the link detracts from the post at all. For ESL people, like me, it can be quite helpful to have such a link. (i find myself looking up such phrases quite often)
nazgulsenpai
Perhaps in consideration of a non-native English speaker who might not understand the phrase.
zsoltkacsandi
Most “benefits” assumed from separation can be achieved with clear interfaces and modular monoliths, without the cognitive and operational tax microservices impose.
> It also adds enough intentional friction that we don't accidentally put logic where it doesn't belong as part of the user authentication process.
Preventing misplaced logic is a matter of good code structure, well defined software development processes and team discipline - not something that requires splitting into a separate microservice, and definitely not something that you want to solve on system architecture level.
zbobet2012
The largest benefit of microservices has always been lifecycle management, and "clear interfaces" in "modular monoliths" does not in fact solve that. If you update the logging library in a monolith, everyone takes that updates even if it breaks half the teams.
That's a "large" organization problem. But large is actually, not that big (about 5-10 scrum teams before this is a very large problem).
It also means on critical systems separating high risk and low risk changes are not possible.
Like all engineering decisions, this is a set of tradeoffs.
zsoltkacsandi
> The largest benefit of microservices has always been lifecycle management, and "clear interfaces" in "modular monoliths" does not in fact solve that.
What lifecycle are we really talking about? There are massive monoliths - like the Linux kernel or PostgreSQL - with long lifespans, clear modularity, and thousands of contributors, all without microservices. Lifecycle management is achievable with good architecture, not necessarily with service boundaries.
> If you update the logging library in a monolith, everyone takes that updates even if it breaks half the teams.
This is a vague argument. In a microservice architecture, if multiple systems rely on the structure or semantics of logs — or any shared behavior or state - updating one service without coordination can just as easily break integrations. It’s not the architecture that protects you from this, but communication, discipline, and tests.
> It also means on critical systems separating high risk and low risk changes are not possible.
Risk can be isolated within a monolith through careful modular design, feature flags, interface boundaries, and staged rollouts. Microservices don’t eliminate risk - they often just move it across a network boundary, where failures can be harder to trace and debug.
I’m not against microservices. But the examples given in the comment I responded to reflect the wrong reasons (at least based on what I’ve seen in 15+ years across various workplaces) for choosing or avoiding a microservice architecture.
Microservices don’t solve coupling or modularity issues — they just move communication from in-process calls to network calls. If a system is poorly structured as a monolith, it will likely be a mess as microservices too — just a slower, harder-to-debug one.
no_wizard
>Most “benefits” assumed from separation can be achieved with clear interfaces and modular monoliths, without the cognitive and operational tax microservices impose.
Perhaps yes. Every situation should be evaluated on merits. This came across that there is also an assumption that we didn't try other solutions first - we absolutely did. Microservice is the best solution to solving the problems we needed solved in this case. Even better than modular monolith with clear interfaces.
>without the cognitive and operational tax microservices impose
When done correctly, I don't think there is a tax. Most operational questions should be automated away once discovered. The only 'tax' is that it lives separately from the larger application and is deployed independently, but I haven't seen in practice this add any notable overhead.
>Preventing misplaced logic is a matter of good code structure, well defined software development processes and team discipline
All true, and a microservice can aid all of these things too, but isn't the solution you should reach for when solving for these things and these things alone in my opinion. That said, myself and others have observed there is time saved on enforcing discipline around this issue once we separated the code away from the main application. I can't deny that hasn't been a good thing, because it has. It would be leaving information out about the benefits we'v experienced, and I see no reason to do that.
All told, completely dismissing the value of microservices as a potential solution is no different than completely dismissing other solutions in favor of microservices. Things have their place, there are pros and cons to them, and should be evaluated relative to their merit for the situation.
You may find you never implement microservices, or implement very few, or perhaps the needs of an organization is as such that its a pattern used most of the time, but the technical merits of doing so - with any decision of this nature, not limited to microservices - should have a backing justification that includes why other solutions don't fit
zsoltkacsandi
> Every situation should be evaluated on merits. This came across that there is also an assumption that we didn't try other solutions first - we absolutely did.
I completely agree. But this a little bit contradicts with your original comment that caught my eye:
> In my experience, a good rule of thumb[0] is if there are actual benefits from being a standalone service.
A rule of thumb is, by nature, a generalization — it simplifies decision making through heuristics. Benefits on the other hand always subjective, they can be interpreted in a given context.
And based on my experience, there will always be some benefits that can be used to justify factoring something out into a separate service. The challenge is that it's often easy to overemphasize those benefits, even when they don't outweigh the downsides. Your example with the auth service and the added friction is, in my view, a good illustration of a justification that might sound reasonable but can lead to unnecessary complexity. (Just to be clear, my intent here isn't to judge your decisions - I understand these trade-offs are often nuanced - and that's why again there is no good rule of thumb for this)
dkarl
My current take on microservices is that people pay serious attention to modularity and API design in the context of microservices. They work hard to break down the problem properly and design good interfaces between parts of the system.
In monoliths, they generally don't.
There's no logical reason why you couldn't pay as much attention to decomposition and API design between the modules of a monolith. You could have the benefit of good design without all the architectural and operational challenges of microservices. Maybe some people succeed at this. But in practice I've never seen it. I've seen people handle the challenges of microservices successfully, and I've never seen a monolith that wasn't an incoherent mess internally.
This is just my experience, one person's observations offered for what they're worth.
In practice, in the context of microservices, I've seen an entire team work together for two weeks to break down a problem coherently, holding off on starting implementation because they knew the design wasn't good enough and it was worth the time to get it right. I've seen people escalate issues with others' designs because they saw a risk and wanted to address it.
In the context of monoliths, I've never seen someone delay implementation so much as a day because they knew the design was half-baked. I rarely see anyone ask for design feedback or design anything as a team until they've screwed something up so badly that it can't be avoided. People sometimes make major design decisions in a split second while coding. What kind of self-respecting senior developer would spend a week getting input on an internal code API before starting to implement? People sometimes aren't even aware that the code they wrote that morning has implications for code that will be written later.
Theoretically this is okay because refactoring is easy in a monolith. Right? ... It is, right?
I'm basically sold on microservices because I know how to get developers to take design seriously when it's a bunch of services talking to each other via REST or grpc, and I don't know how to get them to take the internal design of a monolith seriously.
roguecoder
Bingo!
Every good monolith I've worked in (and I have worked in several, including one that was more than twenty years old) was highly-modular, well-designed with an easy-to-explain architecture.
The other thing they had in common was that code reviews talked about the aesthetics of the code and design, instead of just hunting for errors or skimming for security problems. It was relatively common to throw out the first proposed PR and start over, and that was fine because people were slicing the work small enough they were posting four to six PRs a week anyway.
It took the engineers at the company being willing to collaborate on the craft of software development and prioritize the long-term health of the code over short-term feature delivery. And the result of being willing to go a little bit slower day-to-day was that the actual feature delivery was faster than anywhere else I've ever worked.
Without a functioning professional culture, nothing is going to be great. But at least with microservices people do have to design an API at some point.
rho4
This is probably the first argument for microservices I heard that makes sense to me.
Not that I would ever want to give up our monolith, but we do experience the problems you point out.
> Microservices only pay off when you have real scaling bottlenecks, large teams, or independently evolving domains. Before that? You’re paying the price without getting the benefit: duplicated infra, fragile local setups, and slow iteration. For example, Segment eventually reversed their microservice split for this exact reason — too much cost, not enough value.
Basically this. Microservices are a design pattern for organisations as opposed to technology. Sounds wrong but the technology change should follow the organisational breakout into multiple teams delivering separate products or features. And this isn't a first step. You'll have a monolith, it might break out into frontend, backend and a separate service for async background jobs e.g pdf creation is often a background task because of how long it takes to produce. Anyway after that you might end up with more services and then you have this sprawl of things where you start to think about standardisation, architecture patterns, etc. Before that it's a death sentence and if your business survives I'd argue it didn't because of microservices but inspite of them. The dev time lost in the beginning, say sub 200 engineers is significant.