DEV Community

Cover image for 🔥 Contract Testing: the bug that passes CI and breaks production
PatrickBastosDeveloper
PatrickBastosDeveloper

Posted on

🔥 Contract Testing: the bug that passes CI and breaks production

You deploy with confidence.
Tests pass.
The build is green. ✅

Minutes later…

🔥 Production is broken because an API field changed.

If this has already happened to you (or will), this post is for you.

Recently, we’ve started adopting contract testing at the company where I work, integrating it into the set of validations that already run in our CI pipeline. The company has a strong testing culture and well-established quality standards, and the goal here was to complement what was already in place by adding more confidence to service-to-service communication. In this post, I want to share what we’ve learned so far and my practical impressions of using contract testing in day-to-day development, keeping things straightforward and free from unnecessary theory.

❌ The problem traditional tests don’t solve

In modern architectures, this is common:

Unit tests pass

Integration tests pass

Swagger is up to date

But… the API consumer breaks at runtime

Why?

Because traditional tests validate implementations, not agreements between systems.

And that’s where the real problem lives.

🤝 What Contract Tests are (no academic definition)

Contract Testing is basically this:

A formal agreement between API consumers and providers.

It ensures both sides agree on:

Payload structure

Field types

Status codes

Implicit rules (required fields, formats, etc.)

If someone breaks that agreement…
🚫 the build fails before reaching production.

💥 A simple (and painful) example

The consumer expects this:

{
  "id": 1,
  "name": "Patrick"
}
Enter fullscreen mode Exit fullscreen mode

The provider decides to “improve” the API:

{
  "id": 1,
  "fullName": "Patrick Bastos"
}
Enter fullscreen mode Exit fullscreen mode

✔️ Backend works
✔️ Swagger updated
✔️ Tests pass

❌ Frontend breaks
❌ App crashes
❌ The user finds out first

This bug is not about code — it’s about communication.

🛡️ Where Contract Tests come in

With Contract Testing, the flow changes:

Consumer defines expectations
↓
Contract is generated
↓
Provider validates the contract
↓
Deploy only happens if the contract is respected

Enter fullscreen mode Exit fullscreen mode

In other words:

Whoever changes the API without warning… breaks their own pipeline.

And that’s beautiful ❤️

🧰 The most used tool in .NET: Pact

In the .NET ecosystem, the most mature and widely adopted tool is Pact, using PactNet.

Why Pact works so well

Consumer-Driven Contract Testing (CDC)

Tests written in C#

Versioned contracts

Automatic provider verification

Easy CI/CD integration

🧪 How this works in practice (very short version)
On the Consumer side

You write a test saying:

“When I call /customers/1…”

“I expect this response…”

That test generates a contract file.

On the Provider side

The backend runs a test validating:

“Does my API still respect this contract?”

If not:
❌ build fails
❌ deploy blocked

No production surprises.

⚠️ Contract Testing is NOT a silver bullet

Important to be clear:

❌ It doesn’t replace integration tests
❌ It doesn’t test business rules
❌ It doesn’t guarantee bug-free code

✅ It guarantees communication stability
✅ It prevents breaking changes
✅ It reduces silent incidents

🟢 When it’s REALLY worth it

Microservices

Multiple teams

Public APIs

Independent deployments

Frequently evolving endpoints

🟡 Maybe not worth it (for now)

Simple monoliths

Small teams

Joint deployments

Low complexity

✅ Quick checklist (real-world lessons)

❌ Don’t rely only on Swagger

✅ Version your contracts

✅ Let the consumer define the contract

❌ Don’t couple contracts to implementation

✅ Run contract tests in CI

✅ Fail fast

🎯 Conclusion

Contract Tests don’t prevent bugs.

They prevent surprises.

And in production, surprise usually means incident.

If your API changes without fear,
your consumer suffers without warning.

💬 Let’s talk

Have you ever broken production because of a contract?

Have you used Pact or another tool?

Are you still relying only on Swagger?

Drop a comment 👇
These pains are collective 😄

Top comments (0)