Microservices Governance with Spring Cloud Contract, guest Marcin Grzejszczak (Ep. 37)

Episode 24 · 55:07 · SRT · archive.org · all Tanzu Talk

0:00

So it's been, I don't know, two months? I can't do calendar math. That was in August since we talked last, right? Yeah, about Zipkin. Yes, that's true. So now, before we get to our meaty topic, what's been going on in those two months? What goes on in your life over two months? Oh, plenty. You can do a final release of Spring Cloud Contract, for example. Release a bazillion of different libraries. So, yeah, plenty of stuff can happen, especially in the Spring Cloud team. And then we were just sort of jumping in here cold, but why don't you introduce yourself? Oh, yeah. So my name is Marcin Zrejstak. I'm working for the Spring Cloud team, focusing mostly on Spring Cloud Sleuth, so distributed tracing, Spring Cloud Contract, the new kid in the block. And also, I do quite a lot of automation, so I play around with Concourse and Jenkins, build pipelines, stuff like that. So, yeah, I'm a testing freak, automation freak. It all maps to the projects that I work on. And then, I forget if we went over this last time, but you got hired from industry, as it were. So you haven't always worked for a vendor. You have experience actually trying to do stuff, not just sell stuff. Yeah, yeah, yeah. So basically, in at least one of the companies I work for, I was doing the inside IT, so to say, right? So we were building business applications for ourselves, which is also good because, you know, you care. So you don't have to, like, tell some project manager that, hey, you should also pay for the test that we're going to write because everybody cares for the software. So that was interesting. Yeah. Now, you gave this talk we were talking about, like, yesterday?

2:01

When was it? It was sometime very recently, right? Yeah. So yesterday, there was the Juktoberfest conference here in Poland, in my hometown of Wurz, where also Dave Sire was talking. So, yeah, yesterday I was talking as well as about Sprinkler Contract, and actually, I think it was pretty nice. Yeah? Well, let's get right into it. I was happy. Yeah, so what is the idea of, like, contracts and consumer-driven contracts in a services world and all that? Like, what, other than defining it, like, what are the issues that create the need for it? Like, whereupon did it come from? Yeah, that's a very good start to any discussion about a new approach, new tool. Actually, speaking of the new stuff here, the consumer-driven contracts is a technique or, like, an approach that is almost, I think, 10 years old, so it's not that new. So it's a good thing to start with, asking a question, like, what problems are we trying to solve? So when I was working in one of the companies, the problem that we had was that when you have communication between services, let's not even call them microservices, just applications, the problem that we had were the integration tests. So what are those integration tests? Because many people use different definition of those tests. So for us, these were the tests that were run during the building of the app, where you were doing real calls to an A, like, via HTTP, or you had an in-memory database. So it was more than a unit, right? There was some integration between some protocols. So the problem that we had were the integration tests between services, because when you're writing a test, at some point, you will, like, reach a moment where you want to send a request, right?

4:06

So what's the idea of testing? You want to, like, increase the confidence that your software works. So how can you, like, test this stuff when two applications are talking to each other? I mean, one of the possibilities is to have the real application set up, like, next to the one that you're testing. But, you know, it takes a lot of time. It takes some knowledge to set up the infrastructure. So it's out of the question, especially during the build time. So let's not even talk about this. This makes more sense when you're doing some end-to-end tests, right? So what you can do is to stub the other service, right? So we're doing some sort of a mock where you say that, you know, when I'm going to shoot you with a request like this, then this is the response that I would expect. And we did that. But the problem was that the moment we wrote the stub of the service that we are calling, it was outdated. Because, you know, the other team was working on some features or stuff like that. So this was the first problem. How can we ensure that the stub that we're using is valid? The other problem was that there were a bunch of services talking to a monolithic app. And every team did the same work of stubbing the monolith. So I was really annoyed that we're wasting time. I mean, everybody did the same work. And, like, the problem is that everybody did the same work. We wasted time on this. And it was even worse that every stub that we wrote in each of those teams was outdated the moment it was written because the monolith changes over time. So that was one of the problems, like, the second thing that we wanted to fix.

6:06

