What Happens When Your SaaS Depends on 20+ Third-Party APIs?
Share this post

Maria signs up for a project management tool called Fieldnote on a Tuesday afternoon. She types her email, picks a password, and clicks "Create account." A few seconds later, a confirmation email lands in her inbox. She clicks the link, lands back in the app, and starts a 14-day trial.

She invites a colleague. She uploads a contract PDF to a new project. She gets a notification that the file was processed and indexed for search. She connects her calendar. She asks the built-in assistant to summarize the contract's payment terms, and it does, in about four seconds. At the end of the week, she upgrades to a paid plan, enters her card number, and the app shows "Payment successful." Her company's sales team, using a different login, sees her account activity show up in their CRM the next morning.

To Maria, this is one product. Fieldnote. A single, coherent thing that either works or doesn't.

Behind the screen, that ten-minute journey touched roughly a dozen separate companies, none of which Maria has ever heard of and none of which she needs to. A domain name service resolved fieldnote.io to an IP address. A content delivery network served the static assets. A cloud provider ran the application servers and database. An authentication service verified her password and issued a session token. A transactional email service delivered the confirmation link. An object storage service held the uploaded contract. A document-processing service extracted its text. A search index made that text queryable. A large language model API generated the summary. A payment processor authorized her card and a fraud-screening service quietly checked the transaction before it went through. An analytics platform logged the upgrade event. A CRM synchronization job pushed her account status to the sales team's dashboard.

Fieldnote's engineering team wrote comparatively little of the code that made this journey happen. They wrote the orchestration — the logic that decides what to call, when, and in what order — and the interface that stitches the results into something that feels seamless. The actual capabilities were rented, not built.

This is not a criticism of Fieldnote. It is, in fact, why a small team was able to ship a product this capable at all. A software company in 2015 might have needed a payments engineering team, an email deliverability specialist, a fraud analyst, and a search infrastructure engineer just to reach feature parity with what Fieldnote assembled from APIs in a few sprints.

But there is a question worth sitting with, because most engineering teams answer it only after something breaks: how many companies have to behave correctly, at the same time, for one customer's ten-minute journey to succeed?

The company and the product described here are illustrative composites, not a real business. But the pattern is not fictional. It is close to the default architecture of most B2B SaaS products built in the last several years. This article is an investigation into what that pattern actually means — not as a warning against using external services, but as an attempt to make a mostly invisible form of architectural risk visible enough to manage on purpose.

Chapter 1: Your Product Has an Invisible Supply Chain

Physical manufacturers have understood supply chain risk for a long time. A car company does not build its own semiconductors, tires, or glass. It assembles components from specialized suppliers, and it accepts that a shortage at any one supplier can stop the assembly line even though every other part is available. Software companies build the same kind of structure, usually without naming it.

Call it a software dependency supply chain: not a security supply chain in the sense of verifying package integrity, but an operational one — a network of external capabilities that your product assembles into something a customer experiences as a single system.

It helps to sort these dependencies into categories, because different categories fail in fundamentally different ways.

Foundational dependencies are the substrate everything else sits on: cloud compute, DNS, CDN, object storage, managed database services. When these degrade, almost nothing above them works correctly, because almost everything routes through them.

Identity dependencies cover authentication, single sign-on, and identity verification. These sit at the front door. If they fail, customers who are already inside may be fine for a while, but nobody new gets in, and depending on session design, people already inside may eventually get locked out too.

Transaction dependencies are payments, billing, tax calculation, and fraud detection. These carry money and legal obligation, which makes their failure modes different in kind from most other categories — a failed transaction can create a support ticket, a refund, or a compliance question, not just a broken page.

Communication dependencies are email, SMS, and push notification services. Their failure is often invisible in the UI. The application appears to work. The message that was supposed to arrive simply doesn't, and nobody notices until a customer says "I never got the reset link."

Product capability dependencies are search, maps, AI inference, document processing, and video — the things that are often the actual feature a customer is paying for, not supporting infrastructure around a feature.

Business system dependencies are CRM, analytics, support platforms, and accounting systems. These usually sit outside the customer-facing path entirely. Their failure rarely breaks the product for a customer. It breaks the business's ability to see and operate the product.

The categories matter because "how many APIs do we depend on" is a much less useful question than "which category does each dependency sit in, and what does the product look like without it." A broken analytics pipeline is an inconvenience. A broken authentication provider is an outage. Counting them the same way, as if each dependency contributed equally to risk, produces a badly miscalibrated picture of what could actually hurt the business.

Chapter 2: The Dependency Iceberg

Engineering teams tend to describe an integration by the call they make: POST /v1/payment_intents, GET /v1/customers/{id}, POST /v1/messages. That single line is the visible tip of a much larger relationship, and most of what determines whether the integration is safe sits below the waterline, unmentioned in any architecture diagram.

Below that line sits a long list of things a team is implicitly depending on, whether or not anyone wrote them down:

  • the credentials used to authenticate, and what happens when they expire or get rotated
  • the DNS resolution and TLS certificate chain that let the request reach the provider at all
  • the specific SDK version pinned in the codebase, and whether it still matches the provider's current API
  • the API version the integration was built against, and whether the provider still supports it
  • the rate limits and quotas attached to the account, which are usually invisible until they're hit
  • the region the provider serves requests from, which affects latency and sometimes data residency
  • the webhook delivery mechanism, including retry behavior and delivery guarantees
  • the schema of every field the integration reads, and whether the provider can add, remove, or rename fields
  • the provider's own operational status, which is a dependency on top of a dependency
  • the network path between your infrastructure and theirs
  • the specific pricing tier the account is on, which can silently cap throughput or feature access
  • the provider's data retention policy, which determines whether historical data is even recoverable
  • the permission scopes granted to the integration, which determine what it can and cannot do if compromised
  • the account-level configuration set by whoever originally connected the integration, which may or may not be documented anywhere

"We integrate with Provider X" describes one function call. It does not describe the operational relationship, which includes all of the above, most of which nobody reviews again after the initial implementation ships. The iceberg framing is useful precisely because the visible API call is the smallest and least risky part of the dependency. The parts that cause incidents are almost always below the line.

Chapter 3: Build the Dependency Map

To make this concrete, imagine a fictional B2B SaaS company — not Fieldnote specifically, but a company of similar shape and maturity — that has grown for three years and accumulated roughly twenty external dependencies along the way. No individual decision to add any one of them was unreasonable. Each one solved a real problem faster than building it internally would have.

A representative inventory, organized by function rather than by vendor name:

  1. DNS
  2. CDN
  3. Cloud compute
  4. Object storage
  5. Managed database / data platform
  6. Authentication and identity
  7. Payment processing
  8. Tax calculation
  9. Fraud detection
  10. Transactional email
  11. SMS / notifications
  12. Marketing analytics
  13. Product analytics
  14. Error and performance monitoring
  15. CRM synchronization
  16. Customer support platform
  17. Search indexing
  18. AI inference
  19. Document / file processing
  20. Team collaboration integration (e.g., chat or calendar sync)
  21. Feature flag / experimentation management

That is twenty-one, and a mature product often has more once every internal tool, logging pipeline, and background integration is counted. Few teams have ever listed them all in one place, which is itself telling.

A simplified dependency map for the signup-to-payment path might look like this:

CUSTOMER
   │
   ▼
