Contract-Driven Development – Turn your API Specification into Executable Contracts

Presenter: Naresh Jain
Event: YOW! Conferences
Location: Sydney, Melbourne & Brisbane, Australia

Presentation summary

Being able to develop, test and easily deploy each of your microservice independently is a key indicator of a successful transition to a microservice style architecture. The challenge arises when a new microservice that interact with your existing system has to undergo comprehensive integration testing before deployment. This slows down the path to production and highlights problems at a much later point in the microservice’s lifecycle. Contract Driven Development helps you to shift left and identify compatibility issues earlier in the development life cycle when they are easier to catch and fix.

 

Naresh Jain shares his extensive experience of helping large organizations navigate the challenges of microservices architecture. He the benefits of OpenAPI and AsyncAPI specifications in ensuring seamless integration and deployment of microservices. Through a live demonstration, Naresh showcases how Specmatic, an open-source tool, can be leveraged to validate API specifications, generate tests automatically, and intelligently stub out downstream services.

 

The session offers a comprehensive overview of how Specmatic can streamline the development process, ensure API contract adherence, and facilitate efficient testing practices. Watch this video to gain valuable insights into optimizing your microservices journey and enhancing your time-to-market capabilities.

Share

Transcript

Introduction to the microservices journey

I’m guessing this session is interesting for you for maybe a couple of reasons. One is you have embarked on the microservices journey. Or the other is you have OpenAPI specifications and you’re trying to figure out what to do with them. Yeah? So this session is essentially my experience of the last four years trying to help a couple of really large organisations who have embarked on the microservices journey. They have hundreds of microservices, maybe in some cases, thousands of microservices, and all the microservices – if they are using HTTP – have an OpenAPI specification, and if they are using asynchronous like Kafka or JMS or something like that, then they have AsyncAPI specifications. And often those things go out of sync. They’re not up to date. And the whole reason for people to embark onto the microservices journey – at least for a lot of clients I’ve worked with – is they wanted to reduce their time to market so they can get to market as quickly as possible. And why is that the case? Because the idea with microservices is that I can have really small teams building these microservices and deploying them to production independently.

 

Correct? How many of you have been able to realise that in your organisations? Maybe. And one big challenge in my experience, and correct me if I’m wrong, is that while we have teams being able to independently build these microservices, if you deployed to production, you might have realised that there’s some integration issue or things like that. And so even though some microservices are ready, they want to make sure all microservices are ready. They integrate them, test the whole thing, and then they want to deploy them to production. At least that was one of the main challenges that we were seeing in organisations. And so this talk is a solution, not the only solution, but a solution of how we can address that. We built a little open source tool called Specmatic, so I’m going to demonstrate that. For today’s demo, I’m going to take an app which basically makes a request to a BFF layer, (a back-end for front-end layer), which in turn then calls a domain service. You could have one or more domain services, right? And the domain service gets you back a response. Now, at this point, you might want to log this message onto a Kafka topic so that your analytics services can pick it up and do some analytics on top of it, and finally respond back to the application.

 

I’m going to use this as an example for today, and surely no organisation has this simplistic, basic microservices architecture. Probably it’s a lot more complicated. I’m going to show you a little later about another tool Insights, which is there in Specmatic, which helps you visualise all your microservices, the dependencies between them and stuff like that.

 

Now, generally, what you would do is you would create an OpenAPI specification for your BFF layer so that your app can independently start developing things. Similarly, your your BFF layer can independently develop things and deploy. Same way you would have a BFF layer and between the domain service, a OpenAPI specification. And for things like Kafka, JMS, and asynchronous, you would have something like AsyncAPI specification.

 

How many people are familiar with OpenAPI specifications? That’s quite a lot.

 

How about AsyncAPI? A few. It’s not as popular, but it is the very same idea for async stuff.

 

Live demo setup and overview

So with that, I’m going to jump into a live demo. Most of this is going to be a live demo, and I would like you to guide me what to do next.

 

Rolling up the sleeves a little bit as we get in. So right here, I have a My order API, which is the domain service. So I’m just going to start that so you know I’m not faking this. So I’ve just got the order service kicked off. While this brings up, let’s also bring up Kafka. So I’m going to start Kafka here. And I’m going to start my BFF layer. So I’ve triggered all of these. It’s a little spring boot app. It’s going to run on port 8080.

 