And the third one, like, that is related to this approach, like, typically the consumer-driven contract approach is the creation of the API. Because what often happens is that you take a look at the API of a service that you have to integrate with, and you say, it's useless. I mean, how can you even create such an API, right? You as a consumer, even the name suggests that the consumer will consume, right? So he will call the API. So his voice should be heard. So what consumer-driven contracts gives you is the approach to create better API that suits the needs of the consumer, because it's the consumer that will make the calls. Of course, it's not like the consumer can expect everything and the producer should do it. There has to be some sort of a collaboration between the two. But the result of this should be a nice API that is, let's say, helping both sides to deliver business features faster. That makes, you know, there's two things. One, when I was reading the Spring Cloud documentation of this stuff the other day, yesterday, I guess, I was thinking, I wonder how much of, like, our understanding of how to do software in, like, let's say the past 15 years has been driven by making testing easier, right? Like, the whole idea of, like, not the whole idea, but one of the problems, as you described, that you have with integration testing. Well, first of all, like, you got to have integration testing nowadays, which is more so than testing some individual piece of code. You've got to test when you put the whole thing together, the whole application together and the dependent services, making sure it works well. And there's, I was rereading a good, not defense, but reason for this by the, what's the guy's name? Gary Gruver, the guy who wrote one of my favorite, I don't know, digital transformation, if that's a phrase, books, like, leading the transformation.

8:14

And he was basically pointing out, like, unless you're, like, integrating your code work, like, what are you even doing? You can't move fast enough, basically, as far as getting the software out. But anyhow, it's, as you point out, it's really difficult to, like, integrate stuff when stuff is moving around. And so you start thinking about, well, how could I write my code so it was more easy to test? And, you know, obviously that doesn't drive everything architecturally, but it's got to be, like, at least 15 or 20% of, like, how people design their code nowadays and how they write it so that it can be easily tested. Yeah, precisely. I mean, what's the idea of TDD? It's to make mistakes. So you sit down, you write a test, thing doesn't work. So then you start writing the code. You tweak around, right? You change it until you're satisfied. Like, red, green, refactor, that's the very idea. So you can do the same as a layer of architecture. So why not play around with the API until you're satisfied as a consumer? And that's what you can achieve with tools like Spring Cloud Contract. And then so the second thing I wanted to ask before you go down a rat hole of wonderful explanation is, so, like, what would you, what are things you'd contrast this approach with, right? Like, if you were to put a knot in front of, you know, consumer-driven contracts, like, what are the alternatives that someone would do? The alternatives to the approach, right? Yeah, yeah. So, I mean, what typically happens at companies is that, like, the producers of the API are just saying how things should be done. So this is, like, the typical world. So you have teams, they have their sprints, they have their project managers, owners, whatever. So, you know, there is a feature to be written. And one of the teams says, hey, but the other team has to change their API. So, you know, things start to go crazy that the product owner of one team has to talk to the product owner of the other.

10:20

Can you please put that into the sprint because you're missing a feature? But they say they have more important things to be done. So the change will take place in 15 months and then you run out of business. So it can't go like that, right? So this is also, this consumer-driven contract approach is also an approach to do this sort of a faster and better, let's say, having a better feedback cycle or even faster feature delivery. Of course, there are some gigantic companies, like, I don't know, Facebook or Twitter, that will not, like, ask every single consumer if they're happy with their API, right? You can do this until a certain amount of people that, you know, a certain amount of consumers that you can talk to them and, you know, you can, they can drive the change of the API, of the API of the producer. If you have, if you have many consumers, it's impossible because you would do nothing else but talk and, you know, and adjust the API. But to some extent, that's possible. On the other hand, what's, when you have that many consumers, what you can do at least is, you know, publish some version of your API in terms of the stubs. So that services that integrate with you can, can profit from that because they have a single source of truth. So, so maybe, maybe one way of putting like, uh, the alternative way, the, the other way that people go about this is, you know, you've, you've got some application and it has a bunch of services, uh, different components, if you will, different services that, uh, different teams make. And in these teams, they'll specify the, the API or how the service is used. And in a more traditional approach, uh, basically that team has probably not, not to make it too much of a straw, man, they don't have complete control over it. Like they need to like talk with other people and see if it works out. But the issue is that if you want to modify that API, which is to say, either take something away or add something new, you essentially, uh, have to, you're at risk of breaking the consumers, the people using that API.

12:33

And traditionally what you would do is like, as you kind of outlined two things or, or would occur one, uh, you just tell everyone what the new API is and they have to go and update their code and go through that cycle. Or two, you just deploy it and stuff breaks. And then each, each team who has something that breaks has to stop what they're doing and fix, fix the thing that's broken. That's true. That's true. So, uh, the idea is that you get immediate feedback. Um, so it's better to get it before it reaches production. Uh, yeah, that's the, yeah. Um, another thing that comes to my mind are things like, you know, protobuf or Afro, where basically this problem disappears to some extent, because you have a schema, right? So, so that way, you know exactly how the, uh, it's API looks like of the, of the message that you're sending. Uh, and here we're talking about, uh, I guess we'll, we'll, we'll be talking about, uh, an approach that can be, uh, used in terms of, uh, rest calls or messaging. Yeah. And, and, and, and so, so in both of those cases, you're basically talking about slowing down the process, right? I mean, well, let me rephrase that. If you want to prevent errors from happening, right? Like, like you have to slow down the process that everyone's following, like delaying, you know, release schedules and all of this stuff, which, which is annoying, which, uh, no one likes schedule elongation. But, you know, but to get incredibly abstract so that we can then get specific, like this, this is an interesting example of how, uh, uh, uh, the problem with computers, which is to say they can only do exactly what you tell them to. Like, like, like in, in, uh, you know, in any, any, any human language, you have got lots of synonyms and intonations and meanings. So you could say something like three different ways and basically say the same thing. And you could even come up with a brand new way of saying something that no one had heard before. And they'll like, kind of understand it. But like, when it comes to like programming and computers, like you can't do that.