YOUR APPLICATION
   ├── DNS / CDN
   ├── Identity Provider ────────► Session Store
   ├── Payment Provider
   │        └── Fraud Service
   │        └── Tax Service
   ├── Email Provider
   ├── Object Storage ───────────► Document Processing API
   │                                     └── Search Index
   ├── AI Inference Provider
   ├── Product Analytics
   ├── Marketing Analytics
   ├── CRM Sync
   └── Error Monitoring

Even this understates the real graph, because most of these boxes are not leaf nodes. They are themselves clients of other infrastructure. This is the distinction between a direct dependency — something your application calls — and a transitive dependency — something your direct dependency relies on, which you did not choose, cannot see into, and typically cannot control.

Your payment provider depends on card networks, banking infrastructure, and its own cloud hosting. Your email provider depends on the deliverability reputation of shared sending infrastructure and the receiving mail servers of every provider your customers use. Your authentication service depends on its own database and, often, on the same handful of major cloud platforms that much of the internet already depends on.

You cannot fully audit this second layer. You usually cannot even see it without reading a provider's own architecture disclosures, which are not always public or current. The honest response to transitive dependency is not to try to map every layer exhaustively — that effort has diminishing and eventually negative returns — but to accept that your risk surface is larger than your vendor list, and to weight your resilience decisions accordingly rather than pretending the graph ends where your direct integrations do.

Chapter 4: Now Remove One Piece

A dependency map answers "what do we call." It doesn't answer the more important question: what happens to the product when one of these stops behaving normally. The only way to find out honestly is to walk through specific failures and trace their consequences end to end.

Simulation A: the transactional email provider is unavailable for 45 minutes.

Signup verification emails stop arriving. New users cannot confirm their accounts, so trial activation stalls. Password reset requests silently vanish — a customer who forgot their password now has no way back in, and no visible error message tells them why. Invoices queue up but don't send, which is a minor problem if billing isn't time-sensitive, and a real one if a customer expected same-day confirmation for a large purchase. Team invitations stop delivering, quietly reducing the viral loop that drives account expansion. Alert emails to admins about account activity go missing.

Not everything in that list deserves the same response. Password reset is close to critical — a customer locked out of their own account during an outage has no self-service path forward at all. Signup confirmation is important but can tolerate a delay if the UI is honest about it. Invoices and admin alerts can wait an hour without real damage. The engineering decision this scenario forces is not "is email critical" — it's "which specific email-dependent actions are critical, and can we build a queue so that when the provider recovers, the queue drains instead of silently dropping messages sent during the outage."

Simulation B: the authentication provider becomes unavailable.

Here the first useful move is to separate complete outage from degraded performance, because they produce very different symptoms. In a complete outage, no new logins succeed. Existing sessions may continue working for a while if session validation is cached locally rather than re-checked against the identity provider on every request — a design choice made months or years earlier, for unrelated reasons, that now determines whether existing customers can keep working during the incident. Token refresh, which typically happens silently in the background, starts failing, which means sessions that would otherwise survive begin expiring anyway as their tokens age out. Administrative access, often gated through the same identity provider, may be unavailable exactly when the team needs it most to diagnose the incident. Any service-to-service authentication that routes through the same provider compounds the failure into internal systems that customers never directly touch.

Degraded authentication — elevated latency rather than total failure — is in some ways worse to detect and just as damaging to experience. Logins take eight seconds instead of half a second. Some percentage silently time out and appear to the user as a broken login page rather than a slow one. Nothing in a standard uptime dashboard distinguishes this from a healthy system under load.

Simulation C: the payment provider is responding slowly.

Slowness is more dangerous than an outright outage, because outright outages are obvious and slowness invites bad behavior from both software and humans. A request is sent. Ten seconds pass with no response. Is the request still processing, or did it fail silently somewhere in the network? A frontend timeout fires and shows the customer an error, but the backend request may still complete on the provider's side a few seconds later. The customer, seeing an error, clicks "pay" again. Now two authorization requests may be in flight for the same purchase. Whether that produces two charges, one charge, or a duplicate authorization that later reconciles depends entirely on whether the integration was built with idempotency in mind — a detail almost never on anyone's mind on the day it was implemented, only on the day it matters.

Simulation D: the AI provider starts returning errors.

The correct response depends entirely on a categorization question that most teams have never explicitly answered: is this AI capability a convenience, a core feature, or a critical workflow step? A "summarize this document" button that occasionally errors out is an annoyance. An AI-generated compliance check that gates whether a document can be approved is a critical dependency wearing the disguise of a feature. Teams frequently build the second kind while mentally filing it under the first, because the initial version of the feature really was a convenience, and criticality crept in gradually as customers built workflows around it.

Simulation E: the analytics provider fails.

The product itself may keep running without any visible customer impact. But experimentation halts, because there's no reliable way to measure which variant is winning. Attribution breaks, so marketing spend decisions for the next several days are made on incomplete data. Dashboards that leadership checks every morning go blank or stale. Nobody outside the company notices. Everybody inside making decisions is now flying partially blind, and the danger is that this kind of failure is easy to deprioritize precisely because no customer complains about it.

These five scenarios point toward a taxonomy worth naming explicitly, because "the product is down" collapses several genuinely different situations into one:

  • Product outage — the customer cannot use the application at all.
  • Feature outage — a specific capability is unavailable, but the rest of the product works.
  • Control-plane outage — the ability to configure, administer, or manage the system is impaired, even though the core customer experience is fine.
  • Observability outage — the system works, but the team's ability to see how well it's working is gone.
  • Business-operations outage — customer-facing behavior is unaffected, but internal functions like billing reconciliation, CRM sync, or reporting are broken.

Naming these separately matters because they demand different urgency, different responders, and different customer communication — and because treating a business-operations outage with product-outage urgency (or vice versa) burns trust and attention in the wrong places.

Chapter 5: Failure Is Not Binary

Most incident response training implicitly assumes two states: the API works, or the API is down. Real distributed systems fail in far more textured ways, and the textured failures are usually the ones that cause the worst outcomes, because they don't trip the alarms built for binary failure.

Consider the fuller list of things that can go wrong short of an outright outage: elevated latency that doesn't cross any alert threshold but degrades every downstream experience; intermittent errors that affect one request in twenty, invisible in an aggregate success-rate graph but very visible to the unlucky customer who hit it twice in a row; malformed responses that pass basic validation but contain subtly wrong data; partial responses, where a batch request returns results for some items and silently omits others; stale data served from a cache that hasn't caught up with an underlying change; throttling that returns technically valid responses but at a fraction of requested volume; webhook events delivered twice because the provider's own retry logic doesn't know the first delivery succeeded; webhook events delivered late, arriving minutes after the event they describe; events arriving out of the order they occurred in; a client built against one API version encountering the subtly different behavior of a newer one; regional degradation where one data center serves fine and another doesn't; and internal inconsistency on the provider's side, where two different endpoints report two different truths about the same object.

It's useful to sort all of this into three categories.

A hard failure is unambiguous — a connection refused, a clear error response, a request that visibly did not go through. These are the easiest to design for because the system knows something went wrong.

A soft failure is one where the request technically succeeded, but the result is wrong, incomplete, or degraded in a way the caller doesn't automatically detect. A search query that returns zero results because the index is stale looks, to the calling code, exactly like a search query that correctly returned zero results.

An ambiguous failure is the hardest category, because the caller genuinely does not know what happened. A payment request is sent. The provider receives it, processes it, and the charge succeeds — but the network connection drops before the response reaches your application. From your system's point of view, the request simply never returned. Did the charge happen? You don't know, and guessing wrong in either direction has a cost: assume it failed and retry, and you may double-charge the customer; assume it succeeded and don't retry, and the customer may have been charged for nothing they received, or not charged at all for something they now expect.

