News for hackers. Stuff that matters.
Re:Give Me Back My Monolith by mirkules on Wednesday March 13, 2019 @06:18PM (#19384499)
We’ve done exactly this - turned a team of 15 engineers from managing one giant monolith to two teams managing about 10 or so microservices (docker + kubernetes, OpenAPI + light4j framework).

Even though we are in the early stages of redesign, I’m already seeing some drawbacks and challenges that just didn’t exist before:

- Performance. Each of the services talks to the other service via well-defined JSON interface (OpenAPI/Swagger yaml definitions). This sounds good in theory, but parsing JSON and then serializing it N times has a real performance cost. In a giant “monolith” (in the Java world) EJB talked to each other, which despite being java-only (in practice), was relatively fast, and could work across web app containers. In hindsight, it was probably a bad decision to JSON-ize all the things (maybe another protocol?)

- Management of 10-ish repositories and build jobs. We have Jenkins for our semi-automatic CI. We also have our microservices in a hierarchy, all depending on a common parent microservice. So naturally, branching, building and testing across all these different microservices is difficult. Imagine having to roll back a commit, then having to find the equivalent commit in the two other parent services, then rolling back the horizontal services to the equivalent commit, some with different commit hooks tied to different JIRA boards. Not fun.

- Authentication/Authorization also becomes challenging since every microservice needs to be auth-aware.

As I said we are still early in this, so it is hard to say if we reduced our footprint/increased productivity in a measurable way, but at least I can identify the pitfalls at this point.

Re:Give Me Back My Monolith by bunderbunder on Thursday March 14, 2019 @12:07AM (#19386458)
The first thing I start trying to convince people to ditch on any internal service is JSON.

There are only two times when JSON is a particularly good choice: when it's important for the messages themselves to be human-readable, or when it's important to be able to consume it from JavaScript without using a library. Any other time, something like protocol buffers is going to give you lower latency, lower bandwidth requirements, lower CPU costs, lower development effort, less need for maintaining documentation, and better standardization.

If you ditch the HTTP stuff while you're at it, you can also handily circumvent all the ambiguities and inter-developer holy wars that are all but inherent to the process of taking your service's semantics, whatever they are, and trying to shoehorn them into a 30-year-old protocol that was really only meant to be used for transferring hypermedia documents. Instead you get to design your own protocol that meets your own needs. Which, if you're already building on top of something like protobuf, will probably end up being a much simpler and easier-to-use protocol than HTTP.

Re:Give Me Back My Monolith by stouset on Thursday March 14, 2019 @02:52AM (#19387008)
Not to mention, JSON APIs start out simple because you just call `to_json` on whatever object you need to share and then move on. Except nobody’s ever documented the format, and handling ends up taking place in a million different places because you can just pass the relevant bits around. The API is “whatever it did before we split it up”.

Now when someone goes to replace one side, it’s often impossible to even figure out a full definition of the structure of the data, much less the semantics. You watch a handful of data come across the pipe, build a replacement that handles everything you’ve seen, and then spend the next few months playing whack-a-mole fixing bugs when data doesn’t conform to your structural or semantic expectations.

JSON lets you get away with never really specifying your APIs, and your APIs often devolve to garbage by default. Then it becomes wildly difficult to ever replace any of the parts. JSON for internal service APIs is unmitigated evil.

Re:Give Me Back My Monolith by humbleMouse on Thursday March 14, 2019 @10:15AM (#19389369)
You could just make a shared object module that is versioned and have all the microservices use those objects to communicate. Or you could implement avro schemas. Many ways around this issue.
Re:Give Me Back My Monolith by jypepin on Thursday March 14, 2019 @06:25AM (#19387918)
I don't know if I would recommend NOT using json when starting out (I'm on the side of pushing for a monolith for as long as possible), but yes omg I've been there.

I've migrated multiple services out of our monolith into our micro service architecture and oh boy, it is just impossible to know (or find someone who knows) what structure is passed around, or what key is actually being used or not. Good luck logging everything and pulling your hair documenting everything from the bottom up.

Re:Give Me Back My Monolith by geezerjay on Thursday March 14, 2019 @04:50AM (#19387491)
> Except nobody’s ever documented the format,

That's hardly a JSON problem. You still experience that problem if you adopt any undocumented document format or schema.

Re:Give Me Back My Monolith by danmaz74 on Thursday March 14, 2019 @05:52AM (#19387751)
I think the point the OP was making was more about the tooling than the format. Having a library that forces you to define the format you're using, instead of dumping to_json whatever internal representation you had, also doubles as documentation.

But then, of course, that can be considered boilerplate code (and, in the beginning and most of the time, it actually is just a duplication of your internal object structure).

Re:Give Me Back My Monolith by bunderbunder on Thursday March 14, 2019 @09:47AM (#19389150)
That, and having the format definition file support comments means that you have a convenient all-in-one place where, at lest in the more straightforward cases, you can handily describe both the message format and the service's behavior in a single file that's easy to share among all your teams.
Re:Give Me Back My Monolith by stouset on Thursday March 14, 2019 @12:09PM (#19390561)
Precisely.

The easiest path with JSON is to do none of this, and so the majority of teams (particularly inexperienced ones) do none of it. With protos, someone must at least sit down and authoritatively outline the structure of any data being passed around, so at a minimum you’ll have that.

But even just forcing developers to do this generally means they start thinking about the API, and have a much higher chance of documenting the semantics and cleaning up parts that might otherwise have been unclear, confusing, or overly complex.

Re:Give Me Back My Monolith by mruts on Thursday March 14, 2019 @05:53AM (#19387756)
I’m surprised that bencoded dictionaries haven’t ever become popular outside of bittorrent. They are expressive, fast, and easy to implement.
Re:Give Me Back My Monolith by CoolGuySteve on Thursday March 14, 2019 @01:54AM (#19386810)
Man if you think protobuf is fast, just wait until you try C pod structs with type-length headers.

They are literally infinitely faster to encode/decode than protobuf.

They even have the same obnoxious append-only extensibility of protobuf if that’s what really gets your jimmies firing.

Re:Give Me Back My Monolith by Danieru on Thursday March 14, 2019 @02:49AM (#19386996)
FlatBuffers and then Endian concerns are gone.
Re:Give Me Back My Monolith by presscast on Thursday March 14, 2019 @06:17AM (#19387875)
Are FlatBuffers essentially Capnproto without the RPC?
Re:Give Me Back My Monolith by alexhutcheson on Thursday March 14, 2019 @11:08AM (#19389883)
FlatBuffers can be used with gRPC, so they shouldn't need a separate RPC ecosystem like Cap'n Proto: https://grpc.io/blog/flatbuffers
Re:Give Me Back My Monolith by presscast on Thursday March 14, 2019 @12:07PM (#19390542)
Right, but I'm asking if FlatBuffers' approach to encoding is similar to Cap'n Proto. It seems like the answer is "yes", but I might be missing something.
Re:Give Me Back My Monolith by kentonv on Thursday March 14, 2019 @12:15PM (#19390628)
They are both zero-copy, but the designs have a number of differences. I compared them back in 2014: https://capnproto.org/news/2014-06-17-capnproto-flatbuffers-...

Disclaimers:

1) I'm the author of Cap'n Proto; I'm probably biased.

2) A lot could have changed since 2014. (Though, obviously, serialization formats need to be backwards-compatible which limits the amount they can change...)

Re:Give Me Back My Monolith by presscast on Thursday March 14, 2019 @12:20PM (#19390687)
Cool, thanks for this! I'm happily using Cap'n Proto in a side project, and so far have really enjoyed working with it. It's a really impressive piece of engineering.
Re:Give Me Back My Monolith by netmonk on Thursday March 14, 2019 @01:57AM (#19386818)
asn1 for the win
Re:Give Me Back My Monolith by carapace on Thursday March 14, 2019 @01:53PM (#19391652)
It's a shame you're catching downvotes for mentioning ASN1. It's so much better than all these newfangled things that it makes them look like toys.
Re:Give Me Back My Monolith by rkeene2 on Saturday March 16, 2019 @03:57PM (#19409853)
I'm not sure why ASN.1 isn't more popular for a zero-copy encoding system
Re:Give Me Back My Monolith by StavrosK on Thursday March 14, 2019 @07:25AM (#19388229)
What serialization format do you all recommend when starting out? Protobufs seems to be the go-to, but what about Cap'n Proto etc?
Re:Give Me Back My Monolith by alexhutcheson on Thursday March 14, 2019 @10:57AM (#19389778)
(personal opinion, not speaking on behalf of my employer)

Start out with protobufs, so you can take advantage of gRPC[1] and all of its libraries and all the other tooling that is out there.

If you profile and determine that serialization/deserialization is actually a bottleneck in your system in a way that is product-relevant or has non-trivial resource costs, then you can look at migrating to FlatBuffers, which can still use gRPC[2].

[1] https://grpc.io/ [2] https://grpc.io/blog/flatbuffers

Re:Give Me Back My Monolith by StavrosK on Thursday March 14, 2019 @11:07AM (#19389872)
Ah, good tip, thanks!
Re:Give Me Back My Monolith by bunderbunder on Thursday March 14, 2019 @12:15PM (#19390626)
I'd agree on starting with protobufs, precisely because it's the go-to. Other options have plenty of advantages, but none are as widely supported.
Re:Give Me Back My Monolith by merb on Wednesday March 13, 2019 @06:21PM (#19384525)
> So naturally, branching, building and testing across all these different microservices is difficult. Imagine having to roll back a commit, then having to find the equivalent commit in the two other parent services, then rolling back the horizontal services to the equivalent commit

that should not happen. if it does you don't have a microservice architecture, you have a spaghetti service architecture.

Re:Give Me Back My Monolith by tabtab on Wednesday March 13, 2019 @06:25PM (#19384548)
How does one know if X is the wrong solution or if X is the right solution but the shop is "doing X wrong"? This also applies to monoliths: maybe they can scale, but one is doing them wrong. Changing from doing monoliths wrong to doing microservices wrong is obviously not progress.

The same issue appeared when OOP was fairly new: people started using it heavily and ended up making messes. They were then told that they were "doing it wrong". OOP was initially sold as magic reusable Lego blocks that automatically create nice modularity. There was even an OOP magazine cover showing just that: a coder using magic Legos that farted kaleidoscopic glitter. Microservices is making similar promises.

It took a while to learn where and how to use OOP and also when not to: it sucks at some things.

Re:Give Me Back My Monolith by taneq on Wednesday March 13, 2019 @08:57PM (#19385558)
Simple:

If X is a technology I don't like, and it's not working for you, then it's the wrong solution.

If X is a technology I don't like, and it is working for you, then you simply haven't scaled enough to understand its limitations.

If X is a technology I like, but it's not working for you, then your shop is "doing X wrong".

If X is a technology I like, and it's working for you, then it's the right solution and we're both very clever.

Re:Give Me Back My Monolith by staticassertion on Wednesday March 13, 2019 @10:48PM (#19386130)
edit: See below
Re:Give Me Back My Monolith by taneq on Wednesday March 13, 2019 @11:02PM (#19386193)
Uh, my post was snark towards engineers' general tendency to champion and defend their own favourite architecture over competing approaches rather than focusing on the most suitable architecture for the circumstance, in response to this question:

> How does one know if X is the wrong solution or if X is the right solution but the shop is "doing X wrong"?

Edit: And a civil conversation ensued. :)

Re:Give Me Back My Monolith by staticassertion on Wednesday March 13, 2019 @11:19PM (#19386278)
Sorry, I just misread your post as being more of an attack. As I had said, I dislike the constant "hype means something is bad" posts that I've been seeing for years - I think it's really unfortunate.
Re:Give Me Back My Monolith by taneq on Wednesday March 13, 2019 @11:34PM (#19386333)
No worries, I can see how it might have been read that way in the wider context of the conversation. And I totally hear you on the "hype equals bad" thing. If something's popular, it's popular for a reason. That reason MIGHT just be trendoids jumping on a hype train, but it might also be because the thing is good.
Re:Give Me Back My Monolith by freedomben on Wednesday March 13, 2019 @07:35PM (#19385081)
If you want to learn "the right way" I highly recommend "Building Microservices: Designing Fine-Grained Systems" by Sam Newman[1]

[1] https://smile.amazon.com/Building-Microservices-Designing-Fi...

Re:Give Me Back My Monolith by notyourday on Wednesday March 13, 2019 @08:13PM (#19385299)
I have posted about this before. OP is describing nano-services, not micro-services. Nano-service is micro-service that provides leftpad via JSON api.

You have an auth.yourapp.com and api.yourapp.com and maybe tracer.yourapp.com and those three things are not a single app that behaves like auth, api or tracer depending on setting of a NODE_ENV variable? If so, you have micro services.

Re:Give Me Back My Monolith by dragonwriter on Wednesday March 13, 2019 @07:09PM (#19384901)
> that should not happen. if it does you don't have a microservice architecture, you have a spaghetti service architecture.

A "service" is not defined principally by a code repository or communications-channel boundary (though it should have the latter and may have the former), but by a coupling boundary.

OTOH, maintaining a coupling boundary can have non-obvious pitfalls; e.g., supported message format versions can become a source of coupling--if you roll back a service to the version that can't send message format v3, but a consumer of the messages requires v3 and no longer supports consuming v2, then you have a problem.

Re:Give Me Back My Monolith by merb on Wednesday March 13, 2019 @07:20PM (#19384973)
you should never actually remove an api without a deprecation process. basically a message format is part of your api. actually in a microservice world your interfaces/api should be as stable as possible. if something gets changed, it needs to go trough a deprecation, that means not removing it until a certain amount of time.
Re:Give Me Back My Monolith by xj9 on Wednesday March 13, 2019 @10:11PM (#19385960)
private apis exist for a reason. going through the deprecation cycle repeatedly sounds like a waste of time if you control all sides of the system. which is to say, not every interface needs to be a service boundary. you do need to keep versioned apis, but there is a cost to doing so.
Re:Give Me Back My Monolith by organsnyder on Wednesday March 13, 2019 @10:22PM (#19386024)
IMHO, part of a successful microservices program is treating your "private" APIs like your public ones, with SLAs, deprecation protocols, etc. Otherwise, you end up with tight coupling to the point that you should just go with a monolith.
Re:Give Me Back My Monolith by galangalalgol on Thursday March 14, 2019 @11:03AM (#19389830)
When you are still figuring out the problem space and message formats are being created removed extended and reverted several times a sprint, and you have already gone live with third parties depending on stable interfaces so that you cannot make the changes you need in a timely fashion, the warm arms of a purpose built monolith look particularly attractive.
Re:Give Me Back My Monolith by organsnyder on Thursday March 14, 2019 @11:38AM (#19390199)
Indeed. At that early stage of development, a monolith (or at least monolith-style practices, such as tight coupling between API providers and consumers) is definitely simpler and more efficient. But it wouldn't hurt to take steps from the beginning to make it easier to break the monolith apart when/if it becomes pragmatic to do so.
Re:Give Me Back My Monolith by lmm on Thursday March 14, 2019 @07:31AM (#19388259)
> private apis exist for a reason. going through the deprecation cycle repeatedly sounds like a waste of time if you control all sides of the system. which is to say, not every interface needs to be a service boundary.

The whole point of a microservice is to create a service boundary. If you have a private interface where both sides are maintained by the same team, both sides should be in the same service.

Re:Give Me Back My Monolith by geezerjay on Thursday March 14, 2019 @05:04AM (#19387540)
All interfaces are service boundaries. The only difference is whether you controll all clients and servers or not. Either way, API versioning is a must and trivial to implement, and there is really no excuse to avoid it. All it takes is a single integration problem caused by versioning problemd to waste more time than it takes to implememt API versioning.
Re:Give Me Back My Monolith by NicoJuicy on Wednesday March 13, 2019 @09:30PM (#19385760)
The accept header could fix this easily if no third parties are involved and if it's a rest like API protocol
Re:Give Me Back My Monolith by dragonwriter on Thursday March 14, 2019 @12:03AM (#19386441)
The accept header is a mechanism for communicating formats you support; it doesn't do anything to address the problem of managing change to supported versions, which is a dev process issue, not a technical issue.
Re:Give Me Back My Monolith by NicoJuicy on Thursday March 14, 2019 @08:45AM (#19388699)
Feature flags, just enable it.

When all applications are adjusted, the accept-headers request a protobuff format in return.

=> Propagated everywhere except when a js ajax calls happens to the api-gateway.

Re:Give Me Back My Monolith by geezerjay on Thursday March 14, 2019 @05:06AM (#19387547)
> The accept header is a mechanism for communicating formats you support; it doesn't do anything to address the problem of managing change to supported versions

That assertion is not true. Media type API versioning is a well established technique.

Re:Give Me Back My Monolith by NicoJuicy on Wednesday March 13, 2019 @09:30PM (#19385758)
The accept header could fix this easily if no third parties are involved
Re:Give Me Back My Monolith by sequoia on Wednesday March 13, 2019 @08:56PM (#19385549)
It sounds like you're paraphrasing the great Steven Jobs: "You're holding it wrong"

Be that as it may, I believe mirkules's issue is not an uncommon one. Perhaps saying "building a microservice architecture 'the right way' is a complex and subtle challenge" would capture a bit of what both of you are saying.

Something being complex and therefor easy to mess up does not mean it's a great system and the users are dumb, especially if there are other (less complicated, less easy to mess up) ways to complete the task.

Re:Give Me Back My Monolith by geezerjay on Thursday March 14, 2019 @04:57AM (#19387523)
> Perhaps saying "building a microservice architecture 'the right way' is a complex and subtle challenge" would capture a bit of what both of you are saying.

Supporting API versioning is not a complex or subtle challenge. It's actually a very basic requirement of a microservice architecture. It's like having to test null pointers: it requires additional work but it's still a very basic requirement if you don't want your app to blow up in your face.

Re:Give Me Back My Monolith by mirkules on Wednesday March 13, 2019 @06:30PM (#19384599)
Heh, I like that “Spaghetti Microservices”.

You are right. It should not happen. It is difficult to see these pitfalls when unwinding an unwieldy monolith, and, as an organization all you’ve ever done are unwieldy monoliths, that have a gazillion dependencies, interfaces and factories.

We learned from it, and we move on - hopefully, it serves as a warning to others.

Re:Give Me Back My Monolith by tcbasche on Wednesday March 13, 2019 @06:38PM (#19384675)
I've heard the term 'Distributed Monolith'
Re:Give Me Back My Monolith by c3534l on Thursday March 14, 2019 @01:16AM (#19386686)
I think we should call it angel-hair pasta code. You know, because it's very small spaghetti.
Re:Give Me Back My Monolith by james_s_tayler on Wednesday March 13, 2019 @06:55PM (#19384802)
MicroPasta
Re:Give Me Back My Monolith by cestith on Wednesday March 13, 2019 @07:00PM (#19384848)
Tangled angelhair.
Re:Give Me Back My Monolith by smnplk on Thursday March 14, 2019 @05:50AM (#19387743)
No, it's ravioli oriented architecture
Re:Give Me Back My Monolith by balp on Thursday March 14, 2019 @12:38AM (#19386564)
To handle deploys and especially rollbacks you need a working CI, or rather CD chain. Where eveything is automated. In there are dependencied in your architecture, all of that needs to be part of the same deploy so you can redo the deploy before that one. With a monolith, you get a simple deploy of all dependencies, as they are baked into one thing. There are down sides of everything baked into one thing. Having all your "micoro" servies deplyed as one package, would make the dependency you have act as before the monolith change. Seeing this kinds of dependency imho even in a monolith are an architechture problem, that in the monolith shows up at code changed and fixes can't be handles by localized fixes, but changed have to go into large parts of the code base.
Re:Give Me Back My Monolith by jeffasinger on Wednesday March 13, 2019 @09:01PM (#19385587)
I think a bigger thing here, is that each deploy should have a REALLY good reason to not be backwards compatible for some pretty long time period. If that requirement is painful for you, then you probably have two pretty tightly coupled services.
Re:Give Me Back My Monolith by tanilama on Wednesday March 13, 2019 @09:29PM (#19385748)
Why so many microservices?

Monolith has huge advantage when maybe your code is like 100k lines or below:

1. Easy cross module unit testing/integration testing, thus sharing components is just easier.

2. Single deployment process

3. CR visibility automatically promotes to all parties of interests, assuming the CR process is working as desired.

4. Also, just a personal preference, easier IDE code suggestion. If you went through json serializing/de-serializing across module boundary, type inference/cohesion is just out-of-reach.

And it is not like monolith doesn't have separation of concern at all. After all, monolith can have modules, and submodules. Start abstracting using file system API, but grouping relevant stuff into folders, before put them into different packages. After all, once diverging, it is really hard to go back.

Unless you have a giant team and more than enough engineers to spare for devops. Micrservices can be considered as a organizational premature optimization.

Re:Give Me Back My Monolith by Rapzid on Wednesday March 13, 2019 @09:03PM (#19385600)
JSON parsing IS expensive. Way more expensive than many people realize. There is actually an almost-underground JSON parser SCENE in the .Net ecosystem where people develop new parser and try to squeeze the maximum performance using all sorts of tricks: https://github.com/neuecc/Utf8Json . Here there is discussion of needing JSON support in .Net core that's faster than Json.Net: https://github.com/dotnet/announcements/issues/90 .

And people say web applications are never CPU bound :)

Re:Give Me Back My Monolith by NicoJuicy on Wednesday March 13, 2019 @09:07PM (#19385623)
gRCP is a little bit faster, Google created protobuff which should be easier to migrate to than gRPC, but the protocol is unreadable ( binary)...

JSON has its advantages, I prefer a feature flag when I need performance ( protobuff vs Json), http headers do the rest

Re:Give Me Back My Monolith by stouset on Thursday March 14, 2019 @02:44AM (#19386974)
gRPC is built on top of protobuf. It’s literally just RPC with protobufs.
Re:Give Me Back My Monolith by tekknik on Wednesday March 13, 2019 @10:35PM (#19386077)
one could argue that if serialization/deserialization is the majority of what your api is spending time on then you’ve got distributed monoliths and shouldn’t be making the call in the first place. also ensuring you have the fastest json parser before having a real problem is premature optimization
Re:Give Me Back My Monolith by 0815test on Wednesday March 13, 2019 @09:24PM (#19385722)
> JSON parsing IS expensive

Well, maybe

https://news.ycombinator.com/item?id=19214387

Re:Give Me Back My Monolith by jimbokun on Wednesday March 13, 2019 @06:51PM (#19384770)
"turned a team of 15 engineers from managing one giant monolith to two teams managing about 10 or so microservices"

Knowing absolutely nothing about your product, this sounds like a bad way to split up your monolith.

Probably something like 5 teams of 3 each managing 1 microservice would be a better way to split things up. That way each team is responsible for defining their service's API, and testing and validating the API works correctly including performance requirements. This structure makes it much less likely services will change in a tightly coupled way. Also, each service team must make sure new functionality does not break existing APIs. Which all make it less likely to have to roll back multiple commits across multiple projects.

The performance issues you cite, also seem to indicate you have too many services, because you are crossing service boundaries often, with the associated serialization and deserialization costs. So each service probably should be doing more work per call.

"all depending on a common parent microservice"

This makes you microservices more like a monolith all over again, because a change in the parent can break something in the child, or prevent the child from changing independently of the parent.

Shared libraries I think are a better approach.

"Authentication/Authorization also becomes challenging since every microservice needs to be auth-aware."

Yes, this is a pain. Because security concerns are so important, it is going to add significant overhead to every service to make sure you get it right, no matter what approach you use.

Re:Give Me Back My Monolith by james_s_tayler on Wednesday March 13, 2019 @06:58PM (#19384831)
Probably more like do your best to reason about the boundaries of each bounded context and pick an appropriate number of services based off that analysis?

Surely splitting up your application along arbitrary lines based on advice of an internet stranger whose never seen the application and doesn't know the product/business domain just isn't sound way of approaching the problem.

Re:Give Me Back My Monolith by toolslive on Thursday March 14, 2019 @06:20AM (#19387890)
in the end, your architecture will reflect your organigram. (IIRC it's Conway's law?)
Re:Give Me Back My Monolith by james_s_tayler on Thursday March 14, 2019 @07:37AM (#19388284)
You are correct.

Conway's Law is profound. Lately I realized even the physical office layout (if you have one) acts as an input into your architecture via Conway's Law.

Re:Give Me Back My Monolith by eweise on Wednesday March 13, 2019 @06:27PM (#19384567)
We use grpc instead of rest for internal synchronous communication but we've also found that by using event pub/sub between services, that there are not many uses cases where we have direct calls between services.

We used to have a parent maven pom and common libraries but got rid of most of that because it caused too much coupling. Now we create smaller more focused common libraries and favor copy/paste code over reuse to reduce coupling. We also moved a lot of the cross cutting concerns into Envoy so that the services can focus on business functionality.

Re:Give Me Back My Monolith by danmaz74 on Wednesday March 13, 2019 @09:33PM (#19385769)
> favor copy/paste code over reuse to reduce coupling.

This looks like a big step backwards to me.

Re:Give Me Back My Monolith by barbecue_sauce on Thursday March 14, 2019 @01:23AM (#19386710)
I would say it depends on the stability of what's being copy/pasted. If it's just boilerplate, it's less concerning.

In my opinion, decoupling should be prioritized over DRYness (within reason). A microservice should be able to live fairly independently from other microservices. While throwing out shared libraries (which can be maintained and distributed independently from services) seems like overkill, it seems much better than having explicit inheritance between microservice projects like the original poster is describing.

Re:Give Me Back My Monolith by danmaz74 on Thursday March 14, 2019 @05:11AM (#19387571)
Sure, no problem with boilerplate.

For any non trivial code, which needs to be maintained and be kept well tested, to the contrary of the OP, I would favor shared libraries over copy/paste.

Re:Give Me Back My Monolith by lucidone on Wednesday March 13, 2019 @09:30PM (#19385755)
How do you handle cases where your client is awaiting a response with a decoupled pub/sub backend? E.g a user creates an account and the client needs to know their user id.

Would that user object be the responsibility of one service, or written to many tables in the system under different services, or...?

Re:Give Me Back My Monolith by RhodesianHunter on Wednesday March 13, 2019 @10:55PM (#19386163)
For one, you could use something like snowflake IDs so that whatever server receives the user data first can generate and return an id for that user before tossing the data on a queue to be processed.
Re:Give Me Back My Monolith by lucidone on Wednesday March 13, 2019 @11:34PM (#19386327)
How would you approach a situation where a client updates a record in service A, and then navigates to a page whose data is returned by service B, which has a denormalized copy of service A's records that hasn't consumed and processed the "UpdatedARecord" event?

Do we accept that sometimes things may be out of sync until they aren't? That can be a jarring user experience. Do we wait on the Service B event until responding to the client request? That seems highly coupled and inefficient.

I'm genuinely confused as to how to solve this, and it's hard to find good practical solutions to problems real apps will have online.

I suppose the front end could be smart enough to know "we haven't received an ack from Service B, make sure that record has a spinner/a processing state on it".

Re:Give Me Back My Monolith by jdmichal on Thursday March 14, 2019 @06:11AM (#19387843)
You use eventing only when eventual consistency is acceptable. In your scenario, it sounds like it is not. So then you should use synchronous communication to ensure the expected experience is met. However, that also means that now you can't do stuff with service B without service A being up. So you're trading user experience against resiliency.

Also, you should check your domains and bounded contexts and reevaluate whether A and B are actually different services. They might still legitimately be separate. Just something to check.

Re:Give Me Back My Monolith by romanows on Thursday March 14, 2019 @12:15AM (#19386490)
Some people advocate that microservices own their data and only provide it through an API. In this scenario, Service B would need to query Service A for the authoritative copy of the record. I think the standard way to deal with the query and network time is, yes, to wait until Service A provides the data and timeout if it takes "too long".

Then your question is about optimizing on top of the usual architecture which hopefully is an infrequent source of pain that is worth the cost of making it faster. I could imagine some clever caching, Service A and Service B both subscribing to a source of events that deal with the data in question, or just combining Service A and B into one component.

Re:Give Me Back My Monolith by eweise on Thursday March 14, 2019 @10:27AM (#19389510)
I would create the account directly from the initial call and return its ID and then publish an account created message. Any other services could receive the message and perform some action such as send a welcome email or do some analytics.
Re:Give Me Back My Monolith by mirkules on Wednesday March 13, 2019 @06:33PM (#19384637)
gRPC and Envoy are exactly the things we are exploring now, although copy/paste would never fly in our org.
Re:Give Me Back My Monolith by praneshp on Thursday March 14, 2019 @11:58AM (#19390432)
I know copy paste is looked down upon, but it's even suggested as a good practice (a little bit) in golang. A little copying is better than a little dependency.

https://go-proverbs.github.io/

Re:Give Me Back My Monolith by nevir on Wednesday March 13, 2019 @06:29PM (#19384584)
Consider larger services than a typical microservice.

For the same reason monoliths tend to split when the organization grows, it is often more manageable to have a small number of services per team (ideally 1, or less).

It's ok if a service owns more than one type of entity.

It's less good if a service owns more than one part of your businesses' domain, however

Re:Give Me Back My Monolith by gav on Wednesday March 13, 2019 @06:45PM (#19384729)
> Consider larger services than a typical microservice.

People seem to forget that there’s a continuum between monolith and microservices, it’s not one or the other.

Multiple monoliths, “medium”-services, monolith plus microservices, and so on are perfectly workable options that can help transition to microservices (if you ever need to get there at all).

Re:Give Me Back My Monolith by jeffasinger on Wednesday March 13, 2019 @09:06PM (#19385619)
That's fine, but reorganizations happen, teams can grow, and there is an advantage to having things be separate services in cases like this.

Definitely don't just stuff unrelated stuff into a service since a team that normally deals with that service is now working on unrelated stuff. If the unrelated stuff takes off, you now have two teams untangling your monolithic service.

That said, I'm a big fan of medium sized services, the kind of thing that might handle 10 or 20 different entities.

Re:Give Me Back My Monolith by mcguire on Wednesday March 13, 2019 @10:57PM (#19386169)
I'm going to go out on a limb here and suggest that parsing (and serializing) JSON is unlikely to be the actual problem, performance-wise. (Although "OpenAPI/Swagger" doesn't fill me with enthusiasm.)

More likely, I suspect, is that either you are shipping way too much data around, you have too much synchrony, or some other problem is being being hidden in the distribution. (I once dealt with an ESB service that took 2.5 seconds to convert an auth token from one format to another. I parallelized the requests, and the time to load a page went from 10 sec to <3; then I yanked the service's code into our app and that dropped to milliseconds.)

Performance problems in large distributed systems are a pain to diagnose and the tools are horrible.

Re:Give Me Back My Monolith by therealdrag0 on Sunday March 17, 2019 @02:07AM (#19412492)
I've been using NewRelic and it's been wondrous around illuminating performance problems.
Re:Give Me Back My Monolith by salamander014 on Thursday March 14, 2019 @08:42AM (#19388682)
The whole point of doing microservices is so that you can split up processing responsibility boundaries reasonably, and each team is responsible for being an "expert" in the service it's responsible for.

This also means that each service should have no other services as dependencies, and if they do, you have to many separate services and you should probably look into why hey aren't wrapped up together.

Using a stream from a different service is one thing: You should have clearly defined interfaces for inter-service communication. But if updating a service means you also need to fix an upstream service, your doing it wrong and are actually causing more work than just using a monolith.

EDIT: and because you have clearly defined interfaces, these issues with updating one service and affecting another service literally cannot exist if you've done the rest correctly.

Re:Give Me Back My Monolith by xvector on Wednesday March 13, 2019 @07:10PM (#19384909)
Perhaps a few ideas:

- Performance: use gRPC/protobuf instead of HTTP/OpenAPI, really not much of a reason to use HTTP/OpenAPI for internal endpoints these days

- Repo Management: No one is stopping you from using a monorepo but yourselves :)

Re:Give Me Back My Monolith by mirkules on Wednesday March 13, 2019 @07:55PM (#19385212)
Even just defining what “internal communication” means is difficult. We definitely suffer from the what-if syndrome- “what if some day we want to expose this service to a client?”

Our product is a collection of large systems used by many customers with very different requirements - and so we often fall into this configurability trap: “make everything super configurable so that we don’t have to rebuild, and let integration teams customize it”

Re:Give Me Back My Monolith by xvector on Wednesday March 13, 2019 @08:30PM (#19385406)
Ah. In that case, you can expose your gRPC endpoints as traditional JSON/HTTP ones with gRPC-Gateway, which supports generating OpenAPI documentation too! Best of both worlds.
Re:Give Me Back My Monolith by tchaffee on Thursday March 14, 2019 @06:04AM (#19387802)
Sounds like you're suffering from not using YNGNI enough: you're not gonna need it. Build what you need now. When that changes, you can change what you built. That was the original intention of Agile methodologies and BDD or TDD. When the tests pass, you're done.
Re:Give Me Back My Monolith by gregorygoc on Wednesday March 13, 2019 @06:27PM (#19384568)
You must be doing something wrong. In a company I work for, we clearly have separated microservices by bounded context, thus making them completely decoupled.
Re:Give Me Back My Monolith by jasonpeacock on Wednesday March 13, 2019 @06:53PM (#19384785)
This is the key - decoupled. When you have to rollback commits across multiple service, they are not decoupled, and you're doing something wrong.

Each service should be fully independent, able to be be deployed & rolled-back w/o other services changing.

If you're making API changes, then you have to start talking about API versioning and supporting multiple versions of an API while clients migrate, etc.

Re:Give Me Back My Monolith by vogre on Wednesday March 13, 2019 @07:08PM (#19384898)
>then you have to start talking about API versioning and supporting multiple versions of an API while clients migrate, etc.

Which adds some more complexity that just does not exist in monolythic architecture

Re:Give Me Back My Monolith by icebraining on Wednesday March 13, 2019 @07:41PM (#19385124)
Sure it does. Once you have a large monolith, the same coordination problems hit. Just ask the Linux kernel developers: https://lwn.net/Articles/769365/
Re:Give Me Back My Monolith by flukus on Wednesday March 13, 2019 @08:45PM (#19385488)
> This sounds good in theory, but parsing JSON and then serializing it N times has a real performance cost.

It's not just the serialization cost but latency (https://gist.github.com/jboner/2841832) as well, every step of the process adds latency, from accessing the object graph, serializing it, sending it to another process and/or over the network, then building up the object graph again.

The fashion in .net apps used to be to separate service layers from web front ends and slap an SOA (the previous name for micro-services) label on it. I experimented with moving the service to in process and got an instant 3x wall clock improvement on every single page load, we were pissing away 2/3rds of our performance and getting nothing of value from it. And this was int the best case scenario, a reasonably optimized app with binary serialization and only a single boundary crossing per user web request.

Other worse apps I've worked on since had the same anti-pattern but would cross the service boundary dozens/hundreds/thousands of times and very simple pages would take several seconds to load. It's enterprise scale n+1.

If you want to share code like this then make a dll and install it on every machine necessary, you've got to define a strict API either way.

Re:Give Me Back My Monolith by clon on Wednesday March 13, 2019 @08:27PM (#19385395)
Don't forget:

- Logging. All messages pertaining to a request (or task) should have a unique ID across the entire fleet of services in order to follow the trail while debugging.

Re:Give Me Back My Monolith by tyingq on Wednesday March 13, 2019 @10:26PM (#19386042)
"correlation id" is probably the best thing to poke into Google for guidance on it.
Re:Give Me Back My Monolith by dustindiamond on Thursday March 14, 2019 @08:20AM (#19388538)
I would recommend investigating APM, opentracing and Uber’s jaeger project.
Re:Give Me Back My Monolith by ElBarto on Thursday March 14, 2019 @03:30AM (#19387159)
That's really a list of how not to develop microservices, or even software...

Thoughts must obviously be given to protocols. Json is an obvious bad choice for this use case...

The point of microservices is loose coupling, including in the code. Having a code hierarchy negates this and arguably is bad practice in general.

Re:Give Me Back My Monolith by philwelch on Wednesday March 13, 2019 @11:19PM (#19386276)
I don't think it's necessarily the best idea to immediately have almost as many services as you have engineers. There are usually more gradual and logical ways to split things up.
Re:Give Me Back My Monolith by badloginagain on Wednesday March 13, 2019 @10:31PM (#19386056)
> We also have our microservices in a hierarchy, all depending on a common parent microservice.

Can you explain this a bit more? I thought the point was to have each service be as atomic as possible, so that a change to one service does not significantly impact other services in terms of rollbacks/etc.

If I'm wrong here let me know, our company is still early days of figuring out how to get out of the problems presented by monolith (or in our case, mega-monolith).

Re:Give Me Back My Monolith by zebraflask on Wednesday March 13, 2019 @08:13PM (#19385301)
These are excellent points. I was just having a conversation yesterday about system design and how there seems to be a tipping point after which the transactional/organizational costs of segmenting services outweigh the benefits.

My unscientific impression is that some of the organizational costs - just keeping the teams coordinated and on the same page - can become even more "expensive" than the technical costs.

Re:Give Me Back My Monolith by jrockway on Wednesday March 13, 2019 @06:26PM (#19384557)
Protocol buffers exist to solve two problems you list, parsing overhead and backwards compatibility.
Re:Give Me Back My Monolith by jcims on Wednesday March 13, 2019 @06:52PM (#19384775)
I always wondered why ASN.1 never really seemed to take off. Tooling maybe?
Re:Give Me Back My Monolith by acdha on Wednesday March 13, 2019 @07:10PM (#19384911)
Yes - both the protocol and tooling are expensive to support, even by 90s standards. Unless you have to use it for compatibility with a service you can’t fix it’d be much better to start with a better implementation of the concept.
Re:Give Me Back My Monolith by throwaway2048 on Wednesday March 13, 2019 @08:49PM (#19385501)
ASN.1 has a number of problems with equivalent parsings (very bad in a security context, has been the source of a number of TLS vulnerabilities), as well as the fact that even discovering if two ASN.1 parsers will give the same result is undecidable.
Re:Give Me Back My Monolith by jon-wood on Thursday March 14, 2019 @06:18AM (#19387881)
I agree with all those drawbacks, but auth is something that can be handled with a bit of one time engineering effort. Where I am all traffic to microservices comes through an API gateway which is responsible for routing traffic to the correct service, and more importantly authorising access to those endpoints. Once the gateway has completed auth it places a signed JWT in the Authorisation header, at which point the microservice's responsbility goes from handling the entire auth process to checking the signature can be verified.
Re:Give Me Back My Monolith by DelightOne on Wednesday March 13, 2019 @07:06PM (#19384886)
> Management of 10-ish repositories and build jobs.

Does each micro service have to live in it's own repository? Especially with a common library everyone uses?

Re:Give Me Back My Monolith by statictype on Wednesday March 13, 2019 @08:13PM (#19385298)
I think having a common library that does anything specific to your product is something of an anti-pattern in micro services.

Its not really a micro service - its a distributed monolith

Re:Give Me Back My Monolith by TheCoelacanth on Wednesday March 13, 2019 @11:16PM (#19386266)
There are always going to be cross-cutting concerns. You don't want to have every microservice implement it's own authentication, auditing, etc. from the ground up.