exanubes

Integration strategy: REST API

Integrating services in a distributed system via a REST API is probably the most organic solution there is. HTTP works really well when one service needs a clear, immediate answer from another. Its simple request->response model and widespread support make it a strong choice for read operations and situations where the caller genuinely can’t move forward without that result. Problems start to creep in when that’s the only integration strategy we use for every use case in our project, because there is no one size fits all solution. Everything has trade-offs.

Synchronous by design

REST forces both sides of the interaction to be operational at the same time. If the downstream service is slow, degraded or unavailable, the caller inhertis the failure. Each additional hop in the call chain amplifies tail latency and failure propagation. Temporal coupling between bounded contexts is the core issue with this approach. Your domain workflow can fail not because there’s an issue in the logic, but because another team’s service didn’t answer fast enough.

The false synchrony trap

REST APIs often hide asynchronous processing behind synchronous interfaces, creating the illusion of immediate completion. Teams build “pseudo-sync” APIs that return 202 Accepted with polling endpoints, or respond immediately while queuing work for later processing. This creates the worst of both worlds: you inherit all the complexity of synchronous integration (timeouts, retries, error handling) while losing the predictability of true synchronous behavior.

The caller can’t trust that a 200 OK means the work is actually done, but they’re stuck with synchronous failure modes and can’t leverage async patterns for reliability. You end up building distributed systems that are neither synchronous, nor properly asynchronous.

Data contracts leak across context boundaries

Teams tend to overexpose internal structures because it’s easy, then clients start relying on those details, and suddenly multiple contexts are bound to a contract that no one wants to change. Someone once said, that no matter how many properties an API exposes, with enough time, each property will have clients relying on it.

REST makes it very easy to accidentally smuggle another context’s language and structure into your own. Over time, this erodes autonomy.

Request->Response thinking shapes the system

HTTP encourages imperative, CRUD-style integration:

  • I call you, give me the data
  • I update this, you store it
  • I change this resource

This fosters anemic integrations between contexts: procedural interactions instead of explicit domain behavior. REST endpoints often express operations rather than intent:

// Anemic: exposing data operations
PUT /order/123/status { "status": "confirmed" }
POST /inventory/789/quantity { "delta": -5 }

// Domain-driven: expressing business intent
POST /orders/123/confirm
POST /inventory/789/reserve-items { "items": [...] }

The anemic approach leaks implementation details and forces clients to understand your data model. The domain-driven approach expresses clear business operations, even within HTTP constraints.

As a result, anemic REST APIs make the distributed system look more like a remote database instead of a cohesive system.

Operational overhead and the hidden cost

Each HTTP integration point requires its own reliability infrastructure:

  • timeouts
  • retries
  • backoff strategies
  • circuit breakers
  • load shedding
  • distributed tracing
  • correlation IDs
  • SLIs and SLOs per endpoint (Service Level Indicators/Objectives)

The operational burden multiplies with every REST dependency. You solve the same reliability problems repeatedly at each integration boundary, rather than solving them once at the infrastructure level. Every endpoint becomes a potential failure mode to monitor, alert on, and debug in production.

Without this operational scaffolding, synchronous HTTP is a reliability hazard, and even with all this infrastructure, you still can’t escape the inherent latency and dependency chain amplification.

Scalability Pressure

Because every call is synchronous, the provider has to absorb real-time load. If three upstream services all rely on a single downstream service, that downstream service becomes a choke point. Horizontal scaling helps, but only up to the point where cost and complexity catch up. Async patterns smooth traffic over time, but REST does not.

Transactions

There is no real transaction boundary in HTTP integration. At best, it creates an illusion of coordination in multi-service workflows. Once boundaries cross service lines:

  • No ACID
  • No rollback

You could use a pattern like Saga to compensate but more commonly the system allows for partial failures leaking into the user experience. REST does nothing to help remedy this.

Versioning

Easier said than done. I don’t think I’ve ever been on a project that had intuitive and easy to understand versioning practices. It usually ends up somewhere along the lines of:

  • Consumers never upgrade
  • Old endpoints live forever
  • Payloads must remian backward compatible
  • Quick fixes become permanent
  • Contract negotiation becomes political

All the overhead that comes with http are visible here. After being around the block for so long, http has a very long and expensive compitability tail and so do versioned APIs.

Where REST works well

REST is definitely not the villain here. It’s excellent when the use case fits the model:

  • Queries
  • External APIs
  • Public-facing boundaries
  • Low-coupling read scenarios
  • Situations where immediate results are required

The issue is not REST, it’s using it for domain coupling that requires autonomy

Summary

REST/HTTP excels at simple, synchronous interactions where immediate responses are genuinely required: queries, external APIs, and consumer-driven integrations. It becomes problematic when used as the default integration strategy for cross-domain workflows that require autonomy and temporal independence.

The issue isn’t REST itself, but using it for domain coupling that demands loose coupling and operational independence over time.