Kafka is already up – it’s listening for topics on product queries. This guy is also up and running on port 8090.

 

So let’s just quickly make a curl request and see if everything is okay. Sure. I have one product in my database, and it’s returned that product. So at this stage, everything’s wired up. Remkember, we have the app which is making a request to the BFF layer. So this curl is essentially the app which made a request to the BFF layer, which in turn went to the domain service which is running here.

 

My domain service – the order API – returned the response back, and then it put a message onto Kafka. So you can see here Kafka got a message. So all of this is wired up, which is great.

 

I have an OpenAPI specification – let’s take a look at it. What you can see from here is essentially it has three parts, a find_available products, which essentially takes a query parameter called type, and it is not required – it’s optional. It also takes a page size, which is in the header, and that is of type integer, and it’s mandatory. It gives you back three responses, 200 to 400 and 503.

 

We have something called orders. We have something called product. On product, I have a POST request that I can make, as you can see here. And it has three mandatory fields, name, type, and inventory. And you’d notice the type is an enum which has four possible values.

 

That’s the job of an OpenAPI specification. Most of you will be familiar with it. Now, I want to verify if this OpenAPI specification and the BFF (the service that I have) are in sync or not?

 

How would you verify that? How would you verify if your OpenAPI specification is in sync with your actual implementation?

 

Static code analysis? Static code analysis is one way you could do that. You could have API tests that could verify. Someone could look at the OpenAPI specification, write API tests, and make sure that works. I’m going to show you a third technique today. It’s a plugin. And what it does is it brings up a little UI here. What it basically is pointing is to where my OpenAPI specification is, the API specification that we were just looking at, and where is your application running? Localhost 8080. With that, let’s run the contract test and see what happens. Cool. It’s run seven tests. Five of them have succeeded, two of them have failed. Zero lines of code at this point.

 

Automated Contract Testing with Specmatic

Just take the OpenAPI specification, use Specmatic, and it generated the test for you. What did it generate? How did it figure out what test to generate? Let’s scroll to the beginning and we can see the command I ran.

 

So it basically said, “Hey, I figured out that /products is a route that you have endpoint, and it requires three mandatory parameters”. So I’ve basically plunked in some random values for these. Of course, wherever type was a enum, so I’ve picked those values. So it took gadget, and it generated a request, and it got an ID back 0. And it says, Okay, so this particular thing was successful. And then it took and it changed it to book. This one, it generated Gadget Book. And so it just went through the four types. We have food, and the fourth one is other. So it generated four combinations, just iterated through all of them and made sure all of them succeed. Then /findAvailableProducts is another API. So it basically made a request and it got these two products back and it says, okay, this also seems to be working fine. So hence it said succeeded. And it said, what happens if I put some random type value and see? And of course, that ended up in a 400 bad request. And the last one, I think it generated is a test for a /orders post.

 

It created a POST request, and this came back with a 404 not found. What was the reason for a 404 not found? Because this /orders didn’t even exist. What happened here? Let’s quickly look here. So this is a quick summary that Specmatic spits out. It is called the API Coverage Summary, and it says, Hey, I found /findAvailableProducts. It has three routes in it. I was able to cover the first one. The other two, I was not able to cover at this stage. I also found something called /orders, which is missing in the spec.

 

What does this mean? It’s missing in the spec. So what Specmatic does if you’re using Spring Boot or anything like that, they expose something called as an actuator endpoint on which you can go and query saying, “Hey, can you tell me what all routes are available on this service?” So it went, queried, it got /orders as a route that the actuator exposed. It went and looked in the spec and it says, I can’t find it in the spec. And that’s what it’s saying. It’s missing in the spec. But instead, it found something else in the spec which is not implemented.

 

So it’s there in the spec, but not implemented by the application. In this case, it turns out that it’s a simple typo, as you can see, but it could be a serious issue – something that someone’s missed implementing altogether.

 

And it found another one which also it covered. So now, what do we do? We will quickly look at how I can fix this. So I have this specification, and in this, if you see, it says “ordres”, but actually it’s a typo. It should have been “orders”. So if I fix that and I go back and re-run my test and see what happens. It’s going to again generate the same seven tests. And this time, sure enough, there’s nothing missing in spec or not implemented. It’s addressed those, and we have now 33% coverage on each of them. Why 33%? Because there are three responses, we are able to only exercise one of them.

 

