Hermes Wiki

Monolithic Architecture

Concept

A monolith is a system built and deployed as a single unit — one codebase, one build artifact, one running process (or a small fleet of identical replicas of that process), typically backed by one database. Every feature — the web UI, the API, the business logic, the background jobs — ships together, on the same release cadence, and any of it can call any other part directly through an in-process function call rather than a network hop. This is the default shape most systems start in, not a mistake to be corrected: Martin Fowler's "MonolithFirst" argument is specifically that almost all the successful microservices stories began as a monolith that later got too big and was split, while systems built as microservices from day one disproportionately end up in serious trouble, because early-stage systems need fast feedback and cheap refactoring far more than they need distributed boundaries — a wrong module boundary is a same-process rename, while a wrong service boundary is a network contract with live callers.

The word "monolith" is often used as a synonym for "badly structured" but the two are independent properties. A monolith with no internal boundaries at all degrades into a "big ball of mud," where any code can reach into any other code's internals and nothing can change in isolation. But nothing about being a single deployable requires that outcome — Fowler is explicit that a monolith can have a good modular structure internally (see Modular Monolith for the deliberate, tooling-enforced version of this). Stack Overflow is the most-cited real counterexample to "monoliths don't scale": as of the mid-2020s it served roughly 2 billion page views and 4 billion requests a month from a single ~15-year-old C#/IIS monolith running on fewer than a dozen on-premise web servers (each handling several hundred requests/second at a fraction of its CPU budget) backed by a SQL Server cluster with heavy use of Redis and ElasticSearch as targeted caching/search augmentations — not a rewrite into distributed services. The lesson isn't "monoliths beat microservices" as a general claim; it's that a well-optimized single deployable can absorb far more scale than the "you need microservices to handle real traffic" folk wisdom suggests, and that the decision to split should be driven by an actual organizational or scaling pain point, not by scale alone.

Tradeoffs

Concern Monolith Microservices
Deployment One build, one deploy — simplest possible release pipeline Independently deployable services, but N pipelines to build and operate
Refactoring cost of a wrong boundary Cheap — a same-process move/rename inside one codebase Expensive — a network contract with live callers has to be versioned or migrated
Operational surface Minimal — one thing to monitor, scale, and keep available Every service needs its own monitoring, on-call surface, and failure handling
Team scaling Breaks down as headcount grows — no enforced ownership boundaries, unrelated changes collide Each team can own, deploy, and scale its own service independently
Fault isolation A bug or resource leak in one feature can take down the whole process A failing service degrades its own functionality without necessarily taking others down (if callers handle it — see Circuit Breakers)
Partial scaling Must scale the whole application even if only one feature is hot Can scale just the hot service independently

The row that actually decides the tradeoff is team scaling versus premium cost: microservices incur a real, ongoing tax (the cost of managing a whole suite of independently deployed, independently failing services), which is worth paying only once a monolith's organizational coupling — not its raw request volume — becomes the actual constraint.

When to use / when not to

  • Default to a monolith for a new system, regardless of expected eventual scale — Fowler's argument holds even for systems that will genuinely need to be large later, because the early flexibility to redraw boundaries cheaply matters more than anticipating a split correctly up front.
  • Use it for systems with a small team (roughly under the size where more than one team needs to own different parts of the codebase) — a monolith's single release pipeline and lack of network boundaries removes coordination overhead that a small team doesn't need to pay for.
  • Don't treat "we might need to scale later" as justification for splitting early — Stack Overflow's numbers show a well-optimized monolith absorbs far more load than most systems will ever see; raw traffic is rarely the actual reason a split becomes necessary.
  • Don't assume a monolith is exempt from needing internal structure — an unstructured monolith becomes a big ball of mud regardless of how few users it has, and retrofitting boundaries later is real work; see Modular Monolith for the deliberate middle ground.
  • Split out of a monolith when a specific, already-felt pain shows up: one team's deploys are blocked by another team's changes, a single component needs independent scaling that's cheaper to isolate than to keep over-provisioning the whole app for, or a component has fundamentally different reliability/latency requirements than the rest of the system.

Common pitfall

Conflating "monolith" with "unstructured" and using that as an excuse to skip internal boundaries entirely, on the reasoning that boundaries only matter once a system is split into services. In practice the opposite ordering pays off: teams that let a monolith's internals stay tangled indefinitely find that when a real reason to split finally arrives, they can't — the code has no clean seams to cut along, and what should have been "extract this already-isolated module into its own service" becomes a multi-quarter untangling project first. The teams that split successfully (Fowler's observation) are disproportionately the ones whose monolith had decent internal modularity already, because that's what makes the eventual boundary line visible and cheap to act on.

Engineering Lens

The Principal-level judgment call isn't "monolith or microservices" as a one-time architectural decision — it's recognizing that a monolith is the correct default and the burden of proof sits on the case for splitting, not the case for staying unified. The strongest design-review answer for "why is this still a monolith" is naming the actual absence of a specific pain point (no team-boundary friction yet, no component needing independent scaling yet), not "we haven't gotten around to it" — and the strongest answer for "why did you split this" names the concrete organizational or scaling constraint that made staying a monolith actively worse, backed by evidence, not a general belief that services scale better. Stack Overflow's numbers exist precisely to push back on the unexamined version of that belief.

Sources

Hermes Wiki