Constrange

The hidden tax of distributed systems

Latency, retries, consistency, failure modes, observability, and operational complexity that appear after a system is split apart.

Monoliths are unfashionable. Splitting a system into services promises independent deployment, team autonomy, and scale where it matters. The pitch is real. So is the invoice. Distributed systems charge a tax — not once at migration, but continuously in latency, retries, consistency trade-offs, failure modes, and the operational work of understanding what happened when nobody owns the whole story.

The tax is easy to underquote because it does not appear on the migration plan slide. It appears in p99 latency that nobody predicted. In duplicate charges because idempotency was 'phase two'. In on-call rotations that triple because three services each page for the same customer-visible failure. In post-mortems that end with 'we need better observability' — which is another way of saying the system became harder to reason about.

Constrange does not argue against distribution. It argues against distribution without a honest bill. If the tax is not priced into architecture, staffing, and customer commitments, the system will feel fast in demos and fragile in production.

Latency is no longer a function call

Inside a monolith, latency is mostly CPU, memory, and I/O on one machine. Across services, latency is network, serialization, queue depth, retry backoff, and the slowest dependency in the chain — often one nobody remembered when drawing the diagram. Each hop adds tail risk. Tails compound.

Service chain with latency accumulating at each hop and retry loops amplifying load
The user sees one action. The system performs many networked bets.
Latency sources after the split
Monolith assumptionDistributed reality
One process boundaryMany network round trips per request
Shared memory for readsCache coherence and stale reads
Single deployment unitVersion skew across services
One log streamCorrelated traces or guesswork
Fail fast in-processTimeout chains and retry storms

Retries are not free reliability

Distributed systems assume failure. Retries feel like the responsible response. They also multiply load during partial outages, reorder events, and mask root cause until the downstream service is fully dead instead of merely struggling. A retry policy written for convenience becomes a distributed denial-of-service your own clients run against you.

The tax here is operational: you must design idempotency, backoff, circuit breaking, and bulkheads as seriously as you design APIs. Teams that skip this pay in incident frequency and in customer trust when the same payment posts twice.

  • At-least-once delivery without idempotent handlers
  • Retry storms during partial degradation
  • No distinction between transient and permanent failures
  • Client and server retry policies fighting each other
  • Success metrics that ignore duplicate side effects

Consistency becomes a product decision

Strong consistency is expensive in distributed systems — not morally, operationally. Eventual consistency is often the right engineering choice. It is also a product choice customers may not have been told about. 'Eventually' without a defined bound is a promise nobody can support when finance calls about mismatched balances.

The hidden tax is conversation: product, finance, operations, and engineering must agree what stale data means, how long it may persist, and what the user sees while catching up. Monoliths hide this inside a transaction. Services export it as behaviour.

  1. Read-your-writes violated across services
  2. Cross-service transactions replaced by sagas nobody tested under failure
  3. Reconciliation jobs that become the real system of record
  4. UI showing success before downstream confirmation
  5. Audit asking which number was true at 14:32

Failure modes multiply faster than services

Partial failure is the default

Monoliths fail mostly as a unit — painfully, but visibly. Distributed systems fail partially: inventory reserved, payment captured, notification lost. Each service logs success. The customer experience is failure. Debugging requires reconstructing a story from fragments across teams, time zones, and log retention policies.

Failure shapes worth designing for
Failure modeTypical symptom
Network partitionSplit brain; conflicting writes
Slow dependencyTimeout cascades; thread pool exhaustion
Duplicate deliveryDouble charge; duplicate shipment
Out-of-order eventsState machines in impossible states
Poison messageQueue blocked; silent consumer stall

Observability becomes architecture

Three dashboards per service is not observability. It is decoration unless traces, logs, and metrics share identifiers, retention, and ownership. The tax of distribution is paid monthly in engineer hours correlating IDs across systems — or in incidents that close as 'unknown' because nobody could reconstruct causality.

Invest early in correlation, semantic conventions, and runbooks that span services. Otherwise observability budget becomes headcount budget with worse morale.