All of this with zero lines of code, just an OpenAPI specification, and Specmatic doing all of this magic for you. It’s reading the specification, generating the request, hitting the endpoint, and then getting the response and validating with the specification.

 

Generating examples with GPT-4

Now, I’m going to show you something more interesting. Ready for that? What we will notice is that we have a few tests that are failing, right? And those tests are failing because the specific request that it’s making, that’s not available in our database, right? Specmatic can’t really figure out what is inside your database. So you have to guide Specmatic to say, “Hey, these are the examples or these are the values that are there in my database, so make a request with them”. So let’s go here. I want to enrich this specification with some examples which are what is there in my database. So I’m going to use Generate Examples, again, another part of the Specmatic plugin, which is talking to GPT-4 at this stage. It’s saying, “All right, I’ve looked at the specification. I understand your domain. Can I actually, instead of me sitting here and writing each of these examples by hand, can I just use GPT-4 to generate the examples for me?” Because in this day and age, trying to sit and write these is a bit of lost cause.

 

Of course, GPT-4 is the first time you’re running, it’s trying to fully understand the model and trying to then generate relevant examples for you. Of course, this is a simple example, and I would probably spend next 15 minutes to do that, but why take the pain? And so GPT-4 has come back, and it’s saying, “Hey, I found and generated these examples for you”. It’s giving you a side-by-side, shared comparison. So it’s put this success where essentially, if I am doing a POST on product, then this should be input, and I should get back an ID1, which is an output. It’s generated a bunch of these examples. You’ll also notice that the description and things like that it’s generated is actually fairly relevant. It’s not some garbage that it’s generated. If I use something like faker or things like that you might be familiar with, that will just generate some random values, usually. But here we’re able to leverage this and generate more meaningful values. So now that I have these things, it’s updated here. Let’s make sure the examples are here. So now if I go ahead and run the test, what do you expect to see?

 

Using generative testing

Now it’s executing three tests. Remember earlier it was executing seven tests. Now we have said, “Hey, use only these examples, and generate tests only for those. Don’t go and generate all random examples”. So you’re guiding it, saying, “Generate only these”. And look here, what do we have? Success. Isn’t that cool? Without writing a single line of code.

 

But we’re not happy that we were earlier at seven tests, and now we’re down to three tests. That isn’t cool. So can Specmatic do something for us? I’m going to click Generative Tests. And I’m going to say run tests with generative tests. Let’s try and see what happens. Woo! 41 tests. Just like that. What is it doing? It says, “Hey, I was able to generate 41 tests. Six of them are passing, and 35 of them are failing”. What happened here? So I’ll explain what generative testing is and what it does, but let’s first figure out why these tests failed so that we can fix it and then talk about what happened. As you can see, it’s saying, “Hey, when I made this request, I’m getting response body the message, but the key name message is in the response but was not in the specification”.

 

So what has happened here is it’s generated a whole bunch of combinations which are generating a 400 response. You can see here, it’s generated 27 responses, 400 combinations for us. And when I look at this, this is the request that it’s generated. You can notice for count, it’s tried to put a string instead of a number, and it is expecting to get a 400 response back, which it did get 400 back. However, the response schema did not match with what you were expecting. I’ll show you that in a minute. Let me just scroll down here to my bad request. So you can see I’ve got a time status over here. Sure, I’ve got a timestamp as well. I’ve got a status, I’ve got an error. But here I’ve got a path whereas here I’ve got a message. Looks like, again, the spec is out of whack with with what is actually coming back. So I’m going to just replace that, clear this out, and run the test again. And it’s going to run again the same 41 tests, and I would expect to see all the tests passing at this stage.

 

Fixing test failures and improving coverage

But let’s see if that is actually true. Sure enough, 41 tests, all tests successful. But how did I get 41 tests generated? That’s the interesting piece over here. So let’s go look at some of the tests that this has generated. The initial tests are the standard tests that we were generating, which it’s now calling positive scenarios. Scenarios based on the examples that you had given. But then let’s look at some of the negative scenarios over here. So what has it done? It’s basically taken name and made it null. Name is a mandatory field. It’s made it null, and it’s actually expecting a 400 back, and it did get a 400 back, and hence it’s saying this test is successful. Okay, so this idea of generative tests was an inspiration we took from two different schools of testing, two different ideas of testing. One is property-based testing. Show me a quick hand if you’re familiar with property-based testing. Okay, a few people. So what is property-based testing? You could take any system and you could say, “Okay, what are the properties of this system?”

 

