Writing

Every provider is inconsistent differently

I am the first engineer at Nicer. I wrote the first line of code. When you are early on a small team you get very honest, very fast, about which parts of the system are load-bearing.

The integration layer is load-bearing. It is also the part nobody demos.

Travel data is inconsistent, and every provider is inconsistent differently

A platform like this talks to a lot of travel and technology providers. Each one has its own API shape, its own data model, its own authentication scheme, and — the part that costs you — its own idea of what an error is.

Some return 200 with an error in the body. Some return a room that is not actually available. Some have a rate that expires between the search and the booking, which is correct behaviour and still ruins your day. Some encode dates in a format that is only unambiguous in one hemisphere.

The canonical version of this, the one everyone in travel has met: two people search for the same room at the same time. Both see it. Both try to book. One succeeds and one gets an error after entering their card details. Nobody wrote a bug. Availability at search time is a snapshot, and the industry’s answer — re-verify the rate live against the supplier at checkout — is the correct answer and also an admission that the first number you showed was never a promise.

The naive version of this is a client per provider and a switch statement. It works for three providers. At ten it is unmaintainable, and at thirty it is a full-time job for someone who quits.

Normalise the shape, not just the fields

The mistake I nearly made was thinking normalisation means field mapping — their hotel_name is our property.name, done.

Field mapping is the easy half. The hard half is normalising behaviour:

  • What “available” means. For one provider it is a promise; for another it is a hint that needs re-confirmation before you take money.
  • What failure means. A timeout, a 500, and an empty result set are three different things, and providers disagree about which one they are having.
  • What freshness means. How long a quoted price is good for, and whether the provider will tell you or expects you to know. Rates move with demand; a price quoted at search may be gone by checkout.
  • What identity means. The same physical hotel has a different id per provider, and sometimes more than one within the same provider. Room categories map even worse — “Deluxe King” is not a standard, it is a marketing decision made independently by every supplier.

If you only normalise the fields, all of that leaks upward, and then every feature above the integration layer has to know which provider it is talking to. At that point you do not have an integration layer. You have thirty integrations with a shared folder.

What I would tell myself at provider number three

Write the failure taxonomy early. By the third integration you can see the shape of the problem, and you still have few enough call sites to change your mind cheaply. By the tenth you are documenting whatever you already did.

Make every provider prove it can fail. A provider integration is not done when it returns a booking. It is done when you have seen it time out, return garbage, and expire a rate — and the layer above did something reasonable each time.

Keep one provider-specific escape hatch, deliberately. There is always one supplier with a genuinely different capability worth exposing. Pretending otherwise means either the abstraction gets bent to fit it, or the capability gets thrown away. One clearly-labelled exception is cheaper than a leaky abstraction.

Log the raw payload. You will need it. Not the parsed object — the bytes that arrived. Every integration bug I have chased in this system was solved by looking at what the provider actually sent, which was never what its documentation said it would send.

Why it is worth it

An advisor searches, and the results come back looking like one system — because the layer underneath spent a long time absorbing the fact that it is not.

Nobody has ever thanked me for an integration layer. It is the thing that makes everything above it possible, and it is invisible exactly when it is working.