This is not a hypothetical edge case invented for the article — it's one of the foundational reasons idempotency keys exist in modern payment APIs, and the reason mature integrations are built to ask "what is the actual state of this transaction" after an ambiguous result, rather than to guess. The deeper lesson is architectural: distributed systems create genuine uncertainty, not just occasional errors, and any integration that assumes every outcome resolves cleanly to "succeeded" or "failed" will eventually be wrong at the worst possible moment.

Chapter 6: The Timeout Cascade

Trace a single synchronous customer action end to end. A new user finishes filling out the signup form and clicks submit. Behind that click, the backend might call an identity service to create the account, a fraud service to screen the signup, a CRM API to create a contact record, an email provider to send the confirmation, and an analytics platform to log the event — all before the customer sees the next screen.

Should all five of those calls block the response the customer is waiting for? Almost certainly not, and the reasoning doesn't require inventing specific timing numbers to be clear. Every synchronous call in that chain becomes, by definition, part of the customer's response time. If each service typically responds quickly but occasionally takes several seconds, and five of them sit in sequence on the critical path, the odds that at least one of them is slow on any given request compound. The user isn't waiting on the slowest service in isolation — they're waiting on whichever one happens to be slow today, and over enough signups, one of them always is.

This is the idea of a critical path: any dependency that must respond before the customer can proceed is, by definition, part of the critical path, whether or not anyone intended it to be. The account creation call belongs there — the customer cannot continue without an account existing. Fraud screening might belong there if it's meant to block risky signups before they complete. But the CRM sync, the analytics event, and arguably the confirmation email do not need to hold up the response at all. The customer doesn't need to wait for their sales rep's CRM to update before they see their new dashboard.

The general principle is to separate critical synchronous work — the minimum that must happen before the customer can proceed, and where failure should visibly block the action — from deferrable asynchronous work, which can happen after the response is returned, retried independently, and allowed to fail without affecting what the customer sees. Moving the CRM sync and the analytics event off the request path and into a queue doesn't just make signup faster on a good day. It means a CRM outage or an analytics API hiccup can no longer prevent a new customer from creating an account — which, before that change, it silently could.

This is one of the highest-leverage exercises an engineering team can run on an existing system: walk every customer-facing action, list every external call it makes, and ask which ones the customer is actually waiting on versus which ones simply happen to be written inline because that was the easiest way to build the feature the first time.

Chapter 7: Rate Limits — The Failure You Create by Succeeding

Some of the worst dependency incidents are self-inflicted by growth, not by a provider's mistake. A provider can work exactly as designed, at exactly its documented limits, and still become the bottleneck that breaks your product — because your product got more successful than the integration was built to handle.

A rate limit caps how many requests can be made in a given time window. A quota caps total usage over a longer period — a month of API calls, a number of active records, a volume of processed documents. Throttling is what happens when either limit is hit: the provider starts rejecting or delaying requests, often with a specific status code, rather than processing them.

Picture a customer who imports 100,000 contact records on their first day using the product, and the application synchronizes each one individually to a CRM as it's created. If the CRM's API allows a modest number of requests per second, that import alone could take hours, and every other customer whose data also needs to sync to the same CRM account is now queued behind it. Or picture a product mentioned in a widely shared industry newsletter, sending ten times normal signup traffic to the identity provider within an hour — an event that has nothing to do with any provider misbehaving and everything to do with success arriving faster than the integration was designed for. Or picture a background job that failed to process a batch of records overnight and, on waking up, tries to retry all of them simultaneously against a downstream API that can't absorb that burst.

That last scenario has a name: a retry storm. Retries exist to make systems more reliable — if a request fails, try again, and it probably succeeds the second time. But naive retry logic, applied at scale, can turn a small, transient failure into a much larger, self-inflicted one. If a thousand failed requests all retry immediately and simultaneously, the resulting burst can overwhelm a provider that was only mildly struggling a moment before, extending an outage that would otherwise have resolved on its own.

The standard countermeasures are well established and worth naming plainly rather than mysteriously: exponential backoff, where each retry waits longer than the last, rather than every request retrying on the same clock; jitter, adding a small random variation to each wait time so that a thousand simultaneous failures don't all retry at the exact same instant and re-create the burst they were trying to avoid; queues, which absorb spikes and process them at a sustainable rate rather than passing the full spike straight through to a downstream API; controlled concurrency, capping how many requests to a given provider can be in flight at once regardless of how much work is waiting; batching, combining many small requests into fewer larger ones where the provider's API supports it; and caching, avoiding repeat calls for data that hasn't meaningfully changed.

None of this is exotic. What's notable is how often it's missing from the first version of an integration, because the first version was built and tested at a scale where none of it mattered — and rate limits, unlike most bugs, only reveal themselves once the product is already succeeding.

Chapter 8: Webhooks Change the Direction of Control

Most early integrations are built around a pull model: your system asks the provider a question — "what is the status of this record?" — and gets an answer on demand. You control the timing entirely.

Many mature integrations shift toward a push model, commonly implemented as webhooks: instead of your system asking, the provider tells you when something changes. This is more efficient — no polling, no wasted requests checking for changes that haven't happened — but it inverts who controls the interaction. Your system is no longer initiating; it's receiving, on the provider's schedule, and that shift introduces a set of questions that pull-based integrations never have to answer.

Was the event actually delivered? If your endpoint was down for even a minute, did the provider retry, or is that event simply lost? Was it delivered more than once — because most webhook systems retry on anything that looks like a failure, including cases where your endpoint actually processed the event successfully but the acknowledgment got lost on the way back? Was it delivered in the order it happened, or can network conditions and provider-side retries cause events to arrive out of sequence? Can a missed or corrupted event be replayed later? Is the event actually authentic, or could anyone who discovers your webhook URL send a forged one?

The practical answer to duplicate delivery is idempotent processing: designing the code that handles an incoming event so that receiving the same event twice produces the same end state as receiving it once, rather than a duplicated effect. If a "payment succeeded" webhook fires twice for the same payment, the correct behavior is to record it once — not to send the customer two receipts or apply two credits to their account. This usually means checking, before acting on an event, whether that specific event's unique identifier has already been processed.

Ordering deserves equal care, and it's easy to underestimate because it rarely shows up in initial testing, where events naturally arrive in the order they were generated. Consider a subscription lifecycle: subscription.updated, then subscription.canceled, then subscription.reactivated. If a system processes these strictly in the order it receives them, and they arrive out of order due to retries or network timing, the account's final state can end up wrong — reactivated when it should be canceled, or vice versa — even though every individual event was processed correctly. The fix is usually to make event handling state-aware rather than purely sequential: check the event's own timestamp or a monotonic version number against the current record state, and discard events that are older than what's already been applied, rather than blindly applying whatever arrives most recently.

None of this is a claim about how any specific named provider's webhook system behaves — that varies by provider and by API version, and should be verified against current documentation rather than assumed. The underlying architectural lesson holds regardless of vendor: once control shifts from your system pulling to a provider pushing, your code has to defend itself against duplication, delay, and disorder as a matter of course, not as an exceptional edge case.

Chapter 9: The Provider Changed the API

Integration work is often budgeted as a one-time cost: build the connection, ship it, move on. That framing is wrong, and the error only becomes visible over a timeline longer than most sprint planning ever looks.

Day 0: the integration is built, tested, and shipped. It works.