Like In case of an OpenAPI specification, if a field is optional, then I can say that if I send that particular parameter or if I don’t send the parameter, in both cases, I should get back the same response, like a successful response. That’s one property. If I do not send a mandatory field, then the system should not give me back a 200. That’s a property of the system. So essentially, by looking at the OpenAPI specification, we can generate a bunch of properties and use that as an input to generate tests.

 

The second inspiration we took was from mutation testing. What is mutation testing? Imagine in my code, I have if some condition, then return x, else return y. And so I would have written, let’s say, two tests where in one case, the condition is true, and I would assert that I got x back. Another test I would have written where I would have simulated, so I get the condition false and I get Y back, and I assert that X and Y in each of those respective tests. So what mutation testing does is it basically goes and mutates the code.

 

So let’s say it makes instead of the condition, it says not of that condition, and then it expects two tests to fail. As long as the test fails, it says, “Okay, this is good. Your tests are valid and they’re catching the mutations.” If you’re not catching the mutants, then essentially, your tests are not good enough, right? So we’re not mutating the code, instead, we are mutating the request. So we are basically playing around with the request, and we are saying, “If I don’t send a mandatory one, I should get a 400. If I change the data type of something, then I should get a 400 back”. And that’s how we have been able to generate 41 tests out of this.

 

So quick recap, we started with just an OpenAPI specification. We generated seven tests when we started. We had five tests passing, two were failing, and we saw some mismatches between the OpenAPI specification and your actual implementation. So we fixed those typos and stuff like that. And then we generated examples to guide Specmatic, and then we went down to three examples, three tests.

 

Then we turned on generative test and we got 41 tests. There was, again, some mismatch between the specification We were able to fix that. And now we have 41 tests passing. All of this, again, without writing a single line of code. I keep repeating that without writing a single line of code because I think this is all the mundane stuff. Why would you sit and write tests, code for stuff like this?

 

Intelligent service virtualization

This is good, but we’re not happy yet, right? What else could we do? Notice here, we’ve been able to exercise the 200 and 400, which is why we are now at 67% coverage, but we still have this 500 left, and we have not exercised that. So how do I exercise the 500? So now I have 41 tests which requires, let’s say, the downstream service to respond back to me. But let’s say I want to now write a new test which will expect the downstream service to time out or not respond back at all. And I want to make sure that my service can now give me a 503 back, and I can verify that a 503 is in fact working as expected.

 

That’s maybe a little confusing. So let me go to the slide and see if that would be helpful. So my BFF layer is making a request to the domain service. Now, imagine the domain service here in your BFF layer, you have a timeout, if the domain service does not respond back to you, let’s say within three seconds, then you want to time out. When it times out, basically the BFF layer can’t really do much. It’s going to give you back a 503 service not available. Now, I want to simulate that. I want to actually test if timeouts are being implemented correctly or not. What if the developer has completely forgotten to implement any time out, and it’s just going to infinitely wait for the domain service to respond? So I want to simulate this condition where a downstream service is misbehaving, but I want my service to still adhere to whatever its specifications are. How would you do that?

 

Simulating downstream service timeouts

I want to see if the downstream service times out, then the BFF is still behaving the way I expect it to behave. If the downstream is timing out, I don’t want this guy to keep infinitely waiting. Correct? So I want this guy to respond back. So you could mock the downstream and then simulate these conditions, but for the 41 tests, I do want that guy to be there. It’s just that for the one test, I don’t want the guy to respond back on time, right? So one way to do is watch, see the 41 test run, and then when the 42nd test is about to go, shut down this service. That’s one way to do it, but life’s too short for that. So what I’m going to show you is a technique where where Specmatic can actually stub out a service. However, let’s imagine I don’t have an OpenAPI specification because someone says, “Hey, that’s an internal service, so we’re not going to give you an OpenAPI specification”.

 

