Choosing a vendor is usually framed as a feature decision: they do the thing, building it ourselves would take months, the pricing works. What that framing leaves out is that you’re not just adopting their capability — you’re adopting their operational behavior wholesale. Their p99 becomes part of your p99. Their maintenance window becomes a window where part of your product doesn’t work. Their rate limit becomes a ceiling on your throughput that you discover during your busiest hour, which is also, by construction, theirs.

The subtler inheritance is semantic. Every external API encodes decisions about what its errors mean, and those decisions rarely match the ones you’d have made. A 200 response with an error object in the body. A timeout that may or may not mean the operation was rejected. A “not found” for a resource that exists but isn’t visible to your credentials. An update that returns success and takes effect thirty seconds later. Your code has to translate all of that into your own model of what happened, and every place the translation is sloppy becomes a bug that looks like your fault, because from the user’s perspective it is.

Which is why the integration boundary deserves more design than it usually gets. Calling the vendor’s SDK directly from wherever you happen to need it spreads their semantics — and their quirks, and their eventual replacement — through your whole codebase. A thin layer that speaks your domain’s language, translating their errors into yours and their model into yours, costs little and contains the blast radius. It also gives you one place to put the timeout, the retry policy, the circuit breaker, and the fallback, rather than discovering that four call sites each handle failure differently and two of them not at all.

The other half is deciding, per integration, what should happen when it’s down — because it will be, and the default answer is usually accidental. If the payment processor is unreachable, failing the request is correct. If the analytics service is unreachable, failing the user’s action because of it is absurd, yet that’s exactly what an unbounded synchronous call in the request path will do. Sorting dependencies into “the product cannot function without this” and “this is nice to have” is a five-minute exercise that determines whether a vendor’s bad afternoon is a degraded feature or a full outage on your side. Timeouts matter here more than anything: a dependency without a timeout isn’t a dependency, it’s a shared fate.

None of this argues for building everything yourself; that trade is usually terrible. It argues for entering the arrangement with clear eyes. You’re not buying a feature, you’re taking on a relationship with someone else’s system — its reliability, its semantics, and its incidents, none of which you control. The integration work that pays off isn’t the part that makes the happy path work. It’s the part that decides, in advance, what your product does on the day theirs doesn’t.