14:35

It's like, I know this one way to talk to an API. And if you change that, even by like one character, it's going to screw everything up. And so I have a good story here, but you're completely right. So in one of the companies that was working there, you know, there is this, uh, Boy Scout rule thing, right? So you go to the code, you see, like you do some stuff, a feature, but you see a small thing that could be fixed. So you should fix it like a Boy Scout. So the problem was that one of the developers saw a typo in the API. So he fixed it, but he broke all of the collaboration with consumers because they had the typo in their, on their side. Yeah. So yeah, that's precisely, uh, what we don't want to have, or we want to break as fast as possible. So for example, during the building of the app, so at the very beginning of the cycle. That's right. It's like, it's like who machine learns the machine learners. That's, that's the problem. So, so anyhow, so, so then, so we, we've got this potential problem of like, if you want to change your, your, uh, your, your APIs or your service definitions or whatever, you're going to slow things down and break things. And that's, that's where our hero enters the picture, right? It's like, so what do we do to solve that problem? And so like, like you sent over, like, uh, it was way back from 2006 back in the WS star days and everything. I, you know, there was a pretty good, uh, description of, of, um, it's a lot easier to understand if, if you have sort of like, uh, I don't know, a soap sort of background. Like when I was reading through this thing, like the, the, the, the thing that made the most sense was what was it called? Like the principle of ignoring stuff, which was to say, if you're interfacing with an API and there's a bunch of stuff as a program, you don't understand, just ignore it, which that doesn't completely solve the problem, but it kind of gets to the theory of like, and therefore the producer of the API can muck around and, and they can at least add things.

16:38

It's still introduces a problem of changing existing stuff, but it's, it's like halfway to this idea of being able to modify stuff. Precisely. So, uh, what we're talking about are not schemas. Absolutely not. So maybe before we go on, let's define some common terms that everybody's on the same page. So to begin with, we have the communication of consumer and producer, right? So the producer is a service that exposes some API, right? Let's focus on HTTP here. So you have a controller, you have an app with a controller, that's a producer. So the consumer is the service that consumes that API of the producer. So if you're sending a call to somebody who has a controller, then you're the consumer of his API. Now, a contract is an agreement between the producer and consumer on how this API should look like, right? So that it's some sort of a written agreement. And I'm not talking about like word or PowerPoint presentations where we agree on the API has to be something, uh, that can be a base for our tests and stops. And the consumer-driven contracts approach is an approach where the consumer drives the changes of the API of the producer, right? So we have those four terms. Now, you mentioned the schema. So contracts are not schema. And Spring Cloud contract, a contract is a Groovy file, Groovy DSL, uh, where you can define that for a particular request, for a particular response should be sent back. So it's not defining that this is the body of the request that you might send. And this is the response that you might receive with all the possibilities. It's not the case. So if, uh, in the contract you say that, you know, in the body you will send a field full, but in fact that body can have 500 different fields, I mean, you don't care.

18:41

You'd care only about that particular field. So it's not a schema. It's sort of a, like a scenario, so to say. Uh, let's talk about an example. Yesterday I was giving a talk where the example was buying a beer. Uh, since, uh, like we talked, uh, uh, the last time I, I told you, I told everybody actually that the, uh, we have this application called the brewery to test our Spring Cloud stuff. So of course the sample applications for Spring Cloud contract had to be brew, uh, beer related. So let's say that we have a case where somebody asks us if they can buy a beer, right? So we don't have this knowledge to say if the age of the person is okay. So we have to ask a fraud service, so to say, or like, uh, age check service. Is this person eligible for, for buying the beer? So I would assume having two contracts. One contract would be if a request comes in with a person having an age, I don't know, greater than 20, then the response will be, uh, you can't get the beer. So another contract would be if the age is greater than 20, oh yeah, you can get the beer. So these are two possibilities. You can get the beer or not, regardless of how many, let's say, fraud cases you would have. So if the person could be, let's say, declined of having the beer for 100 different reasons, I would assume most likely that there would, there would be one contract. Why, why am I emphasizing that? Because contracts are not supposed to be a duplication of your, of your implementation, right? Because that way you would have to write the feature twice. Once in the production code, right? And once in the contract. So contract is some sort of an agreement of a subset of, of, uh, features that you have.