So what do we do? We have a feature in Specmatic where you can generate the OpenAPI specification. So I’m going to go this called proxy. So I’m going to say, “Hey, Specmatic, run a proxy for Host 8090, which is where my domain service is running, and record whatever you see”. So this is your classic service virtualization. Basically now, Specmatic says, “Okay, proxy server started on port 9000”. What do I do? In my application properties, instead of 8090, now I’m going to make this 9000. I’m telling Specmatic that domain service is now available on 9000. Once that I’ve done this change, I have to restart my domain, the BFF layer, to make sure that it is up and running on that port, or at least it’s looking for it there.

 

Now, let me run this test, and you would see that on the proxy, a bunch of traffic is going through it, and it’s recording all that traffic. It stopped. And this test has no clue at this stage. It’s just run the way it was running. 41 are still there, 41 has passed. But what happened here is it’s recorded all of this.

 

And let me now just kill this. And when you kill, it will just basically spit out everything that it recorded. Let’s look here at what has happened. So there is a folder called Recording. Remember, I said, put whatever you find in the Recording folder, and it’s generated OpenAPI specification for me for the downstream service the domain service. And it generated 13 stop files, which is nothing but a request-response pair for me. But notice one thing. We had 41 tests, but we only have 13 of these stub files. So we call this Intelligent Service Virtualization. It’s not dumb. It’s not just capturing every request response and storing it as is. It’s actually looking and de-duping all of that and saying, “Okay, this is just a variation of that. This is just a variation of that”, and condenses it down to 13 unique request response pairs. So we call that Intelligent Service Virtualization because that’s trendy, right? All right, so now I have these stub files. What do I do? I say, “Specmatic take this YAML file and run it as a stub”. And it’s loaded all of them. It says, Okay, I have this running here.

 

Stubbing and testing asynchronous services

Now, what I’m going to do is I’m going to go to my domain service, which is running on port 8090. I’m going to kill that because I no longer need this guy sitting around. Let’s now go ahead and run the test again. What would we expect to see? Forty-one tests, all passing. But we see one failure. It’s not magic, right? Let’s look at why it failed. It says, okay, negative scenario. I expected a 400, but I have got a 201 back. Why did this happen? Let’s look at this particular thing that’s failed. Okay, so it’s made a request, and notice here, type is 989. But remember, type was an enum with only those four values. So the stub that was generated, the YAML file that was generated, did not capture this aspect that the enum for the type is only of these four values. So I’m going to just copy this thing and go to the generated stub over here, and I’m going to just let it know that whenever it gets a request of type “query”, it should only accept this enum.

 

So the stub that it generated, the YAML file, the contract, that did not have this captured. It couldn’t figure that out. So we have to guide it a little bit saying, “Okay, whenever a request comes, you have to accept this”. Same thing in the request body. There’s also, again, a type which is here. So we have to let it know that that also should be of type. So these are the two changes I have to do. We’re still trying to figure out if we can do this ourselves, but for now, we’re not yet there. So you have to guide this a little bit. So that’s a little bit of a caveat or a drawback. Any of you want to contribute to that, that would be great! Now, let me run this again. 41 tests, and all 41 passing. Of course, still no 500 because all we have done is we’ve just been able to stub out the downstream service. Now that we’ve stubbed it out, we have full control. We can play around with it any which ways we want. So what can we do now? Remember, we wanted to simulate a situation where we should be able to time out the downstream service.

 

So, to be able to do that, I’m going to go to my specification. I’m going to go to find. And notice we have these examples over here where we are saying this is an example of success where the type that we are sending as a parameter, gadget. I’m just going to create another example over here. I’m going to call it timeout. Copilot is already saying, okay, it looks like you would want a value like this, and I’m going to say other. There are four options. I’m going to say any time I’m going to send other, then timeout. Similarly, I’m just going to put a timeout here, and let’s say 99. And finally, when I get a response back here, I should have examples and example time out, and this should be the response back. So what I’m basically saying is, anytime I send a request for find products with type equals other, I expect a 500 back. All right? So let me just do that change and run this test. 41 tests were there, remember? So I’m going to go ahead and run this test now. So now it’s generated 42 tests.

 

Ensuring backward compatibility with stubs