“You do not pay for microservices in licences. You pay in the hours spent proving what happened across boundaries that did not exist before the split.”Constrange

Operational complexity is headcount

Each service adds deployment pipelines, secrets, certificates, capacity planning, on-call rotations, and dependency upgrades. Autonomy for teams can reduce coordination cost. It can also duplicate platform work until four teams maintain four slightly different Kafka clients. The tax is real even when every team is competent.

  • Platform team becomes bottleneck for 'standard' patterns
  • Security patches multiplied by service count
  • Local optimisations that break global invariants
  • Integration tests that never run in CI because they are 'too hard'
  • Documentation that rots per repo instead of per system

When distribution is worth the tax

Honest triggers for the split

Distribution earns its cost when boundaries match real constraints: independent scale for a hot path, regulatory isolation, team ownership that aligns with business capability, or technology diversity that cannot live in one deployable unit. It does not earn its cost when the goal is slide aesthetics, resume keywords, or avoiding a difficult refactor inside one codebase.

A modular monolith with clear module boundaries often delivers most autonomy benefits without most network taxes. The industry forgot this because monolith became a synonym for mess. Mess is not the same as unified deployment.

Pricing the tax before you split

  1. Model p99 latency for the full user journey, not per service.
  2. Define consistency guarantees in language finance and product accept.
  3. Design idempotency and sagas before go-live, not after duplicate incidents.
  4. Budget observability and on-call as part of service count, not overhead.
  5. Run a game day that kills one dependency and measures blast radius.

If the exercise reveals that customer outcomes require synchronous coupling anyway, you have learned something cheaply. Many programmes learn it expensively at scale.

Reducing the bill without pretending it away

Consolidate services that always change together. Prefer asynchronous boundaries only where asynchrony matches the business. Invest in shared platform primitives so teams do not reinvent retries. Keep cross-service transactions rare and visible. Measure end-to-end outcomes, not service health alone.

The goal is not to avoid distribution. It is to pay the tax deliberately — with staffing, design, and customer expectations aligned — instead of discovering the invoice in production at month-end volume.

Patterns that predict an expensive split

Programmes that split early often share a shape: the monolith was genuinely messy, leadership wanted visible progress, and network costs were described as 'engineering overhead'. Six months in, the same business rules span five deployables, but now require choreography. The mess did not disappear. It gained HTTP status codes.

Another pattern: the split followed the org chart, not the transaction. Each director got a service. Customers still experience one journey. Incidents still require one war room. The tax arrived; the autonomy did not.

  • Split before load testing the monolith's honest bottleneck
  • No correlation ID standard before service two shipped
  • Shared library version drift treated as 'team freedom'
  • Product told 'it is async' without a user-visible definition
  • Platform team sized for one cluster, not for N pipelines

Questions for your next platform review

  • What is our all-in p99 for the customer journey after the split?
  • Which consistency guarantees have we promised without defining 'eventually'?
  • What happens to load when all clients retry at once?
  • Can on-call reconstruct causality across services in one hour?
  • Would a modular monolith achieve the same outcome with less tax?

Distributed systems are not wrong. Unpriced distribution is. Name the tax before you split — or operations will name it for you, loudly, at the wrong time.

The organisations that distribute well do not pretend the tax is zero. They budget latency, staff observability, write down consistency, and rehearse failure before customers fund the lesson. That discipline is architecture — not pessimism.

Frequently asked questions

Is the distributed tax always higher than a monolith?

Not always — but it is almost always underestimated. Compare end-to-end outcomes, not box count.

Can better tooling eliminate the tax?

Tooling reduces toil; it does not remove network, partial failure, or consistency trade-offs.

When is eventual consistency acceptable?

When product, finance, and operations agree on bounds and user-visible behaviour — in writing.

How do we know we split too early?

Frequent cross-service incidents for one user action, rising p99, and sagas nobody trusts are signals.

Share

LinkedInXWhatsAppFacebookRedditBlueskyEmail
Keep reading

Related posts