20:42

Um, and why do we need it? Because we need something that both sides can agree upon. So if we're writing a new feature, uh, most likely both the consumer and the producer have to write something. So often what happens is that the producer, let's say, starts a sprint earlier than the consumer, they've done, done something, then the consumers are not happy with the API. So they have to change, uh, like the producer has to change some stuff again, then the consumer starts their, their work and, and it's becoming hectic and it takes a lot of time. So the first thing that should be done is actually agree that a, most likely, uh, this is the contract that the consumers would like to have. And, and you start talking. So the, the contract becomes a written agreement between both sides and what can sprinkle a contract do with that on the producer side, it can do quite a few things. Uh, before we go into details, I told you about those two biggest problems. So stubs, uh, reusability and validity, right? So how can we ensure that, um, the stubs that the consumers will use are still okay. And here comes spring cloud contract. So when the producer writes, uh, like he gets the, the contracts, like those, those, those, those groovy files, what happens is that the plugins that we have either Maven or Gradle converts those contracts into tests, what does it mean? So if in the contract you say, if you send the request at endpoint full, there's going to be a bar response. We're generating a test that actually sends a request and expects the bar as a response. So if you don't have such an endpoint, if you're lying in the contract, you will not be able to build yourself.

22:45

So that's the, the key thing, uh, that sprinkler contract gives you that the contract is valid only if you, your tests pass, if you, if those, uh, auto-generated tests pass. Right. Uh, and then who, who, uh, where do those tests come from? They are auto-generated. Ah, right, right, right, right. Yeah. So if you write a, um, a couple of contracts using the DSL that we provide, uh, it's statically typed. So all the groovy haters saying that groovy is dynamic, uh, don't worry, everything is statically typed. So your ID will help you with, with the DSL. So what happens is that for every single test, a single contract, a test will be generated on the producer side. That way we can ensure that you, uh, that the contract tells the truth. Because the second thing that happens that the, the, the, the, the plugins that we wrote, uh, do is they convert the contracts into stubs. So that way only after checking whether the producer says the truth in the contract will you, uh, be able to create the, uh, stops that you can publish to Artifactory or Nexus. So that way we are solving both the reusability and validity problem, right? So like to recap, contracts give you a written agreement, agreement between the consumer and the producer side, but it's only valid if it's telling the truth, right? So in order to verify if it's telling the truth, we are creating tests from the contracts. They are auto-generated and once they pass and all the build pass, we help the developers by creating a jar with the stubs inside that you can publish somewhere where other teams can reuse it. So they don't have to write like 10 times the same thing. Right.

24:45

I hope I managed to explain it somehow. And then so, so to, uh, recap and re-explain on, on, uh, on my side, the consumer of what you're producing, so to speak, uh, basically the consumer and the producer get together and, and they, uh, as happens in, in real life, hence the contract metaphor is good. And they basically agree on a contract, right? They're like, here is, here is the way we're going to talk with each other as consumer and producer. And there's like, like, like in your, uh, in your beer buying thing. I mean, maybe it's actually different than this, so, so you should correct it if so, but it's sort of like there is, uh, there's someone, something that has beer and something that wants beer, a producer of beer and a consumer of it. And the consumer comes in, and, and, and one of the parts in the contract is requesting a beer. So there's like, I would like a beer. So somehow you put in the contract how to do that. And then, uh, one would hope there's also a part in the contract that allows the producer to give the beer consumer a beer. So there's sending a beer, so to speak. But then as you added, there's another step that's basically like, uh, verification that, that it's legal to, uh, to, to give you a beer under whatever geography you're in. And so there's, I mean, to grossly simplify it, there's basically three things that happen between a consumer and a producer. And so the contract you would establish is those three things. And, and then like, how does that, what is that represented in? Is that like, what, if you were to open that up in a text editor, what is it? It's a groovy file. So basically, and it looks, uh, in a very simple way because you have, uh, uh, like I said, it's statically typed. So the ID helps you. And you say like, there is a request part when you say for that method, for that URL, for those headers and such a body, I would like such a response that has such a status, such headers. And such a body, right? So it's, it's, it's, uh, it's not a schema, as I, as I said, it's some sort of a scenario.

26:47