It’s added one more test because I’ve added an example. And sure enough, that test has failed. This is basically saying, “Hey, I expected a 503, but I’m still getting a 200 back. Why? Because we’ve not done anything on the stub side. We’ve just set the expectation on this side. So now we’ll go and quickly set the expectation. And I want to show you how simple it is to do that. So here I have a /products which takes gadget, right? So I’m just going to duplicate this, call it as timeout, and on the timeout, I’m going to say other. Then here, I’m going to specify delay in seconds. I’ll set the delay in seconds to be 5 seconds. That’s the only change I need to make. Give an example, just copy an existing one and specify delay in seconds, five. So take five seconds before you respond back. Every time I make a change, you will notice that this automatically reloads the specification, so I don’t need to do anything with the proxy, the stub server. It automatically does that. Let’s go back to this, and let’s clean this out and run this.

 

42 tests. One test was failing last time. 42 tests, all 42 tests succeeded. You see this 503 is covered, and you have 100% coverage on this. So this is as simple as basically being able to try out different fault injection scenarios. Isn’t that great? Again, without a single line of code.

 

Someone can argue that the JSON files are code, and you did copy them… So with this, notice I’ve been able to get to 100% API coverage. Of course, there are two more scenarios, and I can cover them as well, but you get the point.

 

Interactive IDE extension for no-code Contract Testing

By the way, all of this stuff I’ve been doing through this interactive IDE mode, but I don’t expect developers to keep doing this. You’ll notice that in this contract test, this is a programmatic way of running this. So I’m specifying where my application is, localhost 8080. I have the stub running on 9000. I have this. I mean, it’s not running. You’re saying this is where you would want to run it.

 

And then in the “before all”, you essentially specify where are your stubs, start the Kafka mock, start the application, and similarly in the shutdown, the shutdown. This is the only one-time code that you would have to do. This also is generated, by the way, you don’t actually handwrite it. You only have to give those properties. And once you do that, you’d be able to generate the same 42 tests that you were seeing programmatically. Now I could run this in my CI pipeline whenever a developer raises a pull request, I would actually run this and make sure that no contract is broken before I accept the PR. So this becomes part of your regular development process, and it’s not being individually, manually being done.

 

All right, with that, let me just quickly summarise what we have seen so far. So we have the BFF, which is the system under test for us. We have Specmatic, which is running those contract tests for you using the OpenAPI specification. Similarly, we have Specmatic, which is also being used to stub out the downstream services, again, using the OpenAPI specification. We also use Specmatic to generate the Kafka stub, which essentially uses AsyncAPI to generate an in-memory broker and creates a topic on which it’s listening.

 

And it also does schema validations. I’ll show that in a minute. So essentially, the test initially sets the expectation from those JSON files that we had, onto the Specmatic stub. Then it also sets the expectation on the async, then makes a request which goes to the stub, the stub responds back, posts a message on the topic, and then gives a response back. Once it gets the response, it basically validates if the response is as per the schema or not. And then finally also verifies if on the messages that were posted on Kafka, whether they are as per the specifications in the sense of whether the message format meets your expectation and the count, how many messages were actually sent. So that’s the full cycle of what we call as contract tests over here. So what is actually happening here?

 

We have OpenAPI or AsyncAPI, which basically you can use for intelligent service virtualization, which means the consumer can now talk to this mock or stub, whatever you like to call it, without actually having to depend on the real service. There’s a very same API specification can also be used against the provider to generate contract tests against it.

 

A single source of truth for your API Specification

So this now becomes your single source of truth that basically validates if essentially the provider is adhering to this and using the same thing, I can stub out the provider and do my thing as a consumer. That’s the key idea over here. You have the same specification which is used both by the provider and consumer, and we put that into a single source of truth. So all API, OpenAPI or AsyncAPI specifications, we put into a Git repo, and it goes through a PR process to get into the Git repo.

 

The first part of the PR process does linting to make sure that the OpenAPI specification or AsyncAPI specification is as per the standards that you’ve agreed in your company. Next thing it does, is backward compatibility or compatibility testing.

 

Backward compatibility testing of API specs

What is compatibility testing? So let’s assume I have a version of the spec that is already in use. Now I want to add new endpoints or I want to add a new parameter to the existing specification because APIs are always evolving. But when I do that, I want to make sure that I didn’t accidentally break the compatibility. Now, to be able to do that, you would leverage Specmatic to do backward compatibility testing.

 