Month 8: the provider releases a new API version with additional fields and slightly different behavior. Nothing forces an upgrade yet, so nobody does.

Month 18: the provider announces that the version your integration was built against is being deprecated, with a defined sunset date some months out. This announcement typically arrives by email or changelog, not as an error in your application — meaning it's entirely possible for it to go unnoticed by whoever isn't specifically watching that provider's release notes.

Month 24: the sunset date arrives, or a specific behavior quietly changes even without a formal deprecation — a default value shifts, an endpoint starts requiring a field it previously made optional, a previously permissive rate limit tightens. The integration that worked fine yesterday breaks today, not because your code changed, but because the ground it was standing on did.

This pattern is common enough to deserve a name: think of it as a dependency maintenance tax — a conceptual shorthand for the ongoing engineering effort that every external dependency quietly generates over its lifetime, not a formal accounting term with a fixed dollar value. That tax includes monitoring each provider's release notes and changelogs, testing against new API versions before they become mandatory, updating SDK dependencies as they're released, handling deprecations before they become emergencies, rotating credentials on schedule, and reviewing security-relevant changes a provider makes to its authentication model.

None of this shows up as a feature on a roadmap. It's easy to defer, because deferring it has no visible cost until the day the deferred work becomes an incident. The practical implication is that the true cost of a dependency isn't measured at integration time — it's the sum of the initial build plus this recurring tax, indefinitely, for as long as the dependency exists. A team with twenty external dependencies has, in effect, subscribed to monitoring twenty other companies' engineering roadmaps for changes that could quietly become their own production incidents.

Chapter 10: Who Owns the Roadmap?

There's a strategic version of this problem that sits above the technical one, and it belongs in front of founders and product leaders, not just engineers.

What happens when your own product roadmap depends on functionality that another company controls, and that company changes its mind?

A feature might be built around a specific permission a provider's API currently exposes — access to a customer's calendar data, say — and the provider later restricts or removes that permission for privacy or policy reasons unrelated to your product entirely. A workflow might depend on a data field a provider currently returns, and a future API version drops it. A business model might assume a provider's current pricing, and the provider restructures its tiers in a way that changes your unit economics overnight. A growth plan might require a higher usage quota, and the provider ties that quota to a partnership tier, an approval process, or a contract you don't yet have.

None of this is unusual or alarming on its own — it's ordinary vendor dependency, the same category of risk any business accepts when it relies on a supplier it doesn't own. What's useful is separating two dimensions that are easy to conflate.

Technical dependency describes how deeply your code is integrated with a provider's specific interface — how much of your data model, business logic, and customer-facing behavior assumes that provider's particular way of doing things.

Business dependency describes how much your commercial success relies on that provider's continued cooperation, pricing, and policy choices, independent of how the code is written.

These two dimensions don't move together. An API that's technically simple to swap — a straightforward transactional email provider, for instance — may still carry meaningful business risk if switching means renegotiating deliverability reputation from scratch. Conversely, a deeply and expensively integrated platform, like a major cloud provider, may carry comparatively low business risk precisely because its commercial incentives are stable and its long-term viability is not in serious question. Evaluating a vendor only on integration effort, or only on brand reputation, misses half of what actually matters.

Chapter 11: The Replaceability Test

Not every dependency deserves the same level of defensive investment, and building for portability everywhere is its own form of waste. What's useful is a consistent set of questions to ask about any dependency that matters enough to warrant the exercise — a replaceability test.

How much proprietary, provider-specific behavior does the integration actually rely on, beyond generic request-response calls? How much data is stored in a format or structure that's specific to this provider's model of the world? How much business logic silently assumes this provider's particular way of representing things — its object model, its terminology, its constraints? How much of that leaks into customer-facing behavior, such that customers themselves would notice a switch? How many internal systems consume this integration, directly or indirectly, and would need to change together? Is there an abstraction layer between your application and the provider's specific API — and, separately, do you actually need one? How long would a real migration realistically take, accounting for testing, data movement, and customer communication, not just the code change? Could both providers run in parallel during a transition, or does the nature of the data make that impossible? Can historical data actually be exported and moved, or does it live in a format only the original provider can interpret? Can customers keep operating normally throughout a migration, or does it require a maintenance window that interrupts them?

A crucial and easy-to-miss point: none of this is an argument for wrapping every external API in an abstraction layer "just in case." Abstraction has its own real cost — additional code to maintain, a layer that has to be kept current with a provider's evolving capabilities, and a risk of building the wrong abstraction before the team has enough experience with the real API to know what actually varies. For dependencies that are unlikely to change and cheap to replace even without preparation, direct integration is often the correct engineering choice, not a shortcut. The replaceability test isn't a checklist that always ends in "add an abstraction layer." It's a way of deciding, deliberately, whether a specific dependency is both critical enough and volatile enough that paying for portability now is worth more than the flexibility it costs.

Chapter 12: The Fallback Myth

"Just have a fallback provider" is common advice, and it understates how hard fallback actually is for most categories of dependency, because it treats every API as functionally interchangeable plumbing.

Email is close to the easy end of the spectrum. Sending a transactional message through Provider A or Provider B is largely equivalent from the application's point of view, provided both are configured and both accounts exist ahead of time — though even here, deliverability reputation is built over time per sending domain, so an emergency switch mid-incident may see a temporary dip in delivery rates as the new provider's infrastructure is less "known" to receiving mail servers.

Authentication is considerably harder. A fallback identity provider would need to somehow already have — or be able to instantly reconstruct — the same session state, user records, and password hashes as the primary. In practice, this usually isn't a live fallback at all; it's a disaster-recovery plan involving pre-provisioned redundancy, not something toggled on mid-incident.

Payments sit in an even harder category, because switching processors mid-outage carries financial and compliance weight beyond the technical integration — reconciliation, dispute handling, and existing customer payment methods on file that may not transfer cleanly to a second processor without customers re-entering their cards.

AI inference occupies a more nuanced middle ground. Routing between model providers is often technically feasible, especially for products that treat the model as replaceable infrastructure behind their own prompts. But model behavior differs meaningfully between providers and even between versions of the same provider's models, so a fallback model may produce different — not just slower — output, which matters more for some use cases than others.

Maps, search, and fraud services tend to be provider-specific in ways that are easy to underestimate: different schemas, different semantics for the same concept, and materially different quality or coverage, especially for maps and fraud scoring, where the underlying data itself — not just the API shape — differs between vendors.

It's useful to sort dependencies along a rough spectrum rather than a binary "has fallback or doesn't":

  • Easy-to-substitute capability — largely stateless, generic behavior, low switching cost (a basic transactional email provider, a generic CDN).
  • Moderately portable capability — some provider-specific behavior or data, but a migration is realistic with planning (an analytics platform, a support ticketing system).
  • Stateful dependency — the provider owns meaningful state about your customers that would need to be reconstructed or migrated (a CRM, a document storage system).
  • Deeply embedded dependency — provider-specific concepts are baked into your product's data model, business logic, and customer-facing behavior (an identity provider with years of session and permission history, a payment processor with years of subscription and dispute history).

The further right on that spectrum a dependency sits, the less "just add a fallback" means in practice, and the more it means "have a deliberate, tested, and probably slow migration plan" rather than an instant failover.

Chapter 13: Degraded Mode Is Often Better Than Fallback

Given how hard true fallback is for most critical dependencies, the more achievable and often more valuable question is different: when a dependency fails, what is the minimum useful version of the product that can stay available, even if it's not the full product?

