CodeOath
← All posts
Architecture & Patterns70 min total · 17 parts

Microservices vs. Monolith: Architecture Patterns and Trade-offs

Contents — Part 4 of 17: What It Actually Costs You
Part 4 of 17 · ~1 min

What It Actually Costs You

  • A function call becomes a network call. Slower by orders of magnitude (microseconds become milliseconds, at best), and now it can fail in ways a function call simply can't — a timeout, the other service being down, a partial success where some of a multi-step operation completed and some didn't. Code that used to be a guaranteed, synchronous call now needs retry logic, timeout handling, and a plan for "what if this never comes back."
  • No more cross-service transactions. A monolith's single database can wrap "debit this account, credit that one" in one ACID transaction, guaranteeing both happen or neither does. Across services with separate databases, that guarantee is gone — you're into eventual consistency and patterns like the Saga pattern (below) instead of a guarantee that both operations succeed together.
  • Distributed debugging. A single user request might now touch five services — tracing what actually happened requires centralized logging and distributed tracing (correlation IDs threaded through every call), infrastructure a monolith never needed because a stack trace inside one process was always enough.
  • Operational overhead. Five services means five things to deploy, monitor, version, and keep patched, versus one — each with its own CI pipeline (see CI/CD Pipelines Explained), its own container image (see Docker Fundamentals), and its own on-call surface area.
  • Testing gets harder. A monolith's integration tests can spin up one process and test real interactions between modules directly. Testing a real interaction between two services now means running both (or convincingly faking one), and a bug that only reproduces when two specific service versions interact a specific way is a category of bug a monolith doesn't have at all.