Did you know, Pact is nearly 10 years old!
As the de-facto leader in contract-testing, the eco-system has grown to be vast, just take a look below
However today, I am going to take you on a little journey of how it came to be, and show you what is to come.
A lot happens in 10 years. We’ve seen it all here at Pact, from the proliferation of micro services, to ever increasing protocols like Protobufs, GraphQL, transports such as gRPC, Websockets and MQTT, EventDrivenArchitectures and data pipelines or emerging standards such as OpenAPI, AsyncAPI and CloudEvents.
As we launch our Pact Plugin Framework bringing you new possibilities to the Pact eco-system, I’d like to invite you to try an interactive history lesson of Pact, from past, to present and beyond!
Pact and the Pact-Plugin Framework will unlock the possibility of testing multiple transport and content types. You will see Pact used for gRPC, Protobuf and CSV based messages. Hope it feeds your imagination of the possiblities, it certainly has for me!
If it piques your interest, you should sign up for our upcoming webinar to hear more about our exciting news and what it means for you and the software development community.
The birth of Pact Ruby
Pact was originally written by a development team at realestate.com.au in 2013, born out of trying to solve the problem of how to do integration testing for their new Ruby microservices architecture. They threw together a codebase that was to become the first Pact implementation.
git add . && git commit -m 'Gem skeleton' && git push by James Fraser
Ron Holshausen (at the time at DiUS, still one of the present day maintainers of Pact, and co-founder of PactFlow), first commit came shortly after.
A few months later Beth Skurrie (then at DiUS, still one of the present day maintainers of Pact and co-founder of PactFlow ), joined one of the teams that was working with the Pact authors' team.
She had recently seen a talk by J.B.Rainsberger entitled "Integration tests are a scam", which promoted the concept of "collaboration" and "contract" tests, so she was immediately interested when she was introduced to Pact.
J. B. has since softed his message, as have we, I think we all mellow as we get older :)
Beth's first commit in Pact Ruby
After trying (as most people do) to convince the authors that the provider should be the contract writer, she was soon convinced by Brent Snook, one of the original authors of Pact, of the value of consumer driven contracts. At this stage, she realised that what was missing in the current implementation was the ability to run the same request under different conditions, and "provider states" were born.
Viva la Pact Broker
What the heck is a Pact Broker anyway, Saf?
The Pact Broker (as Pact was) being written to solve our own problem, which was trying to coordinate pact versions between projects.
It is an application that allows you to release customer value quickly and confidently by deploying your services independently and avoiding the bottleneck of integration tests, introducing a pact matrix.
It looks a little like this
By testing the Pact Matrix, you can be confident to deploy any service at any time, because your standalone CI tests have told you whether or not you are backwards compatible – no “certification environment” needed. And when there are multiple services in a context, this approach scales linearly, not exponentially like the certification environment approach.
Preaching the message
Soon after, Beth decided that Pact idea was the best thing since sliced bread, and she hasn't stopped yacking on about it since. Hear Beth, Jon Eaves from REA and Evan Bottcher from ThoughtWorks speak at YOW!2014 in this YouTube video
Want a bit more of Beth? we told you she couldn't stop yakking
Ron began spreading the message, read a blog post from 2014 here
The birth of Pact JVM
Pact spread around the codebases in the wider program of work at realestate.com.au, until it hit its first Java microservice. realestate.com.au had many JVM projects, so a group of DiUS consultants (including Ron Holshausen again) started the pact-jvm project on a hack day.
Ron raised his first issue, https://github.com/pact-foundation/pact-jvm/issues/31, which led to his first PR
You can watch a talk from Ron here talking about pact and Pact JVM
Like all grown up frameworks, processes are needed, and a Pact Specification was born.
Beth penned the first Pact test cases, which came to be Pact Specification v1.0.0
It was at this stage that the authors realised that the Rubyisms in the format were going to have to be replaced by a non-language specific format, and the idea of the v2 pact-specification arose on Mar 27, 2014 though it would take a while (just over a year) before it became reality.
After tossing around the idea of implementing Pact yet again in another language, a decision was made to wrap the Ruby implementation (which was packaged as a standalone executable) to avoid the maintenance burden and potential of implementation mismatches. This became the pattern that was used for most of the following Pact implementations. Each language implemented a Pact DSL and mock service/verifier client, and called out to the Ruby mock service process/verifier in the background. The original Ruby JSON syntax was often used between the native clients and the mock service, as it was simpler to implement, however, the mock service took care of writing the actual pact in the v2 format.
The birth of Pact JS
Three versions of Pact-JS have existed, Fuying created the first commit of DiUS/pact-consumer-js-dsl. A familiar face Beth pops along for her first commit.
A few days apart, DiUS/pactjs0 was created, the first commit by
Jeff Cann. Ron dropped his first commit
ultimately deprecating it a little while later.
Enter Matt Fellows, dropping his first commit. A man of many talents, Matt is still one of the present day maintainers of Pact, as well as co-founding PactFlow.
Enter the still current Library, Pact-JS. It's first commit by Tarcio Saraiva.
A few months later, Pact-JS became the sole library going forward
This multi-language capability gave us the ability to start building cross-platform contract testing suites, as demonstrated below with JSON/HTTP interactions in laser focus
Pact proliferates - Lead by example
Since the implementation of the v2 format, newer features have been added, and the v3 and v4 formats add support for handing multiple provider states, messaging, and 'generators'.
One of the strengths of Pact is its specification, allowing anybody to create a new language binding in an interoperable way. Whilst this has been great at unifying compatibility, the sprawl of languages makes it hard to add significant new features/behaviour into the framework quickly (e.g. GraphQL or Protobuf support).
Wrapping the Ruby implementation allowed new languages to implement Pact quickly, however, it had its downsides. The standalone package worked by bundling the entire Ruby runtime with the codebase using Travelling Ruby, so it was large (~9MB). The native libraries also had to deal with the mock service process management, which could be fiddly on different platforms. It also made it difficult to run consumer tests in parallel, as each mock service process could only handle one thread at a time. The Ruby implementation was also lagging behind in feature development compared to the JVM, as Beth was spending more time on the Pact Broker.
To provide a single Pact implementation that could be used by all the required languages, the decision was made to create a reference implementation in Rust, that could be wrapped by each client language using FFI. The distributable package will be orders of magnitude smaller, and make it easier to run tests in parallel and avoid the process management issues, we have been slowly moving to our Rust core which solves many of the challenges that bundling Ruby presented.
It is worth noting that the "shared core" approach has largely been a successful exercise in this regard. There are many data points, but the implementation of WIP/Pending pacts was released (elapsed, not effort) in just a few weeks for the libraries that wrapped Ruby. In most cases, an update of the Ruby "binaries", mapping flags from the language specific API to dispatch to the underlying Ruby process, a README update and a release was all that was required. In many cases, new functionality is still published with an update to the Ruby binary, which has been automated through a script.
Beth often refers to the Ruby Goldberg machine, in a nod to Rube Goldberg.
We would love your engineering support in bringing efficiencies to our CI/CD processes used in our open source projects, or your artistic skills, if someone fancies drawing a Pact Rube Goldberg machine <3
Moving beyond HTTP
But, the industry has continued to innovate since Pact was created in 2013, and RESTful microservices are only one of the key use cases these days - protocols such as Protobufs and Graphql, transports such as TCP, UDP and HTTP/2 and interaction modes (e.g. streaming or server initiated) are starting to become the norm. Standards such as AsyncAPI and CloudEvent are also starting to emerge.
For example, Pact has been a rather HTTP centric library, and the mixed success in retrofitting "message support" into all languages shows that extensions outside of this boundary aren't trivial, and in some respects are a second class citizen.
The reason is simple: HTTP doesn't change very often, so once a language has implemented a sensible DSL for it and integrated to the core, it's more a matter of fine tuning things. Adding message pact is a paradigm shift relative to HTTP, and requires a whole new developer experience of authoring tests, integrating to the core and so on, for the language author to consider.
You can read more about non-HTTP messaging with Pact 🔗 here
Please note this one is a pet-project work in progress but it does show off message testing in various pact languages (Java, JS, .NET, PHP, Python & Ruby)
Pact Plugin Philosophy
Being able to mix and match protocol, transport and interaction mode would be helpful in expanding the use cases.
Further, being able to add custom contract testing behaviour for bespoke use cases would be helpful in situations where we can't justify the effort to build into the framework itself (custom protocols in banking such as AS2805 come to mind).
To give some sense of magnitude to the challenge, this table showed some of the Pact deficiencies across popular microservice deployments as of a couple of years ago
So the pact plug-in eco system was born, a way to allow new transport types, content matchers/generators and more to easily be added to the pact framework, without needing to wait for the core maintainers to roll it out. You can create your own, for public, private or commercial consumption!
Enough blurb, show me da code
Whilst it may be quite technical for some, others will relish the possibilities this will unlock. If you want something or see a use case, but aren’t quite sure how to put it to reality, try out our demos and give us a shout via 🔗 canny, our feature request board, or 🔗 slack
To prove how easy it was, and as a nice little nod back to the Grandmother of Pact, Ruby. Your very own devo avo put his money where his mouth is and built his own.
Try out our pact plug-in framework here
This will allow you to see Pact and the Pact-Plugin Framework to test multiple transport and content types. You will see Pact used for gRPC, Protobuf and CSV based messages. Hope it feeds your imagination of the possiblities, it certainly has for me!
And to anchor it back to a picture you probably know from our Pact docs, plugins just sit in the middle and help extend the capabalities
Choose Possibilities, Choose plugins, Choose Pact!
A thank you to those who got us here