If the AI summarization feature is unavailable, the manual workflow it was meant to speed up can remain: let the customer write their own summary, or read the source document directly, rather than blocking the whole action on AI availability. If analytics ingestion fails, the events don't need to be dropped — they can be queued locally and sent once the provider recovers, preserving the data even though the dashboards are temporarily stale. If email is down, the underlying action — the signup, the invitation, the password change — can still be accepted and recorded, with the notification queued to send once the provider is healthy again, rather than blocking the action on a channel the customer doesn't actually need to complete their task. If CRM sync fails, the sync task can be queued rather than lost, so sales visibility catches up once the provider recovers instead of silently missing the interaction forever. If the search provider is unavailable, showing a limited set of recently viewed or recently modified items, where that's an appropriate substitute, may keep a customer working instead of staring at a blank error page. If a recommendation engine fails, falling back to generic or popularity-based content is usually far less jarring than showing nothing at all.

This is degraded mode: deliberately defining, ahead of time, what a reduced but still-functional version of the product looks like for each critical dependency, rather than discovering the answer improvised, under pressure, during a live incident. A degraded product that customers can still mostly use is, in almost every case, a better outcome than one that fails outright — and it's dramatically easier to reason about calmly in a design review than to invent on the spot at 2 a.m.

Dependency Critical function Possible degraded mode Must be preserved Can wait
AI inference Document summarization Manual review workflow Access to source document The summary itself
Transactional email Password reset, invites Queue and send on recovery The underlying account action Delivery timing
CRM sync Sales visibility Queue sync task The interaction data Real-time dashboard accuracy
Analytics Experimentation, reporting Local event buffering Event data itself Dashboard freshness
Search index Content discovery Recent-items fallback Core navigation Full-text relevance
Recommendation engine Personalized content Generic/popular content Basic page functionality Personalization

Chapter 14: Circuit Breakers, Bulkheads, and Backpressure

Three related engineering patterns govern how a system behaves under dependency stress, and each has a plain-language analogy before it has a technical one.

A circuit breaker works the way an electrical circuit breaker does: when a dependency starts failing repeatedly, instead of letting every new customer request wait, time out, and fail against it one by one — wasting time and resources on calls that are very likely to fail anyway — the system "trips," stops sending traffic to that dependency for a period, and fails fast instead. This protects both the caller, which stops wasting resources on doomed requests, and the struggling dependency, which gets a period of reduced load that may help it recover rather than staying pinned under continued traffic. After a cooldown period, the breaker allows a small amount of traffic through to test whether the dependency has recovered before fully reopening.

A bulkhead borrows its name from ship design, where a hull is divided into separate watertight compartments so that flooding in one doesn't sink the whole vessel. In software, this means isolating the resources — connection pools, thread pools, queues — used for one dependency from the resources used for others, so that one dependency's slowdown doesn't consume every available resource in the application and starve unrelated features that have nothing to do with the failing dependency.

Backpressure is the mechanism that prevents incoming work from overwhelming a system's downstream capacity. Rather than accepting every request regardless of whether anything downstream can actually process it, a system with backpressure slows or rejects new work once it detects that a dependency can't keep up — a form of self-protection that trades some rejected requests now for avoiding a much larger backlog and cascading failure later.

Practically, these patterns show up as bounded queues that reject new work once full rather than growing without limit, connection pools sized to prevent one slow dependency from consuming every available connection, concurrency limits on how many simultaneous calls to a given provider are allowed, and deadlines and timeouts set deliberately rather than left at framework defaults, which are often far longer than any customer-facing interaction should reasonably wait. None of these patterns require exotic infrastructure — most are available in mainstream libraries and frameworks — but they require someone to have made the deliberate decision to use them, rather than assuming the default behavior of "wait until it responds or errors" is good enough.

Chapter 15: Dependency Criticality Is Not the Same as Dependency Count

A team that reduces its dependency count from twenty-five to eighteen has not necessarily reduced its risk. If the seven removed dependencies were low-impact and easy to recover from, and the eighteen remaining include two high-impact, hard-to-recover dependencies with no degraded mode defined, the product is arguably in worse shape than before, despite the smaller number.

A more useful classification plots dependencies along two axes: customer impact, how much of the customer experience breaks when the dependency fails, and recovery difficulty, how hard it is to detect, mitigate, and resolve that failure once it happens.

  • Low impact / easy recovery — a minor internal tool integration that can be disabled or swapped with little disruption.
  • Low impact / hard recovery — a deeply embedded internal system, like an old accounting integration, that would take real effort to replace but whose failure barely touches customers.
  • High impact / easy recovery — a customer-facing capability that fails visibly but has a fast, well-tested fallback or degraded mode.
  • High impact / hard recovery — the dependencies that deserve the most attention: authentication, payments, and core product capabilities where failure is both highly visible and slow to remediate.

Beyond the two-axis view, a handful of additional properties shape how a dependency should be treated: whether it's called synchronously on the customer's critical path or asynchronously in the background; whether it's stateful, holding data that must be reconciled, or stateless, where a failed call simply needs to be retried; whether it's realistically replaceable or effectively embedded; whether it's optional, enhancing the experience, or mandatory, gating it entirely; whether it's customer-facing, visible in the product itself, or purely internal; and whether it functions as a data processor, temporarily handling information on your behalf, or a data owner, holding the authoritative copy of something you'd need back in a migration.

Applying these properties to a few fictional examples produces a useful dependency profile without resorting to a fabricated numerical "reliability score" — pseudo-precision that implies more rigor than the underlying judgment actually has. The authentication provider, for instance, is synchronous, stateful, deeply embedded, mandatory, customer-facing, and a partial data owner (of sessions and identity records) — which places it squarely in the high-impact, hard-recovery quadrant, deserving proportionally serious investment in monitoring, degraded-mode planning, and incident readiness. The marketing analytics platform, by contrast, is asynchronous, largely stateless from the application's point of view, easily replaceable, optional, internal, and a data processor rather than owner — low impact, easy recovery, and reasonably left with lighter operational investment.

Chapter 16: The Dependency Budget

Every dependency, once added, consumes an ongoing share of the organization's finite capacity to pay attention — even a technically excellent one. It's worth naming this cost explicitly, as a dependency budget, while being clear up front: this is not an industry-standard metric and not a claim that there's a universal maximum number of external services a healthy company should have. It's a way of thinking, not a formula.

Every external dependency draws on operational complexity — one more system that has to be understood by whoever is debugging an incident at 3 a.m.; security review — one more set of credentials, scopes, and data flows that has to be assessed and periodically reassessed; maintenance attention — the dependency maintenance tax described earlier; monitoring — one more system whose health needs its own signal, not just an assumption that "the app is up, so it's probably fine"; testing — one more set of failure modes that ideally gets exercised deliberately rather than discovered live; incident response capacity — one more thing that can be the root cause on a bad day, competing for the same limited on-call attention as everything else; vendor management — contracts, pricing changes, support relationships; and architectural flexibility — every dependency narrows, at least slightly, the space of future changes that can be made without touching it.

None of this means dependencies are bad. It means the true cost of adding one is not just the implementation time it takes to wire up the first call — a cost that's usually small and often the only cost anyone actually estimates. The more complete question, worth asking deliberately before adding a new external service rather than after it's already load-bearing, has four parts: What capability are we buying by adding this? What internal engineering effort does it let us skip? What ongoing operational obligation does it create that didn't exist before? And how difficult would it realistically be to remove later, if it turned out to be the wrong choice? A team that asks this consistently doesn't necessarily end up with fewer dependencies — it ends up with dependencies it chose on purpose, understood the cost of, and planned around, rather than dependencies that accumulated as a byproduct of shipping fast.

