31 ms·
Microservices, while often sold as solving a technical problem, usually actually solve for a human problem in scaling up an organization. There's two technical
by barrkel 4y ago
Microservices, while often sold as solving a technical problem, usually actually solve for a human problem in scaling up an organization.
There's two technical problems that microservices purport to solve: modularization (separation of concerns, hiding implementation, document interface and all that good stuff) and scalability (being able to increase the amount of compute, memory and IO to the specific modules that need it).
The first problem, modules, can be solved at the language level. Modules can do that job, and that's the point of this blog post.
The second problem, scalability, is harder to solve at the language level in most languages outside those designed to be run in a distributed environment. But most people need it a lot less than they think. Normally the database is your bottleneck and if you keep your application server stateless, you can just run lots of them; the database can eventually be a bottleneck, but you can scale up databases a lot.
The real reason that microservices may make sense is because they keep people honest around module boundaries. They make it much harder to retain access to persistent in-memory state, harder to navigate object graphs to take dependencies on things they shouldn't, harder to create PRs with complex changes on either side of a module boundary without a conversation about designing for change and future proofing. Code ownership by teams is something you need as an organization scales, if only to reduce the amount of context switching that developers need to do if treated as fully fungible; owning a service is more defensible than owning a module, since the team will own release schedules and quality gating.
I'm not so positive on every microservice maintaining its own copy of state, potentially with its own separate data store. I think that usually adds more ongoing complexity in synchronization than it saves by isolating schemas. A better rule is for one service to own writes for a table, and other services can only read that table, and maybe even then not all columns or all non-owned tables. Problems with state synchronization are one of the most common failure modes in distributed applications, where queues get backed up, retries of "bad" events cause blockages and so on.
- GauntletWizard 4y agoThose human scaling problems are also security scaling problems; the Unix process model (and, in fact, the von Neumann architecture) doesn't separate modules in security capabilities, nor is it practical to do so. Microservices allow your permissions to be clear and precise. Your database passwords are only loaded into the process that uses them. You can reason about "If there's an RCE in this service, here's what it can do". Trying to tie that to monoliths is hard and ugly.
- closeparen 4y ago>scalability (being able to increase the amount of compute, memory and IO to the specific modules that need it). I would like someone to spell this out. It seems to me people are claiming that if a single binary serves some CPU-bound requests and some memory-bound requests, and you give it more memory, then the memory gets "wasted" on the CPU-bound part. Or if you give it more CPU, the CPU gets wasted on the memory-bound part. But this kind of assignment of resources to code paths seems to be a consequence of microservices. In a single computer, single binary situation resources should not get used up unless the workload actually wants to use them. A compute-heavy thread doesn't cost heap. A big heap doesn't slow down a compute-heavy thread. What am I missing?
- rlt 4y agoI’d love for a language / framework that allows for an application to be composed of “modules” that can either be run in a single process, or deployed as multiple independently scalable processes, with a mostly transparent RPC system requiring minimal boilerplate. My IDE should be able to easily traverse the call graph. My development environment should be simple to setup. I’ve worked on microservices that required an insane amount of boilerplate to do simple things. Like 7 layers of controllers, clients, services, data services, etc, just to fetch a simple piece of data. And the developer experience of running dozens of services in a Kubernetes cluster running on my dev machine was awful. Does anything like this exist? I only dabbled many years ago but Erlang/OTP comes to mind. And tRPC for TypeScript calls in browser and server.
- scottmas 4y agoHey, we have a system like that at my company. Microservices are self contained and responsible for exposing their interface via SDKs created with a tRPC like library. Jump to definition works across the entire monorepo like a charm. Could you take a look and give me any feedback? https://github.com/rectech-holdings/umbrella-corp-boilerplate https://github.com/rectech-holdings/umbrella-corp-boilerplat...
- int_19h 4y agoThe industry has been trying to do "transparent remoting" for decades now. DCOM and CORBA were both basically that. It turns out that "transparent RPC" is basically a contradiction in terms. As soon as you start doing things across process boundaries, and even more so across network boundaries, it requires a very different approach for API design - something that's very cheap locally, like passing objects by reference, becomes expensive and full of footguns. If you reduce the feature set to the point where it can be transparently mapped to either local or RPC - which is, basically, function calls processing and returning data organized into arrays & trees (but not graphs) - there's still the issue that RPC has so many more failure points that you have to handle that would never light up in local. This is all still doable; I have my doubts about practical the end result would be, though.
- rlt 4y ago
- mpweiher 4y ago> Microservices [..] actually solve for a human problem in scaling up an organization. So does modularity. "The benefits expected of modular programming are: (1) managerial_development time should be shortened because separate groups would work on each module with little need for communication..." On the Criteria To Be Used in Decomposing Systems into Modules, D.L. Parnas 1972. http://sunnyday.mit.edu/16.355/parnas-criteria.html http://sunnyday.mit.edu/16.355/parnas-criteria.html
- LAC-Tech 4y agoOff topic, but 70s computing papers are just the best aren't they?
- regularfry 4y agoParnas was writing under an assumption of BDUF, big-bang releases, and expensive hardware instances. As soon as you want to decouple module deployments and accommodate changing requirements over time you need something else. That "something else" might be "making sure your modular monolith has a good enough test suite that any team can deploy it with only their changes" or it might be "microservices for all", but Parnas' assumption that independently verified modules will need to be built once by independent teams then function correctly when assembled has been comprehensively squashed in the last 50 years. He's right as far as Conway's Law goes, though.
- unwind 4y agoHere I guess BDUF = big design up front. Edit: failed to spell "I" correctly. Ouch.
- treis 4y agoCan you explain why you can deploy microservices independently but not modules?
- regularfry 4y agoYou can deploy modules independently, but the technical and organisational measures you need in place to do it safely are an extra step you need to take whereas with microservices they're built in. Modules live in the same execution context, so you need a good shared ownership model for that context itself. The point is that Parnas never conceived of doing it because he was writing about a world where the interfaces between modules were known ahead of time and were static.
- daitangio 4y agoTotally agree. Also microservices shines IF you need different release schedule for two services. If they are managed in the same project/team, the effort you pay is high, the advantage could not pay your bill, so be careful in such scenario.
- zeroxfe 4y agoBoth Modularization and Microservices solve scaling problems. Modularization allows development to scale. Microservices allow operations to scale.
- chasd00 4y agoyeah i've always thought that microservices shine when the org chart defines them. I've been on teams where we were responsible for multiple microservices and it was so annoying to have to bounce back and forth between code bases and deployments and then it was super easy to intertwine different microservices when you were in the them so often. I feel like if you don't have a team dedicated full time per microservice then you probably shouldn't be using that architecture.
- strictfp 4y agoThis has been my major criticism of them; you cement the design of the app by creating an organizational structure around your software components. What happens if you need to redesign the architecture to meet new needs? That's right; it's not going to happen, because people will fight it tooth and nail. You also cannot produce any meaningful end-user value without involving several teams. Microservices is just "backend, frontend, and database team" reincarnated. My take: do Microservices all you want, but don't organize teams around the services!
- adolph 4y agoConway's law is an adage that states organizations design systems that mirror their own communication structure. It is named after the computer programmer Melvin Conway, who introduced the idea in 1967. His original wording was: Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization's communication structure. — Melvin E. Conway https://en.wikipedia.org/wiki/Conway's_law https://en.wikipedia.org/wiki/Conway's_law
- jonhohle 4y agoOne of the principal engineers at Amazon called that “Org-atecture”.
- lightbendover 4y agoAmazon is a good example of a huge organization absolutely crippled by micro-service fiefdoms driven by sr.mgr whims and not strong technical guidance from such PEs.
- lmm 4y agoAnd look how much damage it's done to their business.
- beastcoast 4y agoDepends entirely on the org. My org actually broke SOA (thousands of services and some intense technical debt) and is now in a years long process of moving toward a centralized service orchestration model. This couldn’t have happened without PE, Sr PE and DE involvement.
- ramblerman 4y agoYou shoot yourself in the foot pretty hard regarding point 2 (scalability) if you have your microservices share a DB.
- jollyllama 4y agoOr hardware. I worked on an HPC product which used "microservices" of a kind but many of them were tied to specific hardware nodes. So much of what we think of as "microservices" relies on a fairly narrow set of assumptions, and mostly makes sense in specific contexts, i.e. cloud, APIs, etc.
- barrkel 4y agoFWIW, most of Google's Ads services share a database - the catch is it's F1 on top of Spanner. Latency is highish, but throughput is good. In the outside world, for an application that may truly need to scale, I'd go MySQL -> Vitess before I'd choose separate data stores for each service with state. But I'd also question if the need to scale that much really exists; you can go a long way even with data heavy applications with a combination of sharding and multitenancy.
- henryfjordan 4y agoMicroservices, by definition, do not share a DB. If they do then you just have multiple processes in the same larger service.
- osigurdson 4y ago>> because they keep people honest around module boundaries Imagine a world where every pip/nuget/cargo package was a k8s service called out-of-process and needed to be independently maintained. We would have potentially hundreds of these things running, independently secured via mTLS, observability and metrics for each, and all calls run out of process. This is the abysmally slow hellscape that some are naively advocating for without realizing it. It is relatively obvious what should be a library (json, numpy, NLog, etc), and what should be a service (Postgres, Kafka, Redis, NATS, etc.) when dealing with 3rd party components. It is also obvious that team scaling is not determined by whether code exists in a library or service from this example since all 3rd party code is maintained by others. However, once we are all in the same organization, we lose the ability to correctly make this determination. We create a service when a library would do in order to more strictly enforce ownership. I think an alternate solution to this problem is needed.
- closeparen 4y agoBut of course team scaling is determined this way. Merely releasing a library version has zero business impact. To have impact, library teams must go around to every consumer team and beg them to accept a version bump. This is a) shitty work for which it is impossible to retain people, and b) intractable without an enormous labor force once the number of consumers is big enough. Services can be continuously deployed by small owning teams.
- osigurdson 4y agoIf library teams could force consumers of the library to upgrade the problem is seemingly solved. Is this correct or are there other aspects that you are considering?
- zffr 4y agoTwitter seems to have adopted Microservices in 2014, and in 2013 they had ~200M MAU (presumably using a monolith architecture). Even if Microservices are better for scale, most companies will never experience the level of scale of 2013 Twitter. Are Microservices beneficial at much smaller levels of scale? Ex: 1M MAU
- AlchemistCamp 4y agoGithub got absolutely enormous as a Rails monolith, plus some C extensions in a few hot paths.
- zffr 4y agoFrom what I can tell, Github transitioned over to micro-services ~2020 (https://www.infoq.com/presentations/github-rails-monolith-microservices/ https://www.infoq.com/presentations/github-rails-monolith-mi...). By this point it had already grew to a scale that very few companies will ever reach. I'm not sure at what point the monolith codebase became absolutely enormous, but I would bet that GitHub grew to 1M active users with a monolith just fine. Micro-services might be necessary for the companies like Github, Twitter, Google that grow to be huge, but monoliths seem to work just fine for the vast majority of other companies.
- codethief 4y ago> most companies will never experience the level of scale of 2013 Twitter I fully agree with your argument. Then again, as mentioned elsewhere in this discussion, microservices are often not introduced to solve a scalability problem but an organizational one and there are many organizations that have more engineers than Twitter (had). Personally, I still don't buy that argument because by solving one organizational problem one risks creating a different one, as this blog post[0] illustrates: > […] Uber has grown to around 2,200 critical microservices Unsurprisingly, that same post notes that > […] in recent years people have begun to decry microservices for their tendency to greatly increase complexity, sometimes making even trivial features difficult to build. […] we experienced these tradeoffs first hand I'm getting very strong Wingman/Galactus[1] vibes here. [0]: https://web.archive.org/web/20221105153616/https://www.uber.com/blog/microservice-architecture/ https://web.archive.org/web/20221105153616/https://www.uber.... [1]: https://www.youtube.com/watch?v=y8OnoxKotPQ https://www.youtube.com/watch?v=y8OnoxKotPQ
- dalbasal 4y agoI'm glad you made this point. This happens a lot. Organisational problems conflated for technical ones. Technical solutions to organisational politics. Etc. It's often easier to admit technical challenges than organisational ones. Also, different people get to declare technical and organisational challenges, and different people that get to design the solutions. There's also a dynamic where concepts are created by the technical 1% doing vanguard or challenging tasks. The guys responsible for scaling youtube or whatnot. Their methods and ideas become famous and are then applied to less technically demanding tasks, then in less technical organisations entirely. I think if we can be honest at the actual problem at hand, 80/20 fixes will emerge. IE, the "real" value is not the architecture per se, but the way it lets you divide the responsibilities in the organisation.
- pjmlp 4y agoStrong typed languages with support for binary modules are just as good keeping people honest. Each team gets to distribute libraries over repos (COM, JAR, DLL, whatever), no way around that unless they feel like hacking binaries.
- barrkel 4y agoServices generally have a stateless request/response architecture, inherited from the success of HTTP, and contra the experiences of CORBA and COM, the latter being much better suited to Office Object Linking and Embedding and VB visual controls - local, assumed reliable, not across a network boundary. Creating an object-oriented API for a shared library which encapsulates a module to the degree a service boundary would is not trivial and it's very rarely done, never mind done well. Most OO libraries expose an entire universe of objects and methods to support deep integration scenarios. Maintaining that richness of API over time in the face of many consumers is not easy, and it (versioning) is a dying art in the eternal present of online services. The 90s aren't coming back any time soon. If you own a library which is used internally, and you add a feature which needs a lot more memory or compute, how do you communicate the need to increase resource allocation to the teams who use the library? How do you even ensure that they upgrade? How do you gather metrics on how your library is used in practice? How do you discover and collect metrics around failure modes at the module boundary level? How do you gather logs emitted by your library, in all the places it's used? What if you add dependencies on other services, which need configuring (network addresses, credentials, whatever) - do you offload the configuration effort on to each of the library users, who need to do it separately, and end up with configuration drift over time? I don't think binary drops work well unless the logic they encapsulate is architecturally self-contained and predictable; no network access, no database access, no unexpected changes in CPU or memory requirements from version to version. There's plenty of code like this, but it's not usually the level of module that we consider putting inside a service. For example, an Excel spreadsheet parser might be a library. But the module which takes Excel files uploaded by the user and streams a subset of the contents into the database is probably better off as a service than a library, so that it can be isolated (security risks), can crash safely without taking everything down, can retry, can create nice logs about hard to parse files, can have resource metrics measured and growth estimated over time, and so on.
- emodendroket 4y agoWorking at a huge company I feel like every service owning its own data store (or at least only accessing/manipulating another’s through its API, not directly in the underlying store) is the only sensible way to do it, because you can never really envision what clients you will have ahead of time or what they will want to do, and that’s an easy recipe for headaches. But the smaller the company gets the further I’d go along the spectrum from full isolation to isolated services with shared stores to monolith.
- est 4y ago> usually actually solve for a human problem in scaling up an organization. It's invented by a software outsourcing firm to milk billable hours from contracts.
- lightbendover 4y ago> A better rule is for one service to own writes for a table This breaks down when the database is essentially a generic graph. The worst solution I've seen to this is to have another service responsible for generic write operations and any service that wants to write data goes through that service -- you're essentially re-introducing the problem you're purporting to solve at a new layer with an added hop and most likely high network usage. The best solution I've seen is to obviously have the monolith. The enterprise solution I've seen, while not good by any means but nearly essential for promoting team breakdown and ownership, is to just let disparate services write as they see fit, supported by abstraction libraries and SOPs to help reinforce care from engineers.
- KptMarchewa 4y agoAdded network call is often better than disjointed view on database schema.
- lightbendover 4y agoThat's a difficult claim to make without knowing the workload.
- philwelch 4y ago> I'm not so positive on every microservice maintaining its own copy of state, potentially with its own separate data store. I think that usually adds more ongoing complexity in synchronization than it saves by isolating schemas. A better rule is for one service to own writes for a table, and other services can only read that table, and maybe even then not all columns or all non-owned tables. I’ll go one step further and say that you should treat your data stores as services in and of themselves, with their own well-considered interfaces, and perhaps something like PostgREST as an adapter to the frontend if you don’t really need sophisticated service layers. The read/write pattern you recommend is a great fit for this and can be scaled horizontally with read replicas.
- TimPC 4y agoModules aren't an alternative to microservices in a reasonable way though. And for all modules solve the modularization problem at the code level, they don't really solve modularization at the service level. The main alternative to microservices is monoliths and for many applications I far prefer microservices to monoliths. I want modularization in how I scale my app, I don't want to spin up a whole bunch of servers that can serve almost any possible function in production. I'd prefer more delineated responsibilities. I don't think modularization solves this if after the modules you just throw all your modules in one big bucket and serve that up.
- ebiester 4y agoI think what is missing here is that it doesn't have to be an either-or at the organizational level. If you have a particular piece of the system that needs to be scaled, you can take that module out when it becomes necessary. You can alter your system such that you deploy the entire codebase but certain APIs are routed to certain boxes, and you can use batch processing patterns with a set of code outside the codebase. You can have an admin application and a user application, and both of them are monoliths. They may or may not communication using the database or events or APIs. However, you don't make this on the single bounded context guideline.
- michaelsalim 4y agoThat's exactly what the comment you're replying is saying though. You're talking about scalability which they say modules doesn't exactly solve. And I do agree that most people need less of those than they think.
- esailija 4y agoJust because the monolith image contains all code doesnt mean each deployment needs to run all that code. Or even same version of the code. A deployment can run only a small portion of the code determined by deployment args. It can even run an older version than other deployments
- zbobet2012 4y agoThat's just a microservice enabled by configuration.
- MuffinFlavored 4y ago> But most people need it a lot less than they think. Normally the database is your bottleneck and if you keep your application server stateless, you can just run lots of them At my last job, there were quite a few times where being able to scale some small "microservice instance" up from 2 -> 4 instances or 4 -> 8 or 8 -> 12 was a lot easier/quicker than investigating the actual issue. It'd stop production outages/hiccups. It was basically encouraged. Not sure how that can be done with a "it's all modules in a giant monolith".
- jacobr1 4y agoA few ways, the easiest being to scale up the whole monolith with more instance. Another way is run multiple "services" using the same codebase, so you have workload segmentation, either via synchronous network calls, or async worker systems.
- MuffinFlavored 4y agodevil's advocate: > A few ways, the easiest being to scale up the whole monolith with more instance as far as I know, there's no way to granularly scale up a monolith. if the monolith has 20 or 50 or 100 modules and you need 1 or 2 of them scaled, you have to scale up the entire thing which is huge, expensive, etc. > Another way is run multiple "services" using the same codebase, so you have workload segmentation, either via synchronous network calls, or async worker systems. this is interesting. a monolith with some form of IPC? why not do microservices at that point? that sounds like microservices-ish?
- jacobr1 4y ago> you have to scale up the entire thing which is huge, expensive, etc. Yes, yet people still do it that way. This is tradeoff against the costs of microservices. I'm not saying it is worth it, but yes, sometimes you can just inefficiently throw hardware resources at scaling problems. > this is interesting. a monolith with some form of IPC? why not do microservices at that point? that sounds like microservices-ish? Because you are incrementally changing an existing monolith, but still getting some of the benefits of scaling distinct workloads independently. If you do this right, it does lend it self to reworking the new "service" to be become its own microservice. Or you can just keep going with the shared codebase.
- lifeisstillgood 4y agoI think it runs a bit deeper than that. Microservices are how the business views its self. Look, once upon a time managers designed the system that workers implemented. The factory assembly line, or the policy manual. But now the workers are CPUs and servers. The managers designing the system the workers follow are coders. I say coders are the new managers. Now this leaves two problems. The first is that there are a lot of "managers" who are now doing the wrong job. (and explains perfectly why Steve Jobs gets involved in emails about hiring coders.) But that's a different post. For me the second problem is microservices represent the atomic parts of a business. They should not be seen as a technical solution to anything, and because the first problem (managers arent managing workers anymore) there is no need to have the microservices built along Conways Law. And so if you want to build a microservice it is not enough to consider the technical needs, it is not enough to automate what was there, you need to design an atomic building block of any / your company. They become the way you talk about the company, the way you think about the processes in a company. An mostly the company becomes programmable. It is also highly likely the company becomes built mostly of interchangable parts.
- zbobet2012 4y ago> there is no need to have the microservices built along Conways Law This is a misunderstanding of Conway's Law. Your code _will_ reflect the structure of your organization. If you use microservices so will their architecture. If you use modules, so will the modules. If you want a specific architecture, have your organization reflect the modules/microservices defined in that architecture.
- zeroonetwothree 4y agoExactly. Conway's Law is descriptive not prescriptive!
- bradwood 4y agoQuite. And many people still seem to confuse this. The way to exploit Conway's law is not to do anything _technical_ but rather to do something _organisational_. The "Reverse Conway Manoeuvre" is the term, IIRC, for this. Rather than trying to "build something along the lines of Conway's law" (which doesn't really make sense) -- one should determine a desired architecture and then set up the _organisation_ and teams to mirror that.
- NicoJuicy 4y agoIt's called "Conway's law" https://ardalis.com/conways-law-ddd-and-microservices/#:~:text=Conway's%20Law%20states%20that%20%22any,Domain%2DDriven%20Design%20are%20adopted https://ardalis.com/conways-law-ddd-and-microservices/#:~:te....
- eklitzke 4y agoI just want to point out that for the second problem (scalability of CPU/memory/io), microservices almost always make things worse. Making an RPC necessarily implies serialization and deserialization of data, and nearly always also means sending data over a socket. Plus the fact that most services have some constant overhead of the footprint to run the RPC code and other things (healthchecking, stats collection, etc.) that is typically bundled into each service. And even if the extra CPU/memory isn't a big deal for you, doing RPCs is going to add latency, and if you get too many microservices the latency numbers can start really adding up and be very difficult to fix later. Running code in a single process is MUCH lower overhead because you don't need to transit a network layer and you're generally just passing pointers to data around rather than serializing/deserializing it. There are definitely some cases where using microservices does make things more CPU/memory efficient, but it's much rarer than people think. An example where you'd actually get efficiency would be something like a geofence service (imagine Uber, Doordash, etc.) where the geofence definitions are probably large and have to be stored in memory. Depending on how often geofence queries happen, it might be more efficient to have a small number of geofence service instances with the geofence definitions loaded in memory rather than having this logic as a module that many workers need to have loaded. But again, cases like this are much less common than the cases where services just lead to massive bloat. I was working at Uber when they started transitioning from monolith to microservices, and pretty much universally splitting logic into microservices required provisioning many more servers and were disastrous for end-to-end latency times.
- jupp0r 4y agoI think all your points are correct but none of them address scalability. What do you do if your one efficient machine is too small for your problem? To be fair, not a lot of problems reach that point in practice, definitely not every problem a microservice is written for. It might still be completely viable to rewrite something so that it's only 10% as efficient but you can scale it horizontally easily.
- spullara 4y agoI do think using something like Flatbuffers or CapnProto where the SerDe is removed (aside from the travel over the network) could be a huge win. Another thing I have always wanted to try is to develop a system using microservices with a RPC layer that uses immutable objects so you can later combine the services and convert RPCs to function calls.
- gregors 4y agoI very much agree with you on points 1 & 2. I'll add a 3rd - drying up repeated functionality such as repeated auth logic in completely separate applications.
- brhsagain 4y agoI don't understand the argument around microservices purporting to fix scalability. Say your system has modules A, B and C in one app, and that A requires more resources while B and C are relatively unused. Won't B and C just run less and thereby use appropriately fewer resources from within the same system as A? Are microservices just an aesthetic separation (it feels nice to know you're "only" scaling up A)?
- linza 4y agoIt doesn't necessarily fix it but has less resource waste or is easier to manage than the bin packing needed otherwise.
- deleted 4y ago[deleted]
- mk89 4y ago> A better rule is for one service to own writes for a table, and other services can only read that table, Been there: how do you handle schema changes? One of the advantages that having a separate schema per service provides is that services can communicate only via APIs, which decouples them allowing you to deploy them independently, which is at the heart of microservices (and continuous delivery). The way I see it today: everyone complains about microservices, 12 factor apps, kubernetes, docker, etc., and I agree they are overengineering for small tools, services, etc., but if done right, they offer an agility that monoliths simply can't provide. And today it's really all about moving fast(er) than yesterday.
- jmchuster 4y agoFor us, we started off with a world where each service communicates to each other only via RabbitMQ, so all fully async. So theoretically each service should be able to be down for however it likes with no impact to anyone else, then it comes back up and starts processing messages off its queue and no one is the wiser. Our data is mostly append-only, or if it's being changed, there is a theoretical final "correct" version of it that we should converge to. So to "get" data, you subscribe to messages about some state of things, and then each service is responsible for managing its own copy in its own db. This worked well enough until it didn't, and we had to start doing true-ups from time to time to keep things in sync, which was annoying, but not particularly problematic, as we design to assume everything is async and convergent. The optimization (or compromise) we decided on, was that all of our services use the same db cluster, and that if the db cluster goes down, it means everything is down. Therefore, if we can assume the db is always up, even if a service is down, we consider it an acceptable constraint to provide a readonly view into other services db. Any writes are still sent async via MQ. This eliminates our syncing drifting problem, while still allowing for performant joins, which http apis are bad at and our system uses a lot of. So then back to your original question, the way that this contract can break is via schema changes. So for us, since we use postgres, we created database views that we expose for reading. And postgres view updates are constrained that they must always be backwards compatible from a schema perspective. So then now our migration path is: - service A has some table of data that you like to share - write a migration to expose a view for service A - write an update for service B to depend upon that view - service B now needs some more data in that view - write a db migration for service A that adds that missing data, but keeping the view fully backwards compatible
- throwaway894345 4y ago> There's two technical problems that microservices purport to solve There's a third technical problem that microservices solve, and it's my favorite: isolation. With monoliths, you provide all of your secrets to the whole monolith, and a vulnerability in one module can access any secret available to any other module. Similarly, a bug that takes down one module takes down the whole process (and probably the whole app when you consider cascading failures). In most mainstream languages, every module (even the most unimportant, leaf node on the dependency tree) needs to be scoured for potential security or reliability issues because any module can bring the whole thing down. This isn't solved at the language level in most mainstream languages. The Erlang family of languages generally address the reliability issue, but most languages punt on it altogether. > The real reason that microservices may make sense is because they keep people honest around module boundaries. Agreed. Microservices, like static type systems, are "rails". Most organizations have people who will take shortcuts in the name of expedience, and systems with rails disincentivize these shortcuts (importantly, they don't preclude them).
- thayne 4y agoAnother benefit of microservices is it allows you to have a permission boundary, which can restrict the scope of damage if a single server/service is compromised. And for that matter it can (but not necessarily) limit how much damage a bad code release can do. Of course you don't get those benefits for free just by using microservices, but impleminting those kind if boundaries in a monolith is a lot harder.
- JAlexoid 4y agoThe same techniques used to restrict intrusion, can be applied to a monolith. Microservices aren't inherently more secure.
- thayne 4y agoReally? How do you make it so that different modules in the same process have different IAM credentials and can't get the credentials for a different module? How do you make sure a buffer overflow in you analytics module doesn't allow an attacker to read memory from you login module? How do you make sure an RCE in your image upload code doesn't give an attacker access to credentials for your payment processor? Maybe it is possible with some low-level system calls, but at the very least it is a lot more difficult than using separate VMs or containers.
- JAlexoid 4y agoGiving me an example that is created to work explicitly at system/container level isn't the "gotcha" you think it is.(IAM profiles have their own limitations) Separate process vaults, HSMs and other techniques of offloading security credentials - are the same for microservice architecture, as "monolith". The implication that anything other than a microservice architecture must be exclusively an uber executable doing literally everything, is naive.
- thayne 4y ago> Separate process vaults, HSMs and other techniques of offloading security credentials How do give one module access to the vault/HSM without also giving any other code in the same process access? Even in the event of a security compromise. And that still doesn't address the problem of a vulnerability anywhere in the monolith potentially exposing any sensitive data in processes memory (such as user data including password). > The implication that anything other than a microservice architecture must be exclusively an uber executable doing literally everything, is naive. Ok, replace "microservices" with "service oriented architecture". I'm comparing specifically against a monolith where you have an "uber executable doing literally everything".
- Sparkyte 4y agoWhat it is is using the wrong solution for a problem. Vice-versa the number of devs who justify monolithic apps is absurd. The justification of microservices is just as problematic. It is about striking a balance. No reason should something be overly compounded or overly broken up.
- brunoborges 4y ago> scalability (being able to increase the amount of compute, memory and IO to the specific modules that need it). I gave a talk [1] about scalability of Java systems on Kubernetes, and one of my recommendations is that Java-based systems - or any system on a runtime similar to the JVM, like CLR or even Go - should be scaled diagonally. Efficiency is the word. While horizontal scaling can easily address most performance issues and load demand, in most cases it is the least efficient way for Java (and again, I risk saying .NET and Go), as these systems struggle with CPU throttling and garbage collectors. In short, and exemplifying: one container with 2 CPUs and 2GB of RAM will allow the JVM to perform better, in general, than 2 containers with 1 CPU/1GB RAM each. That said, customers shouldn't be scaling horizontally to any amount more than what is adequately reasonable for resiliency, or unless the bottleneck is somewhere like disk access. For performance on the other hand, customers should be scaling vertically. And Kubernetes VPA is already available. People just need to use it properly and smartly. If a Kubernetes admin believes a particular pod should double in number of replicas, the admin should consider: "would this microservice benefit even more from 1.5x more CPU/Memory than 2x more replicas?" and I bet to say that, in general, yes. [1] https://www.youtube.com/watch?v=wApqCjHWF8Q https://www.youtube.com/watch?v=wApqCjHWF8Q
- kevincox 4y agoBasically any software, even something in C or C++ with manual memory management will scale more efficiently vertically than horizontally. Basically it would be better to run one twice as big as it would be to run two instances on the same machine. There is almost always some overhead or cache that will make the performance of fewer instances better. The only exception is resource contention but it is generally not too hard to fix the important cases. My default recommendation has always been to make instances "as big as possible, but no bigger". You may need 3 instances for redundancy, fault tolerance and graceful updates but past that you should probably scale up to near the size of your machine. There are obviously lots of complications and exceptions (for example maybe using most 3 instances uses 90% of your machines so you can't bin pack other processes there so it is better to use 4 instances at 70% as the machines will be used more efficiently) but bigger by default is generally a good option.
- brundolf 4y agoOne other thing they give you at a large organization is flexibility in your stack If a team wants to try out a different language, or hosting model, or even just framework/tooling, those things can be really hard to do within the same codebase; much easier when your only contract is handling JSON requests. And if your whole organization is totally locked into a single stack, it's hard to keep evolving on some axes (I'm generally against microservices, but this is one of the more compelling arguments I've heard for them, though it still wouldn't mean you need to eagerly break things up without a specific reason)
- mstibbard 4y agoForcing consistent language, runtime, tooling etc is generally a good thing as it reduces ongoing operational burden as well as some retention & hiring dilemmas.
- oflor 4y agoTech zoo sometimes considered to be an anti-pattern in microservices. By introducing a different language into your organization, you decrease the mobility of developers between code bases and dilute technical knowledge.
- brundolf 4y agoEverybody shouldn't pull in their favorite stack just for fun, but it seems valuable to have the option of trying out new things that could turn out to be a better way forward for the org
- JAlexoid 4y agoNot worshiping the Microservice, doesn't mean that you avoid services in different languages. Not all engineers need to move around - DS and MLEs are generally useless on the frontend, and vice versa.
- oflor 4y agoRewriting a module from one tech stack to another is not much harder when that module was a part of a monolith and not a separate service, except that you haven't paid the upfront cost of bootstrapping a new service, putting in RPC calls, etc. And in any case, starting a project as microservices is already a bad practice due to a number of reasons, the most important for me is that it's hard to change module boundaries, which you will most likely get wrong in a new project.