So it gives you much more because from the consumer side, in terms of stops, you're not interested in schemas. You're interested in the concrete scenario, right? I'm sending the request. I want a beer, but let's assume that I'm too old. So I should get a response that, Hey, you're told you're not going to get it. Right. So as a consumer, what, what is of your interest is a, is a scenario. So those, uh, contracts represents scenarios basically, but there's a nice thing that you mentioned that you said that, uh, and I agree with that, that the consumer sits with the producer and they agree on the contract. But the problem is that it sounds really waterfall, right? Like we sit down and we assume that this is the contract that we will, uh, we will want, which kind of is not the best way to go. Uh, so here comes the consumer driven contracts. What does it mean? It means that the consumer can sit down, write some codes on his side and let's say, play around with the contract of the producer offline. What does it mean? Let's say that the producer hasn't written a single line of code in terms of verifying if the person can get the beer, right? But let's say nothing is there. The consumer can still play around with the contracts offline, completely not even talking to the producer. And he, the, the, the consumer team can generate stubs locally. So you no longer guess what kind of an API you will need. You can actually do it because you will locally create the stubs. So imagine this, it's like doing TDD. So you write a code and you have a service that you will call. So what, what are you doing? You're creating an interface or you're stubbing an interface. And you say, I don't like what this interface does.

28:47

So you change its behavior and then you change it again. You change it again until you're happy. And then, for example, you say to a friend that was working you in team, you know, I have a prototype of a, of an interface. Can you please like implement those, this interface? And he does it, right? So you can do the same with the API and the contracts. So you sit down, you play with the API. And then you say, for example, okay, this is the API that would be awesome. Let's go and talk to the producer team if they accept it or not, et cetera. You can do it, of course, iteratively. You don't have to like do it at once. But the best thing about this approach is that even though the other side hasn't written a single line of production code, you can still simulate that they have done it because you can work offline. So this is the like the consumer driven thing that the consumer is driving the change and you don't block. Like everything right now is reactive. So this, this process is also reactive, so to say, because the consumer can work offline. He can work on the contract. Then both sides have to block like on the agreeing on the contract and then both sides can work in parallel because they agreed on the contract. So the consumer can like finish up its work if there's still some like implementation to be done and the producer implements the missing feature. Yeah, no, and I think, I think, I think you're, you're hitting on it. It must be sent. Well, we're, we're discussing the snappy phrase for it, but it's almost like a, an inversion of control of how interfaces normally work, right? Like precisely that's a very good way to present it. The, the, the producers usually have all of the control over what it looks like. And, and, you know, I mean, this is this to exaggerate the point, to make the point, like when something breaks, it's not their fault.

30:49

Whereas, whereas like in a consumer driven approach, it's the, the consumers are, are, I mean, tell me this is true, but the consumers are defining what the interface looks like. And, and in their, in their defining it, like maybe it's not totally the producer's fault. Like, and, and, and someone can always maliciously go in and be dumb, right? And do something, do something silly, but it's, it's, it's sort of like the, the relationship that's, that's put up here is it says consumers will come to you and tell you how they want to consume your service. And so it's, it's, it's, as a producer, it's your job to make sure that you can talk to them in the way they want to, instead of, instead of, uh, basically demanding the way that, uh, that, that consumers interface with your APIs and stuff. Precisely. Going further, in Spring Cloud Contract, an important problem is ownership of contracts. Who should own it? Where should it be placed? I'm in favor of actually the producer having the contract inside its repo. But there is another approach, because sometimes for security reasons, for example, the producer's code is hidden. You cannot even see it. You cannot access it. So there is another approach where all the contracts can lay in a single repo. Why would you even want that? For a very particular and nice reason. We are writing about this in the documentation, but if you, let's say, create this repo in a proper structure, in terms of folders and stuff like that, as a producer, you will know, for example, which consumers you broke with a change that you did locally. Why is it so? Because all of the consumers, like, to put it briefly, there is a root folder for a producer. Like, let's say, let's call him Foo, right?

32:51

The Foo producer. So under the Foo folder, all the consumers will put their contracts, like, this is the expectations that we want to have. So, when the producer will generate the tests from those contracts, the producer will know exactly which contract from which consumer it was. So, in that case, breaking changes are close to impossible, right? Because they would have to be introduced by the consumers. The consumer would have to say, I explicitly want you to break the contract, right? But if, like, in that case, most often what companies do when they have the contracts in a single repo is that there is a reviewing process. You cannot just merge the PR. There has to be some, you know, plus ones or okays, something like that. So somebody would have to check and say, hey, you're breaking the backwards compatibility. But the great thing about this approach is that the producer will know exactly which consumer was broken. Of course, we're talking about a very abstract thing. So, I advise everybody to check out the Sprinklr contract documentation where we provide an example and stuff like that. But I hope that I've managed to explain that a little bit at least. But, you know, what you're hitting on there is like the, it sounds simple, is like the testing aspect of all of this, right? So, like, at the beginning, like, when you and I are talking, one of the main problems is testing large systems with lots of services in them is difficult. It's tedious and takes a long time for all sorts of reasons, right? And so one of the things to make testing easier, if you're using a whole bunch of different services, is to basically stub them out. And whether you want to call them, like, mock things or whatever, but essentially, to use yet another word, you're simulating all these other services. So if you have some application that uses, like, 50 different services, like your brewery thing, you know, there's another part of your brewery resource ERP application that I guess is, like, brewing the beer and trying to keep track of, like, this one's been brewing for five days, this one for eight.