Chapter 17: Testing a System You Do Not Control

This is where quality engineering enters the picture, and it enters through a genuinely hard problem rather than a generic reminder that "API testing is important." The fundamental difficulty is structural: your test environment cannot control the behavior of a system that belongs to someone else. Every other kind of testing assumes at least some control over the system under test. Third-party integration testing does not.

Several complementary layers address different parts of the problem, and none of them alone is sufficient.

Contract testing checks whether your application's assumptions about a provider's request and response format actually match what the provider expects and returns — catching cases where your integration silently drifted from the provider's real current contract, often because the provider changed something and nobody noticed.

Mocks and stubs simulate a provider's responses in a controlled way, which makes them valuable for testing your own logic in isolation, quickly and without depending on the real provider's availability during every test run. Their limitation is easy to state and easy to forget in practice: a mock can faithfully reproduce your team's current assumption about how the provider behaves, while the real provider does something else entirely — either because the assumption was wrong from the start, or because the provider's behavior changed after the mock was written and nobody updated it to match.

Sandbox environments, offered by many providers specifically for testing, are genuinely useful, but they are not always behaviorally identical to production — timing characteristics, rate limits, and edge-case behavior can differ, and any specific claim about how a particular provider's sandbox diverges from its production environment should be checked against that provider's own current documentation rather than assumed.

Integration testing against the real provider environment, where a provider makes this feasible without financial or operational risk, is often the most reliable way to validate actual current behavior, precisely because it removes the layer of assumption that mocks and sandboxes both still carry.

Fault injection deliberately introduces the failure modes described in earlier chapters — timeouts, malformed responses, delayed or duplicated webhooks, missing fields — into a test environment, so the system's behavior under those conditions is verified before they occur for real, rather than discovered live.

Replay testing, applied to event-driven integrations, resends previously captured events, safely and repeatedly, to confirm that processing logic is genuinely idempotent rather than merely appearing to be during a single pass.

Production observation — carefully instrumented, not reckless experimentation — recognizes that some dependency behavior, particularly around scale, timing, and real-world data variety, can only be fully understood once the system is running under genuine production conditions. This isn't an argument for skipping earlier testing layers; it's an acknowledgment that they don't capture everything, and that safe, well-instrumented observation of real behavior is itself a legitimate and necessary testing layer, not a fallback used only when the others were skipped.

Chapter 18: Test the Failure, Not Just the Success

Most integration test suites are heavily weighted toward the happy path: the request succeeds, the response looks as expected, the test passes. That's necessary, but it verifies only a fraction of the behaviors that actually determine whether an integration is safe in production.

For each meaningful external dependency, it's worth deliberately considering a broader matrix of scenarios: the request succeeds normally; the request times out entirely; the response is slow but eventually arrives; a 4xx client-error response comes back; a 5xx server-error response comes back; the request is rate-limited; the response is malformed or missing an expected field; a webhook callback arrives duplicated; a webhook callback arrives significantly delayed; an expected webhook callback never arrives at all; a response contains stale data; and the provider recovers normally after a prolonged outage, which has its own failure modes — a flood of queued retries all landing at once, for instance.

Scenario What it tests Common failure if untested
Request times out Timeout handling, retry logic Request hangs indefinitely, blocking a user or a queue
429 rate limited Backoff behavior Retry storm, cascading throttling
5xx server error Circuit breaker behavior Repeated failed calls consume resources
Malformed response Input validation Application crash or silent data corruption
Duplicate webhook Idempotency Duplicate business effect (double charge, double email)
Delayed webhook Ordering and staleness handling Stale state applied after a newer state
Missing webhook Reconciliation logic Permanently inconsistent state with no recovery path
Provider recovers after outage Retry-storm resilience A second, self-inflicted outage right after recovery

Not every integration justifies testing against every scenario in that list — that would be its own form of waste, disproportionate for a low-impact, easily-replaceable dependency. The selection should be risk-based, guided by the criticality classification from Chapter 15. The single most useful question to ask about any given dependency, more useful in practice than the abstract "does the integration work," is this: what would our customer actually experience if this provider behaved badly for the next thirty minutes? For a high-impact, hard-recovery dependency, that question deserves a concrete, tested answer — not a guess formed for the first time during a real incident.

Chapter 19: Observability Must Cross the Company Boundary

A standard monitoring dashboard can report every internal signal as green — CPU low, memory stable, database responsive, error rates normal — while customers experience a completely broken product, because the actual point of failure sits outside the boundary that internal monitoring was ever built to observe.

The clearest version of this: servers are healthy, the database is healthy, CPU utilization is low, but the authentication provider is unavailable. No customer can log in. The infrastructure dashboard shows every internal metric in the green. The product is, from a customer's point of view, entirely down. This is not a hypothetical edge case — it's one of the most common shapes an external-dependency incident takes, precisely because internal monitoring was designed to watch the systems a team built, not the systems it merely calls.

Closing that gap requires deliberately capturing dependency-level signals, not just internal ones: latency specifically attributable to each external call, not blended into overall response time; error rate broken out per dependency, not aggregated across the whole application; timeout rate, distinct from outright error rate, since the two point to different problems; the frequency of rate-limit or throttling responses; retry volume, since a spike there often precedes a visible outage by minutes; queue depth for anything running asynchronously against a dependency; webhook delivery delay, measured as the gap between when an event should have arrived and when it did; how often fallback logic is actually being invoked; and how often degraded mode is active, which is itself a meaningful signal even when the degraded mode is working exactly as designed.

The useful distinction to draw explicitly is between internal health — is our own infrastructure functioning correctly — and customer journey health — can a customer actually complete the thing they came to do, end to end, regardless of which system along the way might be the cause of a failure. These two are not the same measurement, and a monitoring strategy that only tracks the first will, sooner or later, report "all green" during an outage that every customer can see.

Chapter 20: Incident — 2:17 PM

What follows is a fictional, composite incident, constructed to illustrate a pattern rather than to describe any real event at any real company.

2:17 PM. Checkout conversion drops sharply on a live dashboard. Nobody has deployed anything in the last two hours.

2:19 PM. Application servers report normal health. CPU, memory, and error rates on the core API all look unremarkable.

2:22 PM. Error rates specifically on payment-related endpoints begin climbing, distinct from the overall error rate, which is still masked by the much larger volume of unrelated, healthy traffic.

2:25 PM. Dependency-level latency monitoring — the kind described in the previous chapter — shows a sharp rise in response time specifically from the payment provider. Nothing else has changed.

2:28 PM. Automatic retries, triggered by the earlier timeouts, begin adding load on top of an already-struggling dependency.

2:31 PM. A background queue that buffers payment confirmation processing begins growing, faster than it's draining.

From here, a mature team's investigation follows a fairly consistent pattern: check dependency-specific metrics first, since the internal-health dashboard has already been ruled uninformative; check the provider's public status page, while treating it as one data point rather than the definitive answer; review recent deployments on your own side, to rule out self-inflicted causes before assuming an external one; check the network path between your infrastructure and the provider's, in case the issue sits in transit rather than at either endpoint; check credentials and account configuration, in case something as mundane as an expired key or rotated secret is the actual root cause; check quotas and rate limits, in case elevated traffic — not a provider outage at all — triggered throttling; and review logs and traces specifically for the affected endpoints, tracing individual failed requests rather than only looking at aggregate rates.

