Build the Lab Before You Integrate

A separate, isolated, noise-free solution where you prove out the integration before it touches your system. What actually breaks, where to point it, and what you carry back to the real project.

That day arrives: you have to integrate a third-party API. You wire it into your system and suddenly errors start showing up in the logs.

You go in to analyze them and realize you're knee-deep in a mess. The integration has become hard to test, and you've lost the Postman collection — or worse, you still have it and it doesn't help.

That's when the need shows up: check the integration in a controlled environment, free of contamination.

And that's what I suggest to my team: before you integrate, build the lab.


#Postman doesn't test what you think it tests

This is the misunderstanding that makes almost everyone skip this step.

Postman tests their API. It confirms the provider responds, that the contract roughly matches the documentation, that your credentials work.

The lab tests your code against their API. Which is an entirely different thing.

Postman won't show you how that deserializes in C#. Or which exception surfaces when the provider takes too long. Or what happens to your HttpClient when you send a thousand calls instead of one. Or whether your retry created a duplicate charge.

A single request coming back 200 tells you none of that.


#What the lab is

A project built from scratch, clean, isolated. Without the noise of the big system.

That's where you write the main consumption methods, define the contracts, and make sure it works — including at high volume. Since it's isolated, you can even drop a benchmark in and measure that library against another option before committing to one.

In practice I go one of two ways, and the criterion is simple:

  • A console app for most cases. You install the libraries and wire them up by hand.
  • A Web API when it's something more specific and I don't want to spend time setting up dependency injection. There it comes configured out of the box.

It depends on the complexity of what you're integrating. Which one you pick matters less than the fact that it's isolated.

That isolation is the whole point: in a large project these problems still show up, but tangled with your own and buried under twenty layers. In the lab, you can see them. My estimate, having done this a number of times, is that it spares you around 90% of the errors that integration was going to hand you in production.


#What actually breaks

This is the list I write labs for:

Timeouts and 429s. The provider is slow, drops, or rate-limits you. And that raises the question almost nobody asks in time: do you retry? how many times? how long do you wait between attempts?

Volume. Everything works with one request. Bulk testing is another story: that's where the provider's limits show up, and yours too.

Deserialization. The JSON didn't arrive the way the docs promised and it deserialized into nulls or default values without raising a single error. It breaks three layers later, far from the cause.

Idempotency. This is the expensive one. If you retried after a timeout, did the first call land anyway? If the provider isn't idempotent, your retry wasn't a retry: it was a second order, a second charge, a second shipment. And it's a problem that only surfaces when you combine failures with retries and volume — exactly what the lab lets you trigger on demand.

Coupling. This is the deepest one, and it isn't a runtime error: it's a design decision. What's the integration's responsibility and what's your system's? Is retrying handled by the API client or by its caller? Does mapping their model to yours happen inside or outside? Who decides what to do when it fails?

If you don't settle that in the lab, you'll settle it in a hurry inside your own code, with the provider leaking through everywhere.


#Where to point it

It depends on what you need, and it isn't always one single answer:

  • The provider's sandbox, when it exists and when it's faithful. Watch that second condition.
  • A development or testing environment, if the provider gives you a real one.
  • Production, read-only, when the sandbox lies or simply doesn't exist.

That last one deserves a warning. If you're going to hit production to understand the real behavior, it's exactly the scenario where a write guard earns its keep: the lab is a loose project, put together quickly, without the protections of the big system. It's the easiest place in the world to fire off a POST you didn't mean to.


#What you carry back

The lab's deliverable isn't "it works." It's this:

  • The minimum information you need for the implementation to land right the first time.
  • A settled distribution of responsibilities — decided calmly, not mid-incident.
  • Clarity on the likely failure modes, which is what lets you handle them for real instead of wrapping everything in a try/catch and praying.
  • A concrete, reusable implementation, which you then inject behind its interface.
CSHARP
// What comes out of the lab is the contract, not the detail
public interface IShippingProvider
{
    Task<ShipmentResult> CreateAsync(ShipmentRequest request, CancellationToken ct);
}

And that last point has a long tail: because the piece was born isolated, it isn't tied to your system. If another project needs the same integration tomorrow, you already have it. If someone asks for it as a package one day, there's nothing to disentangle — you can publish it as a NuGet.

It's the only kind of reuse you don't pay for later: the kind that came out isolated from the start, not the kind you have to pry loose from a system that already absorbed it.


#What's left

The lab isn't time lost before starting. It's the only moment when you can get the integration wrong without it mattering to anyone.

After that it isn't an experiment: it's code in your system, with users on the other side and an incident waiting its turn.

#integration #testing #dotnet #api