35:03

And then you would have another component that's like, we've committed to sell 50 kegs to this company, so we've got to make sure to allocate that and not sell 50 kegs to this other company. So you've got all these different services. And if you wanted to change, like, aspects of those, you would almost have to, like, deploy them to production to make sure everything integrated. Yeah, that's precise. That would be, you know, insanity, more or less. So instead, you have, like, simulations that run and stuff like that, you know, whatever you want to call them, ways of testing it to integrate it. So I've almost forgotten what I was saying here, but hopefully I'll remember. Most likely about feedback cycle. That's right. And then so the issue, one of the things that you're solving here in an interesting way is, like, so if I'm depending on those services and changes that are made to the services, testing them is annoying and it's hard to test them in the first place. So when you use the spring contracts, the first thing it does is generate a bunch of stubs for you. So you can, it may not test the, I don't know what you kids call it anymore nowadays, but it may not actually test the behavior of the service thoroughly, but it at least tests the semantics, I guess. It tests that, like, the language is English, so to speak. Exactly. And which is a huge thing to test. So you've got that on the consumer side and what's kind of intriguing about this approach, as you were just suggesting, is that so maybe what we should do is if on the producer side, they should proactively know when they're about to break some user of their code. So the users of their service will basically send them or it'll be auto-generated, but they will have a test suite, more or less, that simulates what all of the users of the service will be doing. So when the producer of the API makes changes, they can test if they're going to break things before it, I don't know, is checked in, so to speak, or before it reaches all of the various consumers of things, which...

37:07

Yeah, so you're right. Yeah, so you're right. And actually, with Spring Cloud Contract, depending on where you have the, who is owning the contract, you'll have it in two different ways. If the producer is owning the contract, and the producer, for example, is doing the backwards incompatible change, the next time any consumer will be built, for example, you can put on your CI, like, cron for, I don't know, every 15 minutes or something like that. The build will break, because Spring Cloud Contract, on the consumer side, will download the latest stubs that are, right now, backwards incompatible, and your integration tests will reuse those backwards incompatible stubs, and they will blow up. Right? And if the ownership is in the common repo, actually, if you, let's say, merge the request for a backwards incompatible change of the API, the producer's tests will be broken. Because... Why is it so? Let's say that you have changed a URL from full to bar, right? It's a backwards incompatible change, because you deleted the full and you added a bar. And suddenly, auto-generated tests are run, and they are shooting at the bar endpoint, which doesn't exist. So, the producer code will blow up. As simple as that. Yeah. Now, that makes sense. And it's another good example of, like, how the onus for compatibility is placed on the producer. Yeah. I mean, it makes sense, right? Because these are producer stubs. So, we have to check if the contract is telling the truth against that producer. Hmm. Right, right, right. And so, maybe to get close to wrapping up a little bit. So, how, like, all of the... We mentioned Groovy, which is an actual technology that you can, you know, use a keyboard to do something with. But most everything we've talked about is sort of, like, theory and hand waviness.

39:08

So, like, what does this end up looking like in Spring Cloud? Like, how does... What do the tools actually look like? Okay. So, that's a very good question. So, on the producer side, you create the... Not necessarily on the producer side, but let's skip it. Let's say, let's stay with this example. You create the Groovy based contracts. You're using the Spring Cloud contracts, like, jar. Like, the contents of the jar, right? To do it. You add a Maven or Gradle plugin that allows you to change the contract into the tests or change the contracts into a stub. So, that's on the producer side. On the consumer side, you are using Spring Cloud contract stub runner, which has quite a good name because it's running stubs. So, basically, when you add it, in your integration tests, you can provide an annotation, auto-configure stub runner, where you provide the group ID, artifact ID, and a bunch of other stuff of stubs that you want to be downloaded. So, automatically, when you run your tests, we are checking the nexus or artifactory that you have provided the URL to. We are searching for the stubs that you have provided, like, in the properties of the test. We're downloading those stubs. We're starting fake HTTP servers, and we're feeding those servers with stubs. So, basically, you don't have to do anything to start in-memory HTTP servers in your tests as a consumer. Another thing that we are doing is pretty much the same thing, but for messaging, because we were focused on HTTP. So, Spring Cloud contract gives you the very same approach with messaging. So, you create a contract in which you define a message. For example, if something happens, like, I don't know, if a scheduler is started, a message will be sent.