The response, once the payment provider's degraded performance is confirmed as the proximate cause, typically involves several moves in parallel rather than a single fix: disabling nonessential calls that aren't required to complete the core transaction, to reduce load and simplify the failure surface; activating a pre-defined degraded mode if one exists for this dependency — accepting the checkout action and queuing final confirmation rather than blocking the customer entirely; deliberately reducing retry pressure, since retries at this moment are adding load to an already-struggling system rather than helping; communicating internally so support and leadership aren't discovering the incident secondhand from customers; preserving customer state carefully, so that whatever was in flight when the incident began can be reconciled correctly once the dependency recovers, rather than lost or duplicated; explicitly avoiding duplicate operations during the recovery, since a flood of retried requests hitting a recovering provider can reintroduce the very problem that's resolving; and monitoring the recovery itself, watching queue depth drain and latency normalize, rather than assuming the incident is over the moment the provider's own status improves.

The broader point this story is meant to illustrate: external dependency incidents are not purely technical events. They require operational readiness — a team that knows, ahead of time, what to check and in what order — as much as they require the underlying engineering patterns from earlier chapters. A team encountering this shape of incident for the first time, with no dependency-level monitoring and no degraded mode defined, spends most of its response time simply figuring out what's happening, rather than fixing it.

Chapter 21: Status Pages Are Useful, But They Are Not Your Monitoring System

Provider status pages are a genuinely useful signal, and nothing in this chapter is meant as a criticism of them. But it's worth being precise about what they actually tell you, because it's less than teams sometimes assume.

Your experience of a provider can diverge from what its status page reports for reasons that have nothing to do with the status page being wrong: your traffic may be served from a different region than the one currently affected; your specific account may have different configuration, quota, or plan-tier behavior than the general population being monitored; your integration may be calling a specific endpoint that's degraded while most of the provider's surface is fine; the network path between your infrastructure and theirs may have an independent issue that has nothing to do with the provider's own systems; you may be on an older API version with different characteristics than the current one being actively monitored; or your account configuration may itself be the actual source of the problem, unrelated to the provider's overall health at all.

None of this means status pages aren't worth checking — they're a fast, low-effort first signal, and they're often right. It means they're one input, not a complete monitoring system, and specifically not a substitute for observing your own actual interaction with the provider, which is the only signal that reflects exactly what your customers are experiencing, filtered through your specific account, region, and endpoint usage rather than the provider's aggregate view of itself.

Chapter 22: Third-Party Risk Is Also Security and Data Risk

Every external integration is also a data-sharing relationship and a credential-management responsibility, even when the primary purpose of adding it had nothing to do with security.

That relationship touches API keys and how they're stored, rotated, and scoped; OAuth tokens and what happens when they expire or are revoked; the permissions and scopes actually granted to each integration, as opposed to the broader set that was simply available and convenient to request at setup time; the process, if any, for rotating secrets on a schedule rather than only when a breach forces the issue; webhook signature verification, which confirms that an incoming webhook actually originated from the provider it claims to, rather than from anyone who discovered the endpoint URL; what data is actually shared with the provider, as distinct from what could theoretically be shared; how sensitive that shared data is; and what level of access the provider's integration actually has into your systems and data.

The guiding principle worth applying deliberately, rather than defaulting to whatever scope was easiest to configure during initial setup, is least privilege: does this integration genuinely need access to the full customer dataset, or only to a specific subset relevant to its function? A CRM sync integration that only needs contact and company records arguably shouldn't also have read access to billing history, even if requesting broader access was the path of least resistance when the integration was first configured.

This is also where the dependency inventory from earlier chapters earns its keep as a security tool, not just an operational one, by making two questions answerable for any given provider at any time: what data does this provider actually receive, and what can this provider actually do with the access it's been granted. Without a maintained inventory, both questions tend to be answerable only by someone tracking down whoever originally built the integration — if that person is still around, and if they remember. This chapter is deliberately not a comprehensive security guide; it's a reminder that the dependency-risk lens applied throughout this article and the security lens applied by a separate discipline are looking at largely the same set of relationships, and treating them as unrelated concerns leaves gaps neither lens catches alone.

Chapter 23: When Your API Providers Depend on the Same Cloud

This is one of the less obvious forms of dependency risk, and it's easy to miss because it doesn't show up in any single provider's status page or documentation.

Suppose a product runs its own infrastructure on a major cloud platform. Several of its external providers — the identity service, the AI inference API, perhaps the analytics platform — may themselves also run on the same underlying cloud, or share other common infrastructure, such as a widely used DNS provider or CDN. Even when five separate, independently branded vendors are being used, their actual infrastructure risk may not be five independent things.

This is worth calling correlated dependency risk: the possibility that dependencies which appear diversified at the vendor-relationship level are not actually diversified at the infrastructure level, because they ultimately share a common point of failure several layers down. It's important not to speculate about which specific companies share which specific infrastructure without authoritative sourcing — that kind of claim changes over time and varies by provider, region, and specific service, and getting it wrong is worse than leaving it unstated. The architectural point stands regardless of which specific vendors are involved: using five different named providers does not automatically mean five genuinely independent failure domains, and for a product's most business-critical dependencies, it may be worth deliberately investigating — through each provider's own published infrastructure documentation — whether meaningful correlation exists, rather than assuming that vendor diversity is the same thing as infrastructure diversity. For most dependencies, this level of scrutiny isn't worth the effort. For the small number that genuinely sit in the high-impact, hard-recovery quadrant from Chapter 15, it's a reasonable question to ask at least once.

Chapter 24: When Multi-Provider Architecture Is Worth It

"Always use multiple vendors for redundancy" is advice that sounds prudent and is frequently wrong in practice, because it ignores the very real cost side of the trade-off.

Running two providers for the same capability means additional engineering work to integrate and maintain both; a data synchronization problem, since the two providers now need to agree, or be reconciled, on the same underlying state; an abstraction layer to route between them, which carries the costs discussed in Chapter 11; inconsistent behavior between the two, since even functionally similar providers rarely behave identically in every edge case; roughly double the testing surface; additional operational complexity in debugging, since an incident might now involve figuring out which of two providers is responsible; pricing for two active relationships instead of one; and a debugging environment that's harder to reason about, since failures can now originate from either provider or from the routing logic between them.

Multi-provider architecture is more clearly justified when a capability is extremely business-critical and a failure would have serious financial or reputational impact; when the dependency is realistically portable, per the replaceability test in Chapter 11, so that the redundancy is actually usable rather than theoretical; when there's a known and specific concentration risk — a genuine, sourced reason to believe the primary provider is a likely single point of failure; or when a regulatory or contractual requirement specifically mandates it.

It's more often rational to accept single-provider risk for a capability when the provider is highly reliable in practice, the dependency is difficult or expensive to make genuinely portable, the capability isn't among the handful that would actually stop the business if it failed, and a well-designed degraded mode — the pattern from Chapter 13 — already provides most of the protection a second provider would have offered, at a fraction of the ongoing cost. The throughline worth restating explicitly, because it cuts against a lot of generic advice: resilience engineering is about making deliberate trade-offs case by case, not about maximizing redundancy everywhere it's technically possible.

Chapter 25: The External Dependency Review

This is the central practical framework of the article — a structured way to evaluate any external dependency that matters enough to warrant the time, built from the concepts developed in every prior chapter.