This is almost an accidental finding that we had. We said, “Hey, if I can use the same spec for generating tests as well as generating a mock, now guess what I can do? I can take I can take the new version of the spec that’s come in. I can take the old version of the spec that’s already there. I can take the new version, run it as a stub. So now I have a stub server running. I take the old version and I run it as tests against the stub. If all the tests pass, then the new version is backward compatible.” It’s a trivial idea, but it’s quite profound. You can actually do this, and now I can actually be sure that my new version is backward compatible. You can flip the order and say, “Is it forward compatible or not?” You can do full compatibility testing on this. That’s the idea of the central Git repo, and then, of course, someone will review and merge the PR.

 

Removing dependency on the provider as a consumer

To quickly summarise, we have Specmatic, which can leverage the OpenAPI or AsyncAPI. In fact, we also work with SOAP, by the way, because we realised quite a few companies also use SOAP and stuff like that. So you can take the specification on the consumer side, they can do their development locally and test everything locally. Similarly, the providers can use this almost like a test-driven development cycle where you would start with the OpenAPI specification, you will generate a bunch of tests, all of them will fail, and then you go and basically use GPT to generate the code, because nobody writes code these days. So you can just keep generating code and keep seeing all your tests pass. That’s pretty much the cycle we use a lot of times. And then in the respective CI, you would have each of them run these things, of course, using Specmatic to stub out the other thing, because in my pipeline, I don’t want a dependency on the provider as a consumer. So I can decouple, individually test each of these, but still make sure that nothing’s going to go wrong because we are talking to the single source of truth, the same version of the specification.

 

This is one problem with a lot of other alternatives, you’d see them drift away, and then when you get into some an integration environment, surprise, surprise. Things have evolved. Here, you expect not to see any surprises. At least that’s been our experience having battle tested this for four years now. And then your path to production is clean. And you’re figuring out things as early as possible in the cycle. So that’s the true shift left, and not figuring out these late in the development cycle, right? And this is what truly allows you to independently develop and deploy things. You no longer have to bring everything to an integrated environment, test everything together, and then say, “Okay, you can go ahead”. Because then you’re losing the entire benefit of microservices.

 

Visualise all your services and how they interact before going into production

I talked about insights. Again, an accidental finding. We said if you’re going to have all these tests running in the pipelines and generating all this data, can you do something with it? And the answer is yes, you can do a lot of interesting things with it. So you can actually visualise all your services and how they interact with each other or how they’re dependent on each other.

 

And some of the CTOs, we’ve shown this to, they’re like, “Oh, why do we have this circular dependent frequency. Why is this guy depending on this and then this guy is depending on that?” So you start visualising your landscape of services. You don’t have to wait till you go to production and get the logs and then visualise. You do this much earlier. So if you basically have wrong connections or if you have bottlenecks or stuff like that, you can figure that out very early in the cycle. You can actually click on each of these for more detail and stuff like that. So if you go to insights.specmatic.in, you’ll be able to play around with this. There are examples for you to do that. You can then look at an individual service and see, okay, what are the dependencies of this service and who’s calling this service? Because most often When I’m about to change some service, I actually have no idea who’s calling me, what’s dependent on me, or who I am dependent on. At least the developers would know, but who’s calling you, sometimes you’re not very sure about them.

 

You can also see, is it an HTTP dependency, a Kafka dependency, a readers dependency? What kind of dependency do I have? And then you can actually see what’s overall across all your projects, what’s your API coverage? And how many specs are there actually in the central repo versus not in the central repo or floating around in some emails or stuff like that. So you get all these real-time dashboards for you to be able to visualise what’s your current CDD, contract-driven development, adoption in your company.

 

Go to the link here and play around with it.

 

Plug-in architecture supports different protocols

Again, just a reminder, we can do JMS Mocking as well using this. We can do JDBC Stubbing. So this is something a lot of teams need. They have extremely complicated database interactions and stuff like that. And for the testing, they want to stub that out. So the same approach can basically be used for JDBC stubbing or Redis for that matter. The point I’m trying to make here is the way Specmatic is architected is each of these protocols or each of these different kinds of things are essentially plug-in into the architecture.

 

So you can now add anything else you want to add as a plug-in into it. Okay. So with that, I’m going to shut up, and hopefully this was useful for you.

More to explore