41:16

So, on the producer side, if you put that in the contract, we will execute that scheduler that you mentioned, and we will check if, on that channel, a message was sent. So, if you're not doing that, we will find it out, and your build will break. And on the consumer side, you can say, I will not go into details how, but it's just an interface, so it's really easy to trigger. You can trigger the message, because with HTTP, it's simple, because you're sending a request, and you get the response, right? It's synchronous. But with messaging, you know, suddenly, you get the message. So, we give the tools to trigger that message, and what is awesome is that that message has been tested against the producer. So, the producer is not lying. So, basically, these are the two tools that we support. Oh, and another thing is that on the consumer side, we also support, we integrate with Spring Cloud, basically, which makes sense, because it's Spring Cloud contract, and we can stab out completely the service discovery. So, if you're using, for example, console, and you're using Spring Cloud contract, in your integration test, we don't care that it's console, because we're going to stab it out. It's going to be done completely in memory. So, we're going to start an HTTP server in memory, and when you're going to request for that using, for example, Fain, or, you know, some sort of discovery client, we will route it, the call, to that fake HTTP server that we have started. So, yeah, it's really fast, and you can completely stub out the discovery service. Yeah. That makes sense. So, like, how long does this kind of stuff take? Like, what are the units of work? Like, obviously, they're sort of like defining a contract, right? That's probably... Yeah, so I can actually go through the whole flow. Yeah, yeah, yeah. Yeah.

43:16

So, it's consumer-driven contracts. So, the consumer starts. So, the consumer, by any means, regardless of whether the contracts are in a single repo or the producer side, the consumer starts with a TDD. He writes a test for the feature that he wants to do, and he plays around with the contract that is there locally, because he can work offline, play around with the contract until it suits the needs of the consumer, right? Once the consumer is done, like, the communication with the producer takes part, takes place that, you know, this is the contract that we want to have. So, this is the first part. So, the consumer plays around with the API until it's satisfied. Then, the producer can take the PR with the contract and implement the missing feature. Once it's done, it's merged into, like, this branch is merged, this PR is merged into master, which means that the auto-generated tests passed and stubs of the producer are placed into Nexus or, like, Artifactory. And the last bit is that the consumer can switch off the flag of working offline to working online, which means that the stubs will be downloaded from this Nexus or Artifactory. That's it. So, do people, like, fill out the stubs to mess with them? Or is it just, like, you never touch those because they're auto-generated and you're just going to create a bunch of problems? You never touch the stubs. You touch the contract. Ah, right, right, right. Because the stubs, as an implementation of the HTTP server stub, we're using WireMock. WireMock, in terms of stubs, uses JSON. If you ever need to do a JSON in a JSON, you want to, it's misery and pain. You want to, like, cry. Right. Try to escape all the signs properly. It's really tedious. So, we're doing that for the user. The user can use, you know, Groovy's map notation to create a nice JSON or a G string.

45:21

So, no escaping of, you know, quotes or stuff like that has to take place. So, the consumer can play around with the DSL. There's another approach that Spring Cloud Contract gives you. If you don't like Groovy or DSLs, you can create the stub via the res docs. So, as a producer, you're writing tests using spring res docs. And you also say that, okay, this test passed. And by the way, can you please create a stub from this? So, here, you don't have auto-generated tests because there's no contract. But you can still achieve the same goal of creating a stub. And the rest is the same. So, the stubs will be packaged and you can push them into Nexus or something like that. Right. So, we give various options. So, then, like, where do people get this stuff? Like, how long has it been out? Or, like, give us some, like, project history and stuff. Project history is really interesting because Spring Cloud Contract has a predecessor, which is called Accurist. So, the Accurist project was started, if I remember correctly, two years ago. And it had two GA's. So, it has already been battle proven. And Spring Cloud Contract has been released. Let me check it because I don't really remember. Two weeks ago. So, the GA of the Spring Cloud Contract is two weeks. But it's based on the foundations of a project that had its GA. I can actually even check that. But some time ago. And it was battle proven, as I mentioned. And the release was June the 2nd. It was the second release. And the first one was... Yeah, let me find it. The first one was on the February 29th.

47:22

Right, right. Well, that sounds... So, like... Oh, what was I gonna ask? Oh, I know. So, aside from Spring Cloud, are there other, like, frameworks or implementations out here? Like, what are other frameworks people use for this kind of stuff? Yes. Another framework there is is called PACT. It's also a solution. The difference is... The main difference is that PACT supports multiple languages. So, not only JVM stuff. It has a different approach toward... It also has something called, like, a broker. Where, let's say, this broker can tell you which service uses which contract and which version. You can check out their documentation to learn more. The main difference is that they don't auto-generate tests. And they don't have this stop-runner feature, so the stops are automatically, let's say, downloaded and started for your tests. But, yeah, they also favor this consumer-driven approach. And, yeah, that's basically another tool that I know. Yeah. Are there others? Maybe they are. Well, is there anything else you want to wangle in here before we wrap up? Or do you think we covered it sufficiently? So, like, hopefully we've managed to cover why would you like to consider doing consumer-driven contracts. I hope that we have emphasized what the problems we're trying to solve. So, basically, stop validity and reusability and how you can create a much nicer API using this approach. I hope that we've shown how you can do that with Spring Cloud contracts. It's difficult to talk about code instead of showing it.