1. Purpose. What specific capability does this dependency provide? A vague answer here — "it handles email" rather than "it sends transactional signup, password reset, and billing emails" — usually signals that nobody has actually mapped what the dependency does across the product. A mature answer names the specific customer journeys it touches.

2. Criticality. What customer journey actually fails, and how, if this dependency is unavailable? A risky answer assumes it's "probably fine" without having traced the failure. A mature answer can name the specific broken customer experience, drawn from an exercise like the simulations in Chapter 4.

3. Request path. Is the dependency called synchronously, blocking a customer-facing response, or asynchronously, in the background? A risky answer doesn't know. A mature answer can point to the specific code path and explain why it's synchronous, if it is — not just that it happens to be.

4. State. Does the provider hold state that matters — customer data, session information, transaction history — or is every call effectively stateless and independently retryable? A risky answer hasn't considered this. A mature answer understands what would be lost, and how hard it would be to reconstruct, if the provider relationship ended.

5. Failure behavior. What actually happens on a timeout, an error, a slowdown, or a partial failure? A risky answer is "we haven't tested that." A mature answer has run something like the fault-injection testing from Chapter 17.

6. Retry strategy. Can requests to this dependency be safely retried without risking a duplicated effect, like a double charge or a double email? A risky answer retries blindly. A mature answer has considered idempotency explicitly, per Chapter 5 and Chapter 8.

7. Rate limits and quotas. How would a 10x increase in usage — from growth, a marketing spike, or a large customer import — affect this integration? A risky answer hasn't modeled it. A mature answer knows the current limits and has a plan, such as batching or queuing, for approaching them.

8. Change management. How are the provider's API version changes, deprecations, and SDK updates tracked? A risky answer is "we'd probably notice when something breaks." A mature answer has someone or something actively monitoring the provider's changelog.

9. Observability. Can the team see this provider's latency and error rate independently, distinct from overall application health? A risky answer relies solely on the provider's public status page. A mature answer has dependency-specific monitoring, per Chapter 19.

10. Degraded mode. Can the product continue to provide partial value if this dependency fails? A risky answer is "the feature just breaks." A mature answer has a defined degraded mode, per Chapter 13.

11. Replaceability. How difficult would migrating away from this provider actually be? A risky answer is an untested guess. A mature answer has walked through the replaceability test in Chapter 11.

12. Data and security. What information does this provider receive, and what permissions does it hold? A risky answer is "probably everything, we didn't scope it tightly." A mature answer reflects a deliberate least-privilege decision, per Chapter 22.

13. Testability. Can failure conditions for this dependency be simulated safely, without touching production or real customer data? A risky answer is "only by waiting for it to actually happen." A mature answer has a fault-injection or sandbox-based approach, per Chapter 17.

14. Incident ownership. When this integration fails, who is actually responsible for responding? A risky answer is "whoever notices first." A mature answer names a specific owner, with a documented path for escalation.

This is deliberately structured as a decision-making tool, not a compliance checklist to be filled out once and filed away. Its value comes from being revisited periodically for the dependencies that matter most, not from being completed exhaustively for every dependency exactly once.

Chapter 26: Create a Dependency Register

Running the External Dependency Review once, in a workshop, produces a useful conversation. Turning its output into a maintained register is what makes the conversation durable rather than a one-time exercise that fades from memory within a quarter.

A lightweight register, kept in whatever tool a team already uses for documentation, needs relatively few columns to be genuinely useful:

Column Why it matters
Dependency Clear, consistent naming — not everyone's shorthand for the same thing
Purpose What it actually does, in terms of customer journeys, not just a category label
Owner A specific person or team, not "the backend team" in general
Criticality Drawn from the impact/recovery classification in Chapter 15
Synchronous? Whether it sits on a customer-facing critical path
Stateful? Whether the provider holds data that matters
Failure mode What's actually known about how it fails, from testing or past incidents
Fallback / degraded mode What exists today, if anything
Monitoring Where its dependency-specific signals live
API version The specific version currently integrated against
Data shared What information the provider receives
Last reviewed When the External Dependency Review was last run for this entry

The single most valuable column, in practice, is often the least glamorous one: owner. "We use this provider" is not, by itself, useful information during an incident. What's useful is knowing, without having to ask around, exactly where it's used in the codebase, which specific customer journeys break if it fails, how its health is being monitored, and who is responsible for keeping the integration current as the provider's own API evolves. A register without clear ownership tends to decay quickly — accurate on the day it's created, and steadily less trustworthy after that, as new integrations get added informally and existing ones drift from what's documented.

Chapter 27: Five Questions for Founders

The full technical depth of the preceding chapters isn't the right level of detail for every audience, and a founder or non-engineering executive doesn't need to understand retry algorithms or webhook ordering to grasp the risk that matters most to them. Five questions carry most of the strategic weight.

Which external company can currently stop our core customer journey? Not which vendor is theoretically important, but which one, if it went down right now, would prevent customers from completing the single action the business depends on most.

Which dependency could fail silently while our own infrastructure still looks completely healthy? This is the observability gap from Chapter 19, and it's worth a founder's direct attention because it's exactly the kind of failure that goes unnoticed the longest — nobody's dashboard is red, so nobody's looking.

Which provider would be hardest to replace, and have we ever actually tested that assumption? Many teams have an intuitive sense of which dependency would be painful to migrate away from, but have never walked through the replaceability test in Chapter 11 to confirm whether that intuition is accurate or simply untested.

Which external service sits directly in our customer's critical path but doesn't actually need to? This is the timeout-cascade question from Chapter 6, applied at the level a founder can act on: are there dependencies currently blocking a customer's experience for no reason other than that's how the integration happened to be built the first time?

What does our product actually look like when a major dependency is unavailable — have we ever seen it, even in a test? This is the degraded-mode question from Chapter 13, and the honest answer for many products is that nobody has ever actually seen what the degraded version looks like, because it was never deliberately built — it was simply whatever the application happens to do when a call fails, which is rarely the same thing as a deliberately designed fallback.

A founder doesn't need to know how exponential backoff works to ask these five questions in a leadership meeting. What they need is an accurate sense of where dependency risk is concentrated, so that decisions about where to invest engineering attention are made deliberately rather than by default.

You Own the Experience, Even When You Don't Own the System

Maria never learned any of this, and she never needed to. From where she sat, Fieldnote was one product that either worked or didn't. It worked. She upgraded, invited her team, and moved on with her day.

Behind that experience were roughly twenty companies and systems, coordinated — mostly invisibly, and mostly successfully — by an engineering team that understood something worth restating plainly: outsourcing a capability does not outsource responsibility for the outcome that capability produces.

External services are, without qualification, one of the reasons a small team can build something this sophisticated this quickly. A payments API, an identity provider, an AI inference endpoint, and a transactional email service together replace what would once have required specialized internal teams for each. That is a genuine and durable competitive advantage, not a shortcut to be embarrassed about or a risk to be engineered away entirely.

But the customer's experience of "does this product work" doesn't distinguish between a bug in your own code and a slow response from a provider you've never spoken to. You may not control the payment network, the identity provider, the cloud platform, the messaging service, the AI model, or the CRM API sitting behind your product. Customers will still experience whatever happens as your product, working or not working, because from where they sit, it is your product — the twenty companies underneath it are simply invisible, exactly as they're supposed to be, right up until one of them isn't.

Recent posts

September 4, 2026
Saga Compensation Testing: The Rollback No One Checks
September 4, 2026
Post-Acquisition Technical Integration: The First 100 Days
September 4, 2026
Why Coding Interviews Don't Predict Software Quality