[HN Gopher] Modular monolith and microservices: Modularity is wh...
___________________________________________________________________
Modular monolith and microservices: Modularity is what matters
Author : BinaryIgor
Score : 136 points
Date : 2025-11-04 12:57 UTC (6 days ago)
(HTM) web link (binaryigor.com)
(TXT) w3m dump (binaryigor.com)
| mlhpdx wrote:
| In modular systems, monolithic or distributed, the nuance of
| "depends on" is often the crux of complexity. A C++ binary
| interface dependency is more likely to create work than a string
| correlated via convention.
| CharlieDigital wrote:
| I find it relatively easy to build "modular monoliths" by
| starting from "vertical slices"[0] at the outset. Vertical slices
| being primarily a feature organization strategy scales well
| pragmatically over time, IME. So when first starting out, you can
| keep it simple and just one monolith and one unit of deployment,
| but keep everything separated into their own feature folders with
| common things in a `shared` folder. When you get further along,
| it is possible to consider splitting things out by feature teams
| (if that's what works best) and still keep one runtime (just
| enabling/disabling different features at startup).
|
| The common bits can eventually be moved into a package dependency
| and referenced. Separate features if they become large enough can
| also be moved into separate packages, but part of the same
| monolithic codebase.
|
| In some cases, it can be easier still and just ship the entire
| runtime as-is (without any additional work to enable modularity)
| and simply route different endpoints (e.g.
| https://feature1.domain.com -> node set 1,
| https://feature2.domain.com -> node set 2) so you still have the
| option to monitor and scale the features differently based on
| their load profile and needs. This works great as long as cold
| starts are not a big concern (thereby adding a requirement for
| minimizing package size).
|
| I find this particularly easy on AWS, especially when deploying
| with Copilot CLI[1] because it makes it relatively easy to just
| route different sub-domains to different target groups. Now you
| have one singular container image that just gets scaled
| differently by route (e.g. a high volume feature gets bigger
| nodes and a dedicated route in Route 53).
|
| I find some teams have trouble thinking this way because devs
| many times are not involved enough in the deploy time
| considerations. For more involved app-level partitioning of
| modules, I have a practical example in C#[2] that would work
| equally well with something like Nest.js (or Elysia or Hono) by
| simply using environment variables to declare a "feature role"
| for the instance and dynamically enabling/disabling feature
| modules.
|
| [0] https://www.jimmybogard.com/vertical-slice-architecture/
|
| [1] https://aws.github.io/copilot-cli/
|
| [2] https://github.com/CharlieDigital/dn8-modular-monolith
| bognition wrote:
| Hard agree with this article. Split up your application by
| domains, create public apis between modules, understand your dep
| tree and keep it clean.
|
| The devil in the details is how you pull something like this off.
| At the end of the day is boils down to how do you enforce that
| your team does the right thing. You can have a single person that
| enforces standards with an iron fist, but this doesn't scale. You
| can teach everyone how this should work, but you're going to
| experience drift over time as people come and go. Or you can
| enforce it using technology and automation.
|
| In the cases of the first choice, its going to restrict how big
| your team can get and will end up eating all of the time of your
| one person.
|
| In the case of the second choice, a combination of the tragedy of
| the commons and regression to mean will degrade the system to
| spaghetti code.
|
| For the third scenario language choice matters here a lot. In
| Java with multi maven modules you can setup maven to forbid
| imports of specific module types allowing you to make modules as
| private/public. In Python you can't do any of this.
| stingraycharles wrote:
| As with all of these things, it needs to come from the very,
| very top, otherwise it doesn't work.
|
| In the end, AWS only happened because of Jeff Bezos' infamous
| "all intra-team communication now goes over HTTP, no
| exceptions, or you're fired"-email.
|
| The decision to prefer modules whenever they do the job, and
| defer only to microservices whenever they don't, seems like the
| kind of mantra that needs to come from the CTO and made part of
| the company culture's DNA.
| kevstev wrote:
| I think the real win with microservices is that when you are
| air-gapped between services, it really forces modularity and
| independence. Almost all the time in monoliths things slowly
| degrade into tangled dependencies. Once clean interfaces that
| just required a few specific parameters got lazy at some point
| and now the entire customer or order (or whatever) object is
| passed in and oops now the two are coupled.
|
| This is of course still possible with a microservices
| architecture, but the barrier to changing a rest contract/API
| is usually much higher, and people think a lot more about what
| is being passed across the interface since that data is going
| to be sent over a wire.
|
| Theoretically there is no difference, but its just far easier
| to slip when its one codebase and all it takes is someone a
| little too "LGTM" happy to let it through.
| dennisy wrote:
| The suggested "_contracts" folder lives at the top level of the
| modules or inside each feature/module?
|
| My guess is it's a top level folder which shows the cross module
| deps.
| netbioserror wrote:
| Modularity is how we put out some long-running fires at my
| company. Our old infrastructure, from people who had long-since
| left, was a cross-cutting mish-mash of monoliths that each tried
| to do everything. Data analysis to web serving to PDF generation.
| Written in everything from C to Perl to PHP to VBA. All were
| supported because different clients depended on different
| monoliths depending on when they were brought aboard. Basically,
| each language was intended to serve on whatever walled-garden
| platform any given employee or client was expecting to use it.
| They didn't even talk to each other, reimplemented the same
| algorithms, it was a mess.
|
| We spun out the specialized tasks (data analysis and PDF
| generation key among them) to native-compiled binaries or
| containerized packages like Gotenberg, started moving data around
| between modules via JSON, isolated the legacy monoliths to
| containers, unified on our now-modularized PHP backend, and have
| been working on updating or replacing any other pieces with new
| modules that can serve the task better. Our clients and non-
| engineering employees get antsy, but as a smaller company and a
| smaller programming team, we simply cannot maintain multiple
| 20-year-old codebases with near-total overlap. It makes no sense
| now, it didn't make sense when they were each created.
| drob518 wrote:
| IMO, 99.999% of apps should just use monoliths with basic 1+1
| redundancy. Unless you're working at a FANG and require just
| insane scale, you really don't need microservices or even lots of
| modularity. Just keep it simple. If you need more resources, buy
| a bigger system and scale up, not out. Only consider scaling out
| when you have exhausted scaling up.
| cedws wrote:
| I want to frame this and put it on my wall.
|
| Coming up with a greenfield microservice design with arbitrary
| responsibilities and intercommunication feels so stupid to me.
| Why not build the thing as a monolith and split parts out when
| you actually have scaling problems, instead of solving
| theoretical problems? Development velocity is going to be way
| higher and it gives developers a chance to discover problems
| without having to deal with the mental overhead of shipping N
| services all at once.
|
| The value of fast iteration cannot be overstated. Build the
| minimum viable version of the project first, then stress test
| it and break parts out when needed. This is much easier if you
| write modular code.
| xnx wrote:
| > Why not build the thing as a monolith and split parts out
| when you actually have scaling problems, instead of solving
| theoretical problems?
|
| I sometimes think programmers are the last people who should
| be writing software.
|
| The personality type that likes writing code is the exact
| type that likes tinkering around the edge and working on
| hypotheticals instead of addressing the problem at hand.
| hunter2_ wrote:
| One solution is to have the senior ones (who have already
| been-there-done-that and lost interest in those possibly
| low quality outcomes or low velocities from a business
| perspective) handle architectural decisions and keep things
| on the rails by way of writing issue specs that the juniors
| (with those green personalities) will implement, reviewing
| their code, mentoring them, etc. -- carefully matching an
| ideal threshold of autonomy for each programmer which will
| relax over time.
| drob518 wrote:
| I often see technologies adopted simply because they are
| the "latest and greatest" and "I want to stay relevant for
| my next job interview," rather than sound technical
| reasoning. Unfortunately, these decisions are made by
| humans and those humans have lots of cross-cutting decision
| criteria. It's a rare and highly mature engineer who can
| look at a problem and say "This old technology is a perfect
| fit."
| yearolinuxdsktp wrote:
| This is an organizational/tech leadership problem. Good
| technical leadership will put a kibosh on working on
| hypotheticals, because what you don't do is as important as
| what you do.
|
| Good programmers pride themselves on striking compromises
| and shipping a smaller thing sooner and they love
| iterating. The ones that are working on hypotheticals
| unchecked are not bad--just nobody has educated them.
| nyrikki wrote:
| The problem being, most people are in the cloud, and 'basic 1+1
| redundancy' simply doesn't work well in that distributed model
| and scaled up instances are expensive.
|
| The tooling is good enough to scale out, but micro services are
| mostly beneficial for organizational scaling.
|
| The value for other concerns is mostly situational.
| cedws wrote:
| Big instances are cheap relative to the cost of implementing
| horizontal scaling (at the app layer), plus engineering cost
| of Kubernetes or whatever your preferred orchestrator is,
| plus the heavy cloud tax on going "cloud native."
| nyrikki wrote:
| Even in the old days we were horizontal at the app layer,
| the persistence layer is what is hard.
|
| Unless you have some very specific need, I was horizontally
| scaling the app layer in 1996 even in true monoliths.
|
| K8s can be too much, but even when tooling and costs forced
| us to segment by technology layers, you never tried two
| node failovers.
|
| If you are trying to share state at the app level you would
| most reduce availability, because of split brain etc...
|
| The persistence layer was bad enough with shared quorum
| drives, heartbeat networks etc...
|
| It sounds like you are just spinning up to app servers or
| are you talking about active/passive or active/active two
| node clusters?
|
| That is vertically scaling in the way I understand the
| term, not just scaling out two instances.
| cedws wrote:
| I'm talking about true horizontal scaling where load is
| sharded across replicas. Active-passive is easy. Handling
| web traffic is also easy. Backend business logic is not
| so easy.
|
| If you're message driven you can maybe have a set of
| replicas consume off of a message bus like Kafka or NATS,
| but now you need to maintain that cluster, as well as
| build a messaging layer. What protocol do you use, etc?
|
| Then there's the question of what triggers scaling
| up/down. The simple way is scale on CPU utilisation but
| then you get flapping issues. Or if load isn't completely
| evenly distributed you get one replica starving for CPU
| whilst the rest have too much.
|
| All of these questions go away if you can just provision
| a big node and be comfortable that it will be able to
| handle anything within reason.
| sokoloff wrote:
| I probably agree more than this comment might suggest, but I
| think that you run into organizational dynamics limits before
| you run out of vertical scaling limits.
|
| Shipping a monolith worked on by 250+ devs in 30 teams is slow
| due to the coordination needed as compared to 30 teams shipping
| 50-75 services.
|
| Vertical scaling can take you really, really far operationally.
| BinaryIgor wrote:
| That's the strongest argument I can see; once you can get
| above 5 - 10 teams, you run into the organizational issues.
|
| But again, most systems will never be there ;)
| 3uler wrote:
| I've always maintained that microservices are more for
| scaling people than scaling systems.
| BinaryIgor wrote:
| 100%; and if you have a solid structure in your modular
| monolith - it's trivial to take one or two
| problematic/performance sensitive modules and turn them into
| independent services. But most of the systems will never get up
| to this scale
| drob518 wrote:
| Right. Launch it, get customers, see what needs to be scaled
| up, buy a new server, and only then, carve out that
| problematic piece and scale it as a separate service. But by
| then you have the data to justify it and you have sidestepped
| a lot of complexity.
| zerop wrote:
| Microservices created the need for more teams, devs,
| coordination, meetings, scrum teams, scrum master, Jira, CI CD
| tools, cloud market and so on.. It is very well planned :)
| 9rx wrote:
| It was -- until developers started shouting "You don't need
| microservices. Just build a monolith."
|
| Then, uncoincidentally, they started crying about how they
| couldn't find work anymore.
| drob518 wrote:
| There are a lot of people who shouldn't be doing software
| engineering.
| 9rx wrote:
| 2015:
|
| - "I'm struggling to find work and need to make money.
| What can I do?"
|
| - "Learn to code, good buddy! Software engineering solves
| all problems."
|
| 2025:
|
| - "I learned to code and am still struggling to find work
| and need to make money."
|
| - "You shouldn't be doing software engineering."
| drob518 wrote:
| Yep, exactly.
| 9rx wrote:
| Exactly.
| vb-8448 wrote:
| The 1 + 1 setup can make it up even on FAANG level, it's not
| more about how much a single machine can do but more about how
| i you organize teams and what kind of swe you want ... bigger
| companies want "fungible" like SWEs, and it's easier to swap a
| swe in a microservices architecture vs monoliths.
| drob518 wrote:
| It's easier swapping an engineer into a module associated
| with a well-architected monolith than swapping them into a
| microservice. Given a monolith uses function calls that are
| fast and don't fail when the network cable is unplugged or
| DNS gets misconfigured, it's arguably a lot easier. There are
| fewer failure modes and overall lower complexity. No circuit
| breakers, back offs, retries, etc.
| antonvs wrote:
| > If you need more resources, buy a bigger system and scale up,
| not out. Only consider scaling out when you have exhausted
| scaling up.
|
| This happens at all sorts of companies that are not FAANGs, and
| don't have "insane scale". There's an extremely large spectrum
| between the web site for Joe's Coffee Bar and Amazon. On
| commodity hardware, you hit issues with scaling up a monolith
| long before you reach FAANG level or "insane scale".
|
| I've worked with multiple startups that have hit scaling limits
| with their monoliths. Inevitably, dealing with that is a huge
| problem because the monolith was developed with few resources
| under heavy time pressure. Modularity is lacking, breaking it
| up is difficult. Individual devs are often inclined to say
| that's just a skill issue, and that may have some truth to it,
| but managing those skill issues is a big part of what corporate
| software development is about.
|
| This can have a huge impact on a company's funding, ability to
| deliver new features, ability to scale development, and of
| course ability to scale the user base. Typically, by the time
| they hit that wall, scaling the monolith horizontally is not a
| great option, because it wasn't designed to support that.
|
| It's often been observed that microservices are a primarily an
| organizational tool, and that's true. But organization is
| critical if you have multiple development teams.
|
| That doesn't necessarily mean every app should consist of
| hundreds of tiny microservices. But there can be enormous
| benefits from implementing an app from the start as independent
| services based on its natural divisions between modules.
| drob518 wrote:
| Without knowing what the cause of those scaling problems were
| (CPU, memory, IOPS, etc.), I'll have to take your word for
| it. But having worked at several startups, I can say that
| it's a common disease that engineers start to optimize for
| the "we're going to have a jillion customers" case long
| before they even have one customer, and that adds a whole
| bunch of complexity, cost, and schedule to the development,
| which increases burn and stands between the company and its
| cash-flow positive date. In fact, if it forces another round
| of funding, it can wash out the original employees or even
| kill the company. On the other hand, it's easy to raise money
| when you have customers and are near cash flow positive. You
| can do a HUGE amount with a modern server with 100+ cores, TB
| of RAM, etc. IOPS are the one thing which hasn't scaled quite
| as well. So yea, I'm being a bit hyperbolic in saying you
| need to be a FANG to consider it, but I think the main point
| still stands: most applications, if well-written, can be run
| well on today's hardware and scaled up if they need it while
| eliminating most of the complexity associated with scaling
| out. Yes, there are exceptions to that rule of thumb.
| toonewbie wrote:
| I recommend checking out concept design [0] from Daniel Jackson
| [1]. It provides a framework to enforce modularity while building
| meaningfully designed software.
|
| I have done a bit of work implementing a prototype framework for
| coding concepts using TypeScript [2], and it has worked
| beautifully for the Software Design class at MIT Daniel teaches.
| I think the newest iteration of class this semester uses a
| different approach to code concepts, but it's still a research
| space.
|
| [0] https://essenceofsoftware.com/tutorials/
|
| [1] https://people.csail.mit.edu/dnj/
|
| [2] https://61040-fa24.github.io/pages/concept-
| implementations.h...
| Pxtl wrote:
| Imho the web and SQL brought us to the ridiculous point that the
| most obvious way to "modularize" was to use fully separate
| isolated apps and servers.
|
| This is because web and SQL are both dominant platforms for
| enterprise and are both allergic to modularization. Everything is
| global, everything is shared. Isolation and private interfaces
| are either impossible or late-added afterthoughts on a global-
| first platform.
|
| So faced with this, it made sense that the only way to provide
| modularization was to use isolated computers that only talk over
| sluggish HTTP.
| dragonwriter wrote:
| > This is because web and SQL are both dominant platforms for
| enterprise and are both allergic to modularization. Everything
| is global, everything is shared. Isolation and private
| interfaces are either impossible or late-added afterthoughts on
| a global-first platform.
|
| That's not actually true of SQL-based RDBMSs (except embedded
| ones like SQLite), and hasn't been for longer than the median
| developer has been alive, but it is probably fairly accurate of
| the average app developers understanding of SQL.
| ecshafer wrote:
| I feel like this misses too much real world context and caveats.
|
| What is the problem with Monoliths? Nothing. Until there is. The
| problem with monoliths is when you have a million LoC Java
| application that is on Java 6, and will take months of work to
| get up to date, take 20 minutes to load on a dev machine, starts
| to fail because its getting too big for a dev machine to handle,
| can't bring in any new dependencies because of how old the Java
| version is, and has an old bespoke Ant + Maven + Jenkins + Bash +
| Perl build and deploy system that has been built up over the last
| 30 years.
|
| So what do you do? Breaking off pieces of the code into
| microservices that can run on a new Spring boot and can run on a
| newer nice IaC set up is an easy win. Sure you basically have a
| microlith, but it increases your dev velocity.
|
| I think Monolith issues are typically a symptom of a few other
| things: 1. accumulated deferred maintenance and tech debt 2.
| Inadequate developer tooling 3. Inadequate CICD tooling 4. Rarely
| scale until you really start to hit the size of like Google,
| Uber, Facebook, etc.
| stavros wrote:
| If the monolith takes 20 minutes to load, just wait until you
| see how long it takes for all the microservices to start up.
| netdevphoenix wrote:
| Indeed! And getting odd behaviour and trying to see what went
| wrong and takes you ages until your realise that one of the
| 10 micro services was failing rather than it being an actual
| bug in the micro service you were playing with. Plus, upgrade
| and maintenance gets multiplied by 10.
| nine_k wrote:
| I watched 1000-node microservice systems start up. Most nodes
| start really fast, and most of the system is up in seconds,
| maybe 15-20 seconds, as the flurry of service registration
| passes. A few nodes would take inordinate time to start up,
| apparently because they are unlucky and repeatedly get less
| CPU, less I/O, more retries on congested links, etc.
|
| But you don't need to do this on your dev machine. Nearly a
| decade ago at GrubHub we already had a setup that allowed to
| run a few microservices under development locally, while
| relegating the rest to the staging environment that just runs
| every microservice, like prod, but in small quantities.
|
| A JVM-based microservice used to take, say, 16-20 MiB of RAM;
| a 50-MiB service was considered a behemoth that may need
| slimming down. You could run quite a number of 20-MiB
| containers on a laptop with 16 GiB, along with all your dev
| setup, some local databases, etc.
| ecshafer wrote:
| Well in my experience, you only need to have 2-3
| microservices running to do work locally.
| BinaryIgor wrote:
| If you go up to this scale then yes - it probably makes sense
| to have a few - reasonably amount - services. But I would ask -
| why does it take for this kind of app to be up and running so
| long? It's rather not because of its code size - something else
| has gone wrong :)
| zelphirkalt wrote:
| I think updating dependencies is maybe the most important
| point. Different microservices can have different versions of
| libraries and frameworks, as long as their APIs return and do
| what they should, other microservices don't need to care about
| what version of some library is used. Being able to update
| dependencies for a smaller amount of code at a time can make
| all the difference between "no, that will be too much work
| right now" and "it's doable".
|
| But, if you have a modular monolith, it will be easy to split
| it up into separate services, whether microservices or just
| services. It will be a good test to see how modular your
| system/monolith really is.
| vacuity wrote:
| I think the framing of "monoliths vs. microservices", with the
| implication that you must either have a mountain of a codebase
| or a beach of grains of code-sand, is not helpful. Good
| modularity means that different levels of tradeoffs can be made
| without huge effort.
| BinaryIgor wrote:
| True, that's why I titled the article "Modular Monolith and
| Microservices: Modularity is what truly matters". Modularity
| is crucial here - you can mix it up on multiple levels;
| having a single modular monolith, a few bigger services that
| have many modules inside each or finally, microservices where
| you treat each service itself as a module.
|
| Modularization is what's primary here and gives you
| flexibility; not having one vs multiple units of deployment
| 9rx wrote:
| _> microservices where you treat each service itself as a
| module._
|
| Microservices is where you treat each team of people as
| their own independent business unit. It models services
| found in the macro economy and applies the same patterns in
| the micro economy of a single organization. Hence the name.
|
| The clearest and probably simplest technical road to
| achieving that is to have each team limit exposure to their
| work to what can be provided over a network, which is I
| guess how that connotation was established. But
| theoretically you could offer microservices with, for
| example, a shared library or even a source repository
| instead.
| Scubabear68 wrote:
| Sorry, you lost me at "hence the name".
|
| Microservices was originally envisioned to literally
| create the smallest possible service you could, with
| canonical Netflix use cases being literally only one or
| two endpoints per microservice.
|
| Which is great I guess for FAANGs. But makes no sense for
| just about anyone else.
| 9rx wrote:
| _> Microservices was originally envisioned to literally
| create the smallest possible service you could_
|
| "Micro web services" was coined at one time, back when
| Netflix was still in the DVD business, to refer to what
| you seem to be speaking about -- multiple HTTP servers
| with narrow functional scope speaking REST (or similar)
| that coordinate to build something bigger in a Unix-style
| fashion.
|
| "Microservices" emerged when a bunch of people at a tech
| conference discovered that they were all working in
| similar ways. Due to Conway's Law that does also mean
| converging on similar technical approaches, sure, but
| because of Conway's Law we know that team dynamics comes
| first. "Microservices" wasn't envisioned -- it was a
| label given to an observation.
| foldr wrote:
| Even in this hypothetical scenario, you're radically
| rearchitecting your entire product to save "months of work" and
| the cost of some beefier dev machines. How can that be
| rational?
| andoando wrote:
| The other big advantage is you can monitor and scale your
| services independently and decouple outages.
|
| If one endpoint needs to scale to handle 10x more traffic, its
| wholefully inefficient to 10x your whole cluster.
|
| Ideally you write the code as services/modules in a monolith
| imo. Then you can easily run those services as separate
| deployments later down the line if need be
| j45 wrote:
| You also have to determine how much traffic for monitoring is
| due to microservices itself - where the messaging and logging
| might happen in memory, it now has to be read, and written,
| in much more expensive exectuions.
|
| There's not one silver bullet. It's not 100% monoliths, or
| 100% microservices for all.
|
| Learning from the things we don't do, haven't done yet, in
| the ways you haven't yet thought of also helps expand one's
| skills.
|
| This is because clever architecture, will always beat clever
| coding.
| andoando wrote:
| Im not sure what you mean. Whats the difference in
| messaging and logging? What you mean by messaging?
|
| Like through network versus code running on the same
| machine? Cause that should already be distributed unless
| you can really fit your whole needs on a single machine
| nitwit005 wrote:
| There isn't that much difference between an application and a
| library. You can always create multiple deployments of the
| same code configured differently.
|
| We have an app with two different deployments. One is serving
| HTTP traffic, and the other is handling kafka messages. The
| code is exactly the same, but they scale based on different
| metrics. It works fine.
| jimbokun wrote:
| In other words: when to split services and when to split
| repos are orthogonal concerns.
| Scubabear68 wrote:
| I think the big problem no one speaks about is the name
| "microservices" was incredibly poorly named.
|
| Your design goal should not be "create the smallest service you
| can to satisfy the 'micro' label". Your design goal should be
| to create right-sized services aligned to your domain and
| organization.
|
| The deployment side is of course a red herring. People can and
| do deploy monoliths with multiple deployments and different
| endpoints. And I've seen numerous places do "microservices"
| which have extensive shared libraries where the bulk of the
| code actually lives. Technically not a monolith - except it
| really is, just packaged differently.
| mikepurvis wrote:
| Another key is that you should always be able to reasonably
| hack on just one of the "services" at once-- everything else
| should be able to be excluded completely or just run a
| minimal mock, for example an auth mock that just returns a
| dummy token.
|
| If you've got "microservices" but every dev still has to run
| a dozen kubernetes pods to be able to develop on any part of
| it, then I'm pretty sure you ended up with the worst of both
| worlds.
| Sohcahtoa82 wrote:
| > Your design goal should not be "create the smallest service
| you can to satisfy the 'micro' label".
|
| A place I worked at years ago did what I effectively called
| "nano-services".
|
| It was as if each API endpoint needed its own service. User
| registration, logging in, password reset, and user preference
| management were each their own microservice.
|
| When I first saw the repo layout, I thought maybe they were
| just using a bunch of Lambdas that would sit behind an AWS
| API Gateway, but I quickly learned the horror as I
| investigated. To make it worse, they weren't using Kubernetes
| or any sort of containers for that matter. Each nanoservice
| was running on its own EC2 instance.
|
| I swear the entire thing was designed by someone with AWS
| stock or something.
| Scubabear68 wrote:
| I know one place that did all of their transactional
| payment flow through lambdas. There were about 20 lambdas
| in the critical auth path, and they regularly hit the AWS
| per-global-account limits.
|
| Another place did all their image processing via lambdas,
| about fifty of them. They literally used lambdas and REST
| calls where anyone sane would have done it in one process
| with library calls. It cost them tens of thousands of
| dollars a month to do basic image processing that should
| have cost about $100 or so.
| huherto wrote:
| This is my take too. Just because of the name, people try to
| make them as small as possible and we end up with too many of
| them.
| ecshafer wrote:
| I agree with this. Personally I think the two pizza team
| model, single responsibility isnot a great idea. Most
| successful "microservices" model I've worked on actually had
| 100ish devs on the service. Enough to make on-call, upgrades,
| maintenance, etc. really spread out.
| bikelang wrote:
| Agreed. This is why I prefer the term "service oriented
| architecture" instead. A service should be whatever size its
| domain requires - but the purpose is to encapsulate a domain.
| j45 wrote:
| There's no reason monoliths can't have modularity that can be
| packaged as microservices.
| andoando wrote:
| This is the way. Best of both worlds
| antonvs wrote:
| There is a reason, which is the overall capabilities of the
| development org at a company. It takes more discipline and
| skill to do that consistently. Separate services are a
| forcing function for modularity.
| jimbokun wrote:
| But there are reasons for having more than one repo in a
| company, past a certain number of engineers.
| j45 wrote:
| I don't disagree.
|
| My statement is that monoliths are capable of containing
| microservices and modular code to power them.
| netdevphoenix wrote:
| > The problem with monoliths is when you have a million LoC
| Java application that is on Java 6, and will take months of
| work to get up to date, take 20 minutes to load on a dev
| machine, starts to fail because its getting too big for a dev
| machine to handle, can't bring in any new dependencies because
| of how old the Java version is, and has an old bespoke Ant +
| Maven + Jenkins + Bash + Perl build and deploy system that has
| been built up over the last 30 years.
|
| - a million LoC Java application that is on Java 6 -> Congrats,
| now you have two half a million LoC Java application on two
| different Java versions. And if the set up is like most apps,
| you will likely need both running to debug most issues because
| most issues happen at the system to system interface
|
| - take 20 minutes to load on a dev machine -> that is fair
| enough, I have only ever seen an app that takes that long on a
| modern machine once, most shops doing micro services don't have
| apps that big
|
| - has an old bespoke Ant + Maven + Jenkins + Bash + Perl build
| and deploy system that has been built up over the last 30 years
| -> you can have the same problem on a micro service
| architecture, you can actually have that problem multiplied by
| 10 and now you can spend a whole sprint updating dependencies.
| Fun!
|
| Breaking off pieces of the code into microservices that can run
| on a new Spring boot and can run on a newer nice IaC set up is
| an easy win -> You conveniently forget to mention the
| additional team to fix issues related to system to system
| communication
| tracker1 wrote:
| > > has an old bespoke Ant + Maven + Jenkins + Bash + Perl
| build and deploy system that has been built up over the last
| 30 years
|
| > you can have the same problem on a micro service
| architecture, you can actually have that problem multiplied
| by 10 and now you can spend a whole sprint updating
| dependencies. Fun!
|
| Definitely true... Not to mention when your entire
| orchestration becomes too big to run anything locally, that's
| where the real fun and complexity starts. There's definitely
| such a thing as too many micro-services, or too micro for
| that matter...
| jimbokun wrote:
| You are focusing on the hyperbole to ignore the basic point:
| being unable to change, build and release different parts of
| the code independently can and eventually will bring your
| development velocity to a crashing halt.
| simianwords wrote:
| > And if the set up is like most apps, you will likely need
| both running to debug most issues because most issues happen
| at the system to system interface
|
| wrong. well architected services would have a good interface
| and problems rarely span multiple services.
|
| >- has an old bespoke Ant + Maven + Jenkins + Bash + Perl
| build and deploy system that has been built up over the last
| 30 years -> you can have the same problem on a micro service
| architecture, you can actually have that problem multiplied
| by 10 and now you can spend a whole sprint updating
| dependencies. Fun!
|
| this is no un-nuanced. the point is if you have decomposed
| the codebase into smaller ones - migrations are easier.
| ninetyninenine wrote:
| No you completely missed his point.
|
| He's saying instead of using "microservices" to modularize your
| shit, you can use folders to modularize your shit. Folders?
| Files? When someone told me that I could use folders to
| modularize stuff instead of entire microservices the concept
| was so foreign to me that it opened up a hole new world.
| vjvjvjvjghv wrote:
| Then your beautiful microservice gets old and requirements
| change. Now you have to fix 15 different services, rework
| interfaces, coordinate with multiple teams. My golden rule is:
| if you can't do a monolith right, you will fail even more at
| microservices. I think moving some parts into services but I
| see a lot of simplistic "we have users, so let's do a user
| service. Then we have files, so let's do a file service". This
| will become a maintenance nightmare in my view.
| jimbokun wrote:
| No you can update ONLY the micro services that are impacted
| by the new requirements without impacting the other micro
| services.
|
| > "we have users, so let's do a user service. Then we have
| files, so let's do a file service".
|
| Agreed that this is not a useful heuristic for deciding how
| many services you need.
| SAI_Peregrinus wrote:
| > No you can update ONLY the micro services that are
| impacted by the new requirements without impacting the
| other micro services.
|
| Only if the new requirements don't require a breaking API
| change. Microservices make API breaks more difficult, since
| they're loosely coupled it's harder to find all users of an
| old API & ensure they're updated than it is with a tightly-
| coupled system. Microservices make non-breaking changes
| easier, and help ensure all access is gated through an API.
| yearolinuxdsktp wrote:
| Being saddled with an old code base with a mountain of tech
| debt does not invalidate the OP's argument about modularity and
| microservices. I feel for your pain. You describe a great
| approach of how to tackle a monolith with mountains of tech
| debt by breaking out a microservice.
|
| However, having a monolith does not automatically mean you
| abandon all addressing of tech debt. I worked on a large
| monolith that went from Java 7 to Java 21, it was never stuck,
| had excellent CI tooling, including heavy
| integration/functional testing, and a good one-laptop DX, where
| complex requests can be stepped through in your IDE all the way
| thru in one process.
|
| Your argument dooes not invalidate a service-oriented approach
| with large (non-micro) services. You can have a large shared
| code base (e.g. domain objects, authentication and
| authorization logic, scheduling and job execution logic) that
| consists of modular service objects that you can compose into
| one or three or four larger services. If I had to sell that to
| the microservice crowd, I would call them "virtualized
| microservices", combined into one or many several deployment
| units.
|
| In fact, if I were to start a new project today, I would keep
| it a monolith with internal modularity until there was a clear
| value to break out a 2nd service.
|
| Also, it is completely valid to break out into microservices
| things that make absolute sense and are far detached from your
| normal services. You can run a monolith + a microservice when
| it makes sense.
|
| What doesn't make sense is microservices-by-default.
|
| The danger of microservices-by-default is that you are forced
| to do big design up-front, as refactoring microservices at
| their network boundaries is much more difficult than
| refactoring your internal modules.
|
| Also, microservices-by-default means you now have so many more
| network boundaries and security boundaries to worry about. You
| now have to threat-model many microservices because of the
| significantly increased number of boundaries and network
| surface. You are now forcing your team to deal with
| significantly more distributed computing aspects right away--so
| now inter-service boundaries are network calls instead of in-
| process calls, requiring more careful design that has to
| account for latency, bandwidth and failure. You now have to
| worry about the availability and latency of many services, and
| risk a weakest-link-in-chain service bring your end-user
| availability down. You waste considerably more computing
| resources by not being able to share memory or CPU across all
| these services. You will end up writing microservice caches to
| serve over the network that which could've been an in-process
| read. Or if you're hardcore about having stateless
| microservices (another dogmatic delusion), you will now be
| standing up Redis instances or Memcached for your caches--to be
| transferred over the network.
| rTX5CMRXIfFG wrote:
| > So what do you do? Breaking off pieces of the code into
| microservices that can run on a new Spring boot and can run on
| a newer nice IaC set up
|
| I'm not sure why that's your first instinct as opposed to
| splitting up your monolith into multiple Java packages that
| only have a downstream dependency relationship. (This is the
| second option in the article.) Spinning up microservices is
| hardly an easy win compared to this approach.
| jimbokun wrote:
| > Breaking off pieces of the code into microservices that can
| run on a new Spring boot
|
| I was with you until this part.
|
| The correct answer is:
|
| > Breaking off pieces of the code into microservices that no
| longer have Spring Boot as a dependency so you are not pulling
| in unknown numbers of unneeded dependencies that could have an
| unexpected impact on your application at surprising times, and
| forced version upgrades for security patches that also make
| major semantic breaking changes.
| loglog wrote:
| > Breaking off pieces of the code into microservices ... I was
| with you until "into". Then continue with "Maven modules" (or
| Gradle modules, or some other kind of modules) and solve some
| real issues instead of imaginary deployment structure issues.
| dboreham wrote:
| Yes, but no once you factor in Conway. Teams deploy and are
| responsible for their own running services. The line of
| demarcation is via TCP connections, not the call stack inside
| some process owned by 9 teams.
| BinaryIgor wrote:
| Unless you have a lot of teams - like 10 and more - it's quite
| manageable to work on a solidly modularized monolith. But in
| general you're right; after your reach certain threshold of
| teams, it makes sense to have a few, not one, units of
| deployment
| ReflectedImage wrote:
| So originally software development used micro-services rather
| than modules. A lot of software developers get this wrong and
| think modules were first. They were not. The software developers
| just grew up during the module fad caused by the personal
| computer before everything circled back around to micro-services.
|
| The purpose of OOP was to replicate the benefits of micro-
| services in single user environments. A class corresponds to a
| service type and an object an physical instance of the service.
|
| So why did Monoliths/modules fail? Some pretty simple issues,
| incomplete isolate between the modules, memory corruption and
| performance issues easily propagate between the "isolated"
| modules.
|
| But the main killer is compile times. Monoliths/module based
| programs require massive compile times that grow quickly with the
| size of the program.
| BinaryIgor wrote:
| If you have each module as independently versioned package, the
| compile times are not slow, since you only need to compile
| modules that have changed and modules are provided as compiled
| dependencies.
|
| Each individual module is fast to compile.
| bloppe wrote:
| Is there somewhere I could read more about this take?
| foobarian wrote:
| Sounds like some mystical time during mainframe heyday era.
| ReflectedImage wrote:
| Of course: https://www.youtube.com/watch?v=wo84LFzx5nI
| strken wrote:
| Sorry, what? Are you claiming that ENIAC was using
| microservices? That's a claim that needs some kind of
| supporting evidence, or at least a better explanation.
| ReflectedImage wrote:
| Procedure, functional and Entity Component Systems
| (Sketchpad, 1963) programs came first.
|
| Micro-services then came along (Distributed computing).
|
| Then OOP was then invented to replicate the benefits of
| micro-services in single user environments.
| jillesvangurp wrote:
| As soon as functions were invented, we had modules. And as soon
| as we had the ability to call those over a network, people
| started building the same stuff over and over again. It's eery
| how much of the stuff that Kubernetes does maps almost one to
| one to the same kind of stuff that for example CORBA used to
| do. Naming, discovery, security, events, configuration etc.
| You'll find CORBA specific ways of doing all that. Except that
| stuff dates back 35 years. There is probably a bunch of stuff
| before CORBA that could be added to this list but that's before
| my time.
|
| DCOM (MS) replicated some of that but never really caught on.
| You'll find quite a bit of that kind of stuff in OSGI (Java)
| that was somewhat popular in the early 2000s. And there was of
| course the whole SOAP / Web Services / SOA mess that people got
| into around the same time. Docker emerged early 2010s and
| Kubernetes soon followed and fast forward ten years and we're
| neck deep into the same shit all over again via micro services.
|
| Part of this is just stuff you kind of need if you want to do a
| distributed system (like service discovery, auditing, security,
| etc.). And part of that is a lot of complexity resulting from
| that. Especially when you are distributing for organizational
| rather than technical reasons (Conway's law). Which is a major
| driving force in larger organizations.
|
| The point here is that none of this is new. Also this stuff did
| not really fail but just seamlessly morphed into the next
| thing. People keep on making the mistake of believing that
| buzzword compliance makes things better/easier when in reality
| you are paying a largish price in terms of complexity and
| overhead for not that much gain. There's a lot of wheel
| reinvention happening here as well. And a lot of the exact same
| naivity being projected on the latest and greatest framework or
| thing. The same type of people that are cheer leading
| microservices now would have been cheer leading web services
| twenty years ago. In some cases these literally are the same
| people. Or companies. There's a reason IBM is all over this
| stuff, for example.
| antonvs wrote:
| > So originally software development used micro-services rather
| than modules.
|
| This is not even remotely true, in any conceivable sense. But
| I'd love to hear what you were thinking of.
| bunderbunder wrote:
| My annoying critique of this article is that I don't think the
| author made their case strongly enough.
|
| I've discovered that their taxonomy at the opening of the article
| ("single unit of deployment - monolith, multiple units of
| deployment - microservices/services") is a bit optimistic.
| Because, when it comes to making things unnecessarily
| complicated, human ingenuity knows no limits.
|
| I've now worked on a project that somehow managed to be
| monolithic despite having multiple units of deployment. We
| weren't good about contract/API change management, so in practice
| it was rare that you could separately deploy "independent"
| services.
|
| And I've also worked on a project that had a single unit of
| deployment but was somehow still more microservice-like.
| Everything was packaged into a single giant Docker image that
| contained the binaries for all the services. (You'd pick which
| one a container was running with run-time configuration.) But
| they were well-modularized and services from different versions
| of the image could talk to each other just fine, so in practice
| working on it often felt more like successful microservice
| implementations in development and production. It's just that
| getting things from development to production was an unholy
| nightmare because the CI pipeline for that "mono-image" was such
| a monstrosity.
| BinaryIgor wrote:
| Thanks for the feedback :) I'm working on follow-ups, splitting
| it into multiple articles where I will try to elaborate more on
| various dimensions of modular monolith vs (micro)services
| debate/issue/decision and their various tradeoffs. Will try to
| make it clearer there!
| bunderbunder wrote:
| I realize looking back that my opening sentence isn't quite
| right. I didn't mean to say that your article wasn't
| convincing. I meant that I thought that an even bolder
| version of your premise might also be true. The article is
| great as-is.
| mdhb wrote:
| I always thought Google had some interesting insights and ideas
| when they were playing around with the now abandoned service
| weaver project:
| https://opensource.googleblog.com/2023/03/introducing-servic...?
|
| More academic and language neutral introduction here if that's
| your thing: https://arxiv.org/pdf/2404.09357
| asimpletune wrote:
| The real thing that forces one to choose micro services over
| modules is when data isolation is a requirement, e.g. security.
|
| Capabilities based programming could come along way though to
| help with closing that gap.
| kstrauser wrote:
| I feel like there are competent, competing visions talking past
| each other on the subject. There's kind of a spectrum:
|
| 1. _Everything_ is a monolith. Frontend, backend, dataplane,
| processing, whatever: it 's all one giant, tightly coupled
| vertically-scaled ball of mud. (This is insane.)
|
| 2. Everything is a monolith, but parts are horizontally scaled.
| Imagine a big Flask app where there are M frontend servers, and N
| backend async task queue processors, all running the same
| codebase but with different configurations for each kind of
| deployment. (This is perfectly reasonable.)
|
| 3. There are a small number of separate services. That frontend
| Flask server talks to a Go or Rust or Node or whatever backend,
| each appropriate to the task at hand. (This is perfectly
| reasonable.)
|
| 4. Everything is a separate service. There are N engineers and
| N+50% servers written in N languages, and a web page load hits 8
| different internal servers that do 12 different things. The site
| currently handles 23 requests per day, but it's meant to
| vertically scale to Google size once it becomes popular. Also,
| everything is behind a single load balancer, but the principle
| engineer (who interned at Netflix) handwaves it away a "basically
| infinitely scalable". (This is insane.)
|
| These conversations seem to devolve into fans of 1 and 4 arguing
| that the other is wrong. People in 2 and 3 make eye contact with
| each other, shrug, and get back to making money.
| 383toast wrote:
| you can get pretty far with (2) and (3), haven't really
| understood the need for (4) unless you're FAANG
| yearolinuxdsktp wrote:
| Facts! #1 is not insane as long as you keep your internals
| modular (all-in-one deployment doesn't mean ball of mud...
| you can avoid putting service calls into your domain objects
| or your data plane code). And you can go from #1 to #2 once
| you see the need and slice the services out that need it
| (such as decoupling async batch processing into a 2nd service
| that shares the domain and the data plane code and does not
| include the front-end).
| kstrauser wrote:
| In fairness, it being a tightly coupled ball of mud is part
| of my definition of #1 here. What you describe is basically
| #2 waiting to happen, just that no one's needed to do it
| yet.
| yearolinuxdsktp wrote:
| Makes sense, I agree then that #1 as you exactly define
| it is insane.
| amarant wrote:
| 4 doesn't really work for serious big load applications
| either: nobody will be able to understand the codebase. You
| want 3.
|
| During my days at mojang we did some variation of 3. We had
| ~250k requests/second, and handled it just fine (we had 4
| nines availability forever chasing the fifth, and sub 20ms
| response times)
|
| I think even among those who do see big loads, few see as
| much malicious traffic as we did. This was one of the
| arguments for a micro service architecture. If a DDOS took
| down our login service, already logged in players would be
| unaffected (until their tokens expired anyway)
|
| Well, that was a long winded way to say, 3 is about as micro
| as you want to go. I've only seen 4 done once, and that site
| actually went under whenever they had more than 30 request
| per minute. (Admittedly they had made a bunch of other really
| bad decisions not covered in the above description, but
| having ~30 services on a team of 12, in order to handle a
| handful of requests per hour was certainly their biggest
| mistake.)
| notfed wrote:
| Why is #1 insane (no horizontal scaling) if you only have 23
| requests per day?
| Spivak wrote:
| I feel like people forget just how fast a single machine can
| be. If your database is SQLite the app will be able to burn
| down requests faster than you ever thought possible. You can
| handle much more than 23 req/day.
| kstrauser wrote:
| In the not-too-distant past I was handling many thousands
| of DB-backed requests per hour in a Python app running
| plain PostgreSQL.
|
| You can get really, really far with a decent machine if you
| mind the bottlenecks. Getting into swap? Add RAM. Blocked
| by IO? Throw in some more NVMe. Any reasonable CPU can
| process a lot more data than it's popular to think.
| bluGill wrote:
| Anytime someone talks about scale I remember just how
| much data the low mhz CPUs used to process. Sure the
| modern stuff has nicer UIs, but the UIs of the past where
| not bad, and we a lot of data was processed. Almost
| nobody has more data than what the busiest 200mhz CPU on
| 1999 used to handle alone, so if you can't do it that
| isn't a scaling problem it is a people problem. (don't
| get me wrong, this might be a good trade off to make -
| but don't say you couldn't do it on a single computer)
| bkanuka wrote:
| Don't know if this is sarcasm or not. If you have 23 req/day,
| then there's no tech problem to solve. Whatever you have is
| good enough, and increasing traffic will come from solving
| problems outside tech (marketing, etc)
| kstrauser wrote:
| It's not. It's kind of bonkers to pursue that when you have a
| lot of traffic, but it's a perfectly sane starting point
| until you know where the pain points are.
|
| In general, the vast number of small shops chugging away with
| a tractably sized monolith aren't really participating in the
| conversation, just idly wondering what approach they'd take
| if they suddenly needed to scale up.
| bunderbunder wrote:
| I'm not even sure it's bonkers if you have a lot of
| traffic. It depends on the nature of the traffic and how
| you define "a lot". In general, though, it's amazing how
| low latency a function call that can handle passing data
| back and forth within a memory page or a few cache lines is
| compared to inter-process communication, let alone network
| I/O.
|
| The corollary to that is, it's amazing how far you can push
| vertical scaling if you're mindful of how you use memory.
| I've seen people scale single-process, single-threaded
| systems multiple orders of magnitude past the point where
| many people would say scale-out is an absolute necessity,
| just by being mindful of things like locality of reference
| and avoiding unnecessary copying.
| default-kramer wrote:
| It's not insane. The best codebase I ever inherited was about
| 50kloc of C# that ran pretty much everything in the entire
| company. One web server and one DB server easily handled the
| ~1000 requests/minute. And the code was way more maintainable
| than any other nontrivial app I've worked on professionally.
| geodel wrote:
| I work(ed) on something similar in Java. And it still works
| quite well. But last few years are increasingly about
| getting berated by management on why things are not modern
| Kubernetes/ micro services based by now.
| simianwords wrote:
| most productive applications have more RPS than that. we
| should ideally be speaking about how to architect
| _productive_ applications and not just mocks and prototypes
| notatoad wrote:
| if you have 23 requests per day the insane thing is wondering
| whether or not you've chosen the correct infrastructure,
| becuase it really doesn't matter.
|
| do whatever you want, you've already spent more time than
| it's worth considering it.
| Spivak wrote:
| Once you open the door to 3 it's a lot harder to stop the slide
| into 4. Devs love greenfield codebases and once that's an
| acceptable way to solve a problem they'll reach for it more and
| more. Especially if your setup has low friction to "just"
| spinning up a new service.
| kstrauser wrote:
| True, and some org-level discipline is important here. I'm
| all for polyglot architectures, but only for reasonably small
| values of poly. Like if you wrote the frontend in Python, but
| really some performance critical Rust, fine. Or maybe you
| merged with another shop that already had a bunch of Go, far
| out.
|
| Resist the urge to roll out that Prolog-driven inference
| service that your VP Eng vibe coded after reading an article
| about cool and strange programming languages.
| bluGill wrote:
| I generally think the right rule of thumbs is about 10
| languages - but that includes the build system, CI
| system... Not everyone needs to know all 10, but you need a
| good set of options for that that need it. Most people
| should be using the same 2 or 3 of that 10, but a few teams
| need to do something weird and so need the other options
| that are overall rarely used.
| kstrauser wrote:
| That's very scale-dependent. I can imagine why a FAANG
| might need 10 languages, between Python APIs and Java
| services and Rust and Go things and a Node system and a
| smattering of Perl and some ETL in Scala and BI in C#,
| etc. A startup with less than 50 employees almost
| certainly does not.
|
| Maybe I'd compromise on no more than, say, 2-3 language
| per department. If you're small enough that "engineering"
| is 1 department, then there you go: choose wisely. If you
| have a whole department for frontend, and one for
| backend, and one for ops, and one for analytics, etc.,
| then you can somewhat treat those as encapsulation
| boundaries and have more flexibility.
| svieira wrote:
| Depends on what you consider a _language_. There are a
| lot of DSLs out there and once you add a couple of
| configuration files for Jenkins / Git{Hub, Lab} / Bazel
| / Buck and Vagrant / Docker / Bash and so on you're
| already at 5 languages and you haven't even added the
| lethal trifecta of HTML+CSS+JS for your web front-end or
| Swift+Kotlin if you only want to roll on mobile.
| kstrauser wrote:
| Yeah, that's a rabbit hole to be sure. I personally only
| count languages that you write a non-trivial amount of
| production executable code in. Examples of things I
| wouldn't count: HTML and CSS, JS in the browser, Bash
| used in the build system. If you wrote your webserver in
| Bash for some wild reason, it's included. Mise and Just
| and Makefiles aren't.
| bluGill wrote:
| If you exclude HTML/CSS/JS/Makefiles that means you are
| giving your people an excuse to make a mess of them. You
| have to count everything. You can say that HTML/CSS/JS
| are all one since they are tightly coupled and you have
| to know all 3 if that is what it takes to get under the
| 10 limit, but you should make it clear you are breaking
| the spirit of the rule to do that just because of an
| external factor - and you are not happy about that.
|
| If you write even one line of something you need at least
| one person who is an expert and takes responsibility to
| ensure the code written in the language is good.
| bluGill wrote:
| Not department, project. If you can get to 10 across a
| whole company that would be good, but project
| unreasonable in the largest companies. Even with only 50
| people in a startup staying under 10 will need some
| effort as there is always some cool new toy. (don't let
| them replace languages with frameworks either)
|
| This is important. Sometimes you have multiple
| departments working on a single project (a large embedded
| system). Sometimes a department will work on multiple
| projects - the department needs to be careful that people
| don't need to know too many languages (which is hard - if
| often make sense for a department to develop for all
| apps, so you have to know the language of iPhone,
| android, Window)
|
| People should stay mostly in their lanes (department),
| but they should have the ability to cross to others when
| that is needed instead of just throwing APIs over a wall
| and accusing the other side of using it wrong.
| mytailorisrich wrote:
| "Tightly coupled" is the sticking point. Tight coupling is bad
| architecture whether you have a monolith or microservices,
| which is the general point of the article.
| nine_k wrote:
| Logical separation, the modules, is what allows to preserve
| developer sanity. Physical separation, the (micro-)services is
| what allows you to ship things flexibly. Somewhere on the
| distant high end, microservices also play a role in enabling
| scalability to colossal scales, only needed by relatively few
| very huge companies.
|
| The key problem of developing a large system is allowing many
| people to work on it, without producing a gridlock. A monolith,
| by its nature, produces a gridlock easily once a sufficient
| number of people need to work on it in parallel. Hence modules,
| "narrow waist" interfaces, etc.
|
| But the key thing is that _" you ship your org chart"_ [1].
| Modules allow different teams to work in parallel and
| _independently_ , as long as the interfaces are clearly
| defined. Modules deployed separately, aka services, allow
| different teams to ship their results independently, as long as
| they remain compatible with the rest of the system. Solving
| these _organizational_ problems is much more important for any
| large company than overcoming any technical hurdles.
|
| [1]: https://en.wikipedia.org/wiki/Conway%27s_law
| bluGill wrote:
| > Physical separation, the (micro-)services is what allows
| you to ship things flexibly.
|
| If you are willing to pay a price. Once you allow things to
| ship separately are are locked into the API Hyrum's law
| mistakes of the past until you can be 100% that all uses of
| the old thing are gone in the real world. This is often hard.
| By shipping things together you can verify they all work
| together before you ship, and more importantly when you
| realize something was a mistake there is a set time where you
| can say "that is no longer allowed" (this is a political
| problem - sometimes they will tell you no we won't change,
| but at least you have options).
|
| Everything else is spot on, but I am feeling enough pain from
| mistakes made 15 years ago (that looked reasonable at the
| time!) such that I feel the need to point it out.
| xorcist wrote:
| Scale? At what "scale" runs Linux, perhaps the most well
| known monolith software ever?
|
| (And also the subject of perhaps the most well studied
| flamew^Wdiscussion about mono- versus microservice
| architecture.)
|
| It only runs most of all servers and most of all the mobile
| terminals in the world. Where is that distant higher end,
| where microservices unlock colossal scale, exactly?
|
| Architecture matters. Just not always the way you think. But
| it serves as catnip for everyone who loves a good debate.
| Anyone who gets tired of writing code can always make a good
| living writing about software architecture. And good for
| them. There's a certain artistry to it, and they have
| diagrams and everything.
| nine_k wrote:
| Linux can scale to a machine with hundreds of cores; the
| largest is 1000+ cores IIRC. It's because it scales
| horizontally, in a way, and can run kernel threads on
| multiple cores. But a NUMA configuration feels increasingly
| like a cluster the more cores you add, just because a
| single-memory-bus architecture can't scale too much;
| accessing a far node's memory introduces much more latency
| that accessing local RAM.
|
| It's easy to run a thousand independent VMs. It's somehow
| more challenging to run a thousand VMs that look externally
| as one service, and can scale down to 500 VMs, or up to
| 2000 VMs, depending on the load. It's really quite
| challenging to scale a monolith app to service millions of
| users on one box, without horizontal scaling. But it
| definitely can be done, see the architecture of
| Stackoverflow.com. (Well, this very site is served by a
| monolith, and all logged-in users are served by a _single-
| threaded_ monolith, unless they rewrote the engine. Modern
| computers are absurdly powerful.)
| ivanjermakov wrote:
| I also laugh at 30+ microservice projects where almost every
| service connects to a single oracle database with no
| sharding/partitioning. Inb4 aws dynamodb outage.
| connicpu wrote:
| The number of services at my job that are just grpc wrappers
| for a database with endpoints that are only accessed by
| services that have their own connections open to the same
| database has been driving me insane.
| 4ndrewl wrote:
| Isn't that a microservices 101? No shared databases?
| kstrauser wrote:
| An important and common exception would be for performance
| critical reasons, as mentioned elsewhere. Say most of your
| code's in Python, but you rewrote parts in a faster
| language. There you're not necessaryily separating them for
| isolation or encapsulation purposes. It'd be as much of a
| mistake to force them to hit separate DBs.
| xorcist wrote:
| Wasn't it two pizza teams and using the right tools for the
| right job?
| solumos wrote:
| 1 Could maybe make sense for a proof of concept, but realize
| that you're probably throwing it away (sooner better than
| later).
|
| Start with 2, and think about the separation of deployable
| targets and shared libraries/plugins. You can eventually carve
| out separate infra for deployable targets when resource
| contention becomes a problem (e.g. DB load is affecting
| unrelated services)
|
| 3 is rarely the right first step for a small team
|
| 4 is never the right first step for a small team
| abalone wrote:
| Or there's 3.5: separate services where it makes sense, but not
| necessarily small in number. "Makes sense" would entail things
| like, does it have distinct resource utilization or scaling
| characteristics, or do you want to enable your service to more
| gracefully degrade if that module becomes unavailable.
|
| (This is basically the definition of 3 without the implication
| that it will be rare.)
|
| As opposed to 4 which is about proactively breaking everything
| down into the smallest possible units on the expectation that
| the added complexity is always worth it.
| Sammi wrote:
| Lots and lots and lots of people make no. 1 work. The issue
| isn't whether it is a monolith or not. The issue is whether it
| has good or poor internal architecture.
|
| No 1 can scale to 1000 requests per second. One machine, one
| db, one application. It is totally doable because so many
| people do it.
|
| It's just not sexy and doesn't pad you resume. It's boring
| stuff that just works.
| foobarian wrote:
| I wish our app only got 1krps. Maybe these conversations
| should be bucketed by the traffic volume people are handling
| - it really leads to completely different designs.
| efitz wrote:
| Microservices embody a concepts that the article overlooks or
| downplays:
|
| 1. modularity - yes - but even better than what the article
| describes, there is little or no ability to cheat the modularity
| - the microservice has an api as a contract and is isolated in
| execution so there's no trivial way to go around the api.
|
| 2. Independently committable/deployable. One reason to consider
| microservices is organizational- maybe you don't want to or can't
| share a repro with another team.
|
| Now of course microservices have lots of downsides and are not a
| panacea and may be a bad fit for your project.
| bullen wrote:
| It's about the database, you need to make your own database!
|
| As long as your bottom dependency is fixed, you cannot progress!
|
| http://root.rupy.se
| twodave wrote:
| I think if you build modules expecting the dependencies to not
| eventually include most other modules then you either have a
| trivial application or you're one PM monkey wrench away from a
| bad time.
| simianwords wrote:
| i keep seeing "but this is only relevant if you are working at
| FAANG scale" but most productive applications work at similar
| scale. there's little fun or education just speaking about mocks
| or prototypes.
|
| sure there are a lot of startups that start off and don't have
| much traffic. but i don't like that we dismiss real world
| _productive_ applications because not many companies hit that
| scale. but most productive applications hit that scale!!
| tuhgdetzhh wrote:
| Modular monolith is a pipe dream. Great idea, but it gets messed
| up in reality (similar to communism). You just need a bit of time
| pressure and project deadlines and the devs will crack and throw
| the modularity over board with just a few merges. Multiply that
| over a decade, then you end up with a monster and fight dragons.
| 4ndrewl wrote:
| I mean, it's stood the (multi-decade) test of time, but it's
| how Drupal works. Modularised components which interact with a
| common core. Not perfect, but gives you plenty of real world
| intel about the benefits, pitfalls and environment that you
| need to make it work.
| 0xbadcafebee wrote:
| Unfortunately this is not very useful. 'Modularity' is not a
| guarantee of good software design or operational practice.
|
| You can develop a "module" that is tightly integrated with other
| "modules", making it unusable without the other "modules". This
| means you have one software component that's effectively split
| into two, and at least one is unusable on its own. This creates
| dependency issues, and design issues, as now the second component
| needs to closely track development of the first component. What's
| the point of the second module? None from the application's
| perspective; but it helps with Conway's Law (the worst reason for
| a software design decision).
|
| In addition, "deployment" is not a singular concept. How is a
| monolith a "single unit of deployment" if it depends on a
| database with a changing schema? You have to deploy schema
| changes intentionally, and typically first, but with code that is
| backwards/forwards compatible. What's a deployment then? Is it a
| multiple stage process with multiple components? Or is the
| monolith somehow a "single unit of deployment", even though it
| entirely depends on the database which has its own "single unit
| of development", with the first being dependent on the second?
| (the answer is: a 'deployment' isn't a fixed concept; there is
| merely systems, and their components, and an order of operations
| to make changes without impairing system function)
|
| Good system design depends on other, more specific concepts, like
| loose coupling, high cohesion, single responsibility,
| encapsulation, backwards-compatibility, immutability, versioning,
| and change management, to name a few. Merely making modules
| doesn't mean much; monoliths (like microservices), if not
| mindful, meander into mudballs.
|
| It's also an overstatement to claim that microservices are
| inherently more complex than monoliths. You can easily end up
| with an overcomplicated monolith too. In fact you can easily end
| up with an overcomplicated anything. The difference with
| microservices is you're not [supposed to] make assumptions about
| data relationships, and you do have to preserve whatever
| functionality you expose that others depend on.
|
| And please remember that monoliths and microservices aren't
| competing, as if one is supposed to be better than the other.
| They are just different, like apples and oranges. They have
| specific [and different] design and function implications. Use
| them properly as needed. You should be mixing them, not just
| making one thing in one way as if your whole company must do all
| things "the one twue way". (Examples [not canonical]: dhcpd is a
| microservice; dns is a collection of microservices; your browser
| is a monolith; a web app may be a microservice or monolith (or
| both); etc)
___________________________________________________________________
(page generated 2025-11-10 23:01 UTC)