49:25

So, I encourage everybody to check out the Spring Cloud contract documentation. Also on my blog, TooMuchCoding.com, I think I've written quite a few things on this. So, yeah, hopefully we've managed to show light on a topic that is really old, so to say, but often very neglected. So, people redo their work and they have problems on some staging or test environments when they're doing end-to-end testing. Whereas they can have the feedback cycle, like, pushed back. So, they can already, during the build phase, see that their code is not working, which is the very idea of testing, right? Yeah. And I think to, like, monkey climb it up to, like, larger topics, right? Like, we've gone this long. I don't think you would ever use a word like this, but it's sort of like a way of doing governance in your application. Like, and it's, again, it's kind of a reversal of how people usually think of governance. It's almost, you know, not quite, but it's decentralizing it and moving a lot of the chance for governance. And governance is sort of useless unless there's a way of enforcing when something bad has happened, right? So, it brings a lot of the enforcement of governance down to the consumer level. That's the decentralizing part. And I think, you know, whether or not, like, you're all, like, microservices happy or things like that, you're going to have services in your applications. And it's good to not just, as they say, cowboy it, but you want to have a fair amount of governance in the service for all the reasons that we went over. And also, because, you know, the enterprise architects have to have something to do, right? Exactly. And actually, speaking of it, I wanted to talk about this. Often they want to know what's there. So, actually, using Spring Cloud Contract, what we can easily do is to create ASCII documentation of the contracts that your apps has.

51:29

And there's a new issue for that already in Spring Cloud Contract. I created a spike. So, it's actually trivial to achieve. So, we can present in a nice format for all those business people, like, information about the API in a scenario way. Because this is most likely what they are interested in. How does the service work? And since the contracts represent scenarios, you can easily convert that into a nice documentation. Oh, yeah. That's a good point. You can do, it's a side effect, is you can basically do an inventory of all the stuff you have and who's using it. Which is always a delightfully impossible problem to completely solve. But at least you can have some good swipes at it. Exactly. And especially, I mean, it becomes even, it becomes near impossible to solve if things are moving really quickly. But, yeah, that's interesting. It gives you a better handle on doing that. Well, great. Well, you know, you've already mentioned it. Why don't you tell people where they can go to catch up with you more and follow more stuff that you're doing? Yeah. So, basically, you can go to my blog, TooMuchCoding.com, where you will be able to find my Twitter handle, which is very difficult to spell. So, you can check out the Spring Cloud Contract documentation on cloud.spring.io. I'm there on Gitter. So, you can check out the Spring Cloud Contract Gitter channel. I try to be really fast at solving issues. What else? I guess that's it. I mean, you can check some of the presentations of mine. I haven't yet put in the documentation the video about Spring Cloud Contract. But once there is one out there, I'm for sure going to put there. So, hopefully, it's going to also be more, like, give more explanations than the documentation as such.

53:29

But we try to have the documentation be really practical. So, there are some use cases, like, step-by-step approach to consumer-driven contracts. So, hopefully, the documentation will be good enough for starters. Indeed. Well, great. Well, thanks for, you know, you actually suggested this topic. I think it was nice. So, thanks for suggesting that. And for the audience, thanks for listening into this. It's always good to have you. And this, as always, has been Pivotal Conversations. You can find our stuff over at pivotal.io/podcast or in our not-so-secret backend at soundcloud.com/pivotalconversations. Also, if you're feeling kind of sprightly or are insanely bored and want something to do, you should go over to iTunes and leave us a star rating or even write a review. That would be delightful. You know what you should do when you write a review is you should go write a review and, first of all, talk about how awesome this podcast is and how everyone should listen to it. But at the end, you should put on sort of tips for what enterprise architects should do with their time in sort of a cloud-native world. Or if you don't like that topic, just angle in a few lines on sort of either something that an enterprise architect has done that's awesome recently or a total boneheaded thing that an EA has done. And, you know, I find that it's good to have a little mission for a review write-up. Otherwise, you just talk about how awesome it is. But that's something that you can do as homework. And with that, we'll see everyone next time. Bye-bye. Bye. Bye.

Tanzu Talk · 🍕 Get more pizza.