Skip to main content

The Monolith (and When It's Right)

Editorial

Introduction

At 12:01pm Eastern on Black Friday 2025, Shopify's merchants were selling $5.1 million of product per minute. The edge was absorbing 489 million requests a minute; the application servers behind it, 117 million. Over the four-day weekend the platform ran 2.2 trillion requests, 90 petabytes of data, and 1.75 trillion database writes.

The system taking all of that is a Ruby on Rails application that the industry's conference circuit spent a decade calling a legacy pattern: one codebase — 2.8 million lines, half a million commits — deployed as one unit. A monolith. Not a survivor waiting to be decomposed; a deliberate, defended choice by a company with hundreds of engineers.

The previous lesson priced a boundary: about 200× per call the moment a line in the code becomes a network hop. This lesson asks the obvious next question — what do you get if you refuse to buy any boundaries at all? The answer is better than its reputation, and its real limits are not where most people think they are.

In this lesson: what a monolith actually is (and isn't), its advantages stated as mechanisms rather than nostalgia, how far it genuinely scales, the two curves that decide when it stops being right, and the discipline — the modular monolith — that moves that point years further out.

Scope: when and how to split into services is the next lesson's whole argument (Microservices — and When to Split). Serverless and event-driven shapes get their own lessons after that, and the section closes by turning all of it into a decision rubric in Choosing a Style: The Rubric. Today is about understanding the shape you'd be leaving.

A cutaway anatomy of a monolith drawn as one large deployable unit. A single rounded container labelled one deployable holds four internal rooms named storefront, orders, inventory and billing, separated by thin interior walls to show module boundaries that live in code rather than on the network. A single deploy pipeline enters from the top labelled one pipeline, a single database cylinder sits beneath the rooms labelled one database, one transaction, and a single pager icon marks one on-call. To the right, three stacked copies of the same container behind a load balancer are labelled cloned, not split, showing that a monolith scales horizontally by replication. A focal band carries the Shopify fact that anchors the lesson: five point one million dollars per minute through one codebase, with the sub-note two point eight million lines, five hundred thousand commits. A bottom band states the honest pros as mechanisms: calls are function calls, consistency is one transaction, a refactor is one pull request, operations is one system. The figure communicates that a monolith is one deployment unit with real internal structure, not one server and not an absence of architecture.

One Deployable, Not One Server

Strip the connotations and the definition is one sentence: a monolith is an application delivered as a single deployable unit. One build artifact. One release. One process shape that contains presentation, logic, and data access together — all three layers from Client-Server to N-Tier, sharing one process and paying zero boundary tax between them.

Notice what the definition does not say. It doesn't say one server — you can run fifty identical copies of a monolith behind a load balancer, and almost everyone at scale does. It doesn't say one file, or no structure — the inside can be as carefully partitioned as any architecture you've ever admired. And it doesn't say old: every framework you'd start a project with this afternoon produces one.

The anatomy has four "ones" worth naming, because each becomes an advantage in the next section and a constraint two sections later:

  • One process at runtime (cloned N times). Modules call each other the way functions do.
  • One database in the common case, reachable by every module inside one transaction.
  • One pipeline. Every change, from every team, ships through the same build-test-deploy gate.
  • One operational surface. One thing to monitor, one thing to secure, one pager.

DHH — who has run Basecamp as a monolith since 2003 and coined the phrase majestic monolith for the shape done well — defines the ambition behind it as "an integrated system that collapses as many unnecessary conceptual models as possible." Keep that word integrated. It's the property everything in the next section falls out of.

The Pros Are Mechanisms, Not Nostalgia

Every advantage of a monolith is the same fact wearing different clothes: there is no network inside it. You measured what a boundary costs in the last lesson; a monolith is the arrangement that pays that price exactly zero times. Walk the consequences.

A call is a function call. Orders calling inventory is nanoseconds, type-checked at build time, and it cannot time out, retry, or half-succeed. Shopify's engineers list this first when they defend their architecture: "you can call into different components directly, rather than needing to communicate over web service APIs."

Consistency is one transaction. Decrement stock and create the order and charge the customer inside one BEGIN … COMMIT, and either all of it happened or none of it did. Once those live in separate services, that guarantee stops being something the database gives you and becomes something you build — a whole lesson of machinery (Saga: The Data-Consistency Mechanics, in Data Systems at Scale) to approximate what one transaction did for free.

A refactor is one pull request. Rename a concept, move a responsibility, split a module — the compiler and the test suite check the whole world at once. Martin Fowler's MonolithFirst essay rests on exactly this: "any refactoring of functionality between services is much harder than it is in a monolith." His observation from the field is blunt — "almost all the successful microservice stories have started with a monolith that got too big and was broken up," while systems born as microservices "ended up in serious trouble." The monolith is where you discover the right boundaries cheaply, because a module boundary is a bet you can reverse on a Tuesday. A service boundary isn't.

Operations is one system. One dashboard, one deploy to watch, one thing to secure, one on-call rotation. Every tier you don't have is a pager that never goes off.

None of this is an argument that boundaries are bad. It's an accounting identity: the pros of the monolith are precisely the costs of boundaries, unspent.

"But It Won't Scale" — the Numbers Disagree

The most repeated sentence about monoliths is that they don't scale. Shopify's Black Friday exists as a public counterexample, so let's retire the sentence properly.

A monolith scales the same way almost everything scales: you clone it. Fifty, five hundred, five thousand identical copies behind load balancers, each one able to serve any request. You met the machinery in Fundamentals of System Design (From One User to One Million, Scalability: Vertical vs Horizontal) — nothing about it requires the application to be split into pieces. The unit being multiplied is simply the whole application instead of a fragment of it.

That is not a theoretical concession. It's what the biggest sales weekend on the internet runs on: 489 million requests a minute at the edge, 117 million a minute on application servers, 14.8 trillion database queries in four days — through one deployable, cloned. In 2016, Stack Overflow made the same point from the other direction (Client-Server to N-Tier told that story): so few copies they could fall back to one web server.

Two honest footnotes, because this section is not a sales pitch.

First — the database is the harder scaling story, and that's true for every architecture. Shopify shards; an entire course in this curriculum (Data Systems at Scale) exists because data is where scaling actually gets difficult. Splitting your application into services doesn't make the data problem easier; it usually makes it harder.

Second — cloning scales the whole artifact. Every replica carries every module, including the sleepy ones. If one module needs 100× the capacity of the rest, you pay for 100× of everything. That waste is real, it grows with divergence, and it belongs on the honest side of the ledger — it just isn't the thing that fails first. What fails first is the subject of the next section.

The Two Curves: What Actually Bends

Plot a growing system's pain against its growth and you get two curves that could not look more different.

The traffic curve stays almost flat. Load doubles; you add replicas; the load balancer spreads it; nothing about the architecture changed. This curve is why the "won't scale" myth is a myth — for a monolith, traffic is the solved axis, all the way up to numbers with the word trillion in them.

The coordination curve bends. It measures something no load balancer can spread: how many people must ship through one gate. A monolith has one pipeline by definition, and three things compound as the team behind it grows:

  • The deploy train queues. Every merge from every team rides the same build-test-deploy cycle. Twelve programmers barely notice. Three hundred turn the pipeline into rush hour — and queues don't degrade politely near capacity, they explode (you'll feel this in the widget below).
  • Blast radius stays at 100%. One artifact means a bad deploy can, in principle, take everything with it. From Design to Production computed this exact number; here is where it bites.
  • Coupling compounds silently. Shopify's own description of their growing pains: the application became "extremely fragile, with new code having unexpected repercussions," tests "very slow to run on CI," and the context a new engineer needed was "massive." Nothing enforced the internal walls, so the walls eroded.

Put the two curves side by side and the lesson's core claim falls out: the monolith's wall is organizational, not technical. Traffic doesn't end monoliths. Headcount does — and even then, not always. The next section is about the discipline that moves the bend point; the widget after it lets you find the wall yourself.

A chart with growth on the horizontal axis and pain on the vertical axis, showing the two pressures that grow as a monolithic system succeeds. The first curve, drawn in calm green and staying nearly flat, is labelled traffic and annotated clone more replicas — it rises only gently because horizontal cloning keeps absorbing load, with a marker at Shopify scale noting four hundred eighty nine million requests per minute. The second curve, drawn in warning red and bending sharply upward, is labelled coordination and annotated one deploy train, every team, one artifact — it passes marked points for merge queue wait, full-system blast radius and regression risk from unexpected coupling. A vertical dashed line where the red curve leaves the safe zone is labelled the wall is organizational, not technical. The figure communicates the lesson's central claim: traffic is the solved axis for a monolith, and the curve that actually bends is the human one — how many people must ship through one gate.

The Modular Monolith: Walls Without the Network

Here's the conflation that produces most bad architecture arguments: people use "monolith" to mean messy and "microservices" to mean modular, as if internal structure and deployment units were the same axis. They're independent. You can have one deployable with rigorously enforced internal boundaries — and you can have forty services with none.

The modular monolith is the top-left of that grid: one deployment unit, many real walls. The walls are drawn in code — modules with explicit public interfaces, organized around business concepts — and, this is the part that makes it real, enforced by tooling rather than good intentions.

Shopify is the canonical worked example, and their account is worth reading precisely because it isn't triumphant. They reorganized 2.8 million lines into components — "orders, shipping, inventory, billing" — each "structured as its own mini Rails app," chosen deliberately over microservices because they "wanted a solution that increased modularity without increasing the number of deployment units." To stop the walls eroding, they built Packwerk, a static-analysis tool that runs in CI and rejects pull requests that "break the dependency graph or component encapsulation before they get merged." A boundary you can't violate in a merged PR is a boundary that actually exists.

And the honest part: it was hard, and some of it didn't work. Their first call-graph tool produced results where "it's hard to separate the signal from the noise." Where they drew interfaces without respecting the real dependency graph, the public API "turned out to just be an added layer of indirection." Pushing for consistency "added rules, which always add some friction" without making hard problems easier. It took an internal architecture guild hundreds strong, and years, and as of their last public accounting about a third of components had enforcement switched on.

Read that honestly and the modular monolith is neither a hack nor a free lunch. It's a discipline with a real price — and the price is still a fraction of the microservice premium, because every one of those walls remains a function call, one transaction, one refactorable codebase. You are buying boundaries and declining to buy networks.

One more thing the grid makes obvious: the bottom-right cell exists. Split a ball of mud into services and you get a distributed ball of mud — network prices paid, discipline still absent. Deployment boundaries don't create modularity. They just make its absence more expensive.

A two-by-two grid separating code modularity from deployment units, the two axes people conflate when they equate monoliths with mess. The horizontal axis runs from one deployable to many deployables; the vertical axis runs from no internal boundaries to enforced boundaries. The bottom-left cell, one deployable with no boundaries, is labelled ball of mud and marked as where unattended codebases drift. The top-left cell, one deployable with enforced boundaries, is labelled modular monolith, highlighted as the recommended shape and annotated with Shopify's mechanics: thirty seven components, boundaries checked in continuous integration. The bottom-right cell, many deployables with no boundaries, is labelled distributed ball of mud and marked worst of both — network prices paid, discipline still absent. The top-right cell, many deployables with enforced boundaries, is labelled microservices done well and noted as the next lesson's territory. The figure communicates that modularity and deployment are independent axes: you can add walls without adding networks, and adding networks never adds walls by itself.

See It: Find the Real Wall

Everyone believes the two-curves claim in the abstract. It lands differently when you try to break a monolith yourself and discover which knob actually breaks it.

Try this first: drag traffic up — keep going, past Black Friday numbers — and watch what happens: replicas and cost climb, and the verdict stays green. Traffic was never the wall. Now put traffic back down and grow the team instead. Watch the deploy train load creep up, the merge queue start to stack, and the verdict turn. Then flip on modular discipline and watch the same team size come back from the edge — the wall moved, but it didn't vanish. Ship a few changes along the way and keep an eye on the blast-radius number that never changes.

The Monolith Stress Lab — one deployable, and you choose what grows. Drag the traffic slider from a hundred requests per second past Black Friday scale and the monolith simply adds replicas: the fleet grid grows, the bill climbs linearly, and the verdict stays green, because traffic is the solved axis. Then grow the team instead and watch the one deploy pipeline become rush hour: merges queue behind a single build-test-deploy train whose wait time explodes as utilization approaches capacity, regression risk climbs as unexpected coupling compounds, and the blast radius readout stays pinned at one hundred percent of the system per deploy. Flip on modular discipline — enforced internal boundaries in the style of Shopify's components and Packwerk — and the same headcount steps back from the wall: shorter cycles, fewer surprise regressions, but the wall only moves, it never disappears. Press merge to send real changes through the train and watch them queue when the train is loaded. The wait-time math is a real M/M/1 queue, cycle time times load over one minus load, which is why the queue degrades politely at low load and catastrophically near capacity.

When It's Right — and When It Stops

So when is the monolith the right answer, rather than the default you haven't examined yet? The evidence from the last five sections compresses into three questions.

How many people ship this system? DHH runs Basecamp with a dozen programmers; at that size the coordination curve is a flat line and every microservice premium is pure loss. Shopify runs hundreds of engineers against one deployable — but only by paying for discipline: components, enforced boundaries, an architecture guild. Somewhere past that, even discipline strains, and DHH himself concedes the other side: for organizations with "thousands of developers," splitting is "a wonderful way to parallelize opportunities for improvement." The team size that reads this course mostly lives far below that line.

Does any part need to be genuinely different? A module that must scale 100× the rest, run a different runtime, or sit inside a different compliance boundary is the honest technical trigger — it's the its own scaling curve / failure domain / security boundary test from Client-Server to N-Tier, applied to one module. If nothing qualifies, a split buys you hops and pagers.

Are your boundaries discovered yet? Fowler's field observation cuts hardest here: successful microservice systems started as monoliths because that's where the right boundaries revealed themselves — cheaply, reversibly, one refactor at a time. If you can't yet name your system's natural seams, you are not ready to pour concrete on them.

The pattern behind all three: stay integrated until something specific and nameable forces you out — then split exactly that thing. "The patterns that make sense for organizations orders of magnitude larger than yours," as DHH puts it, "are often the exact opposite ones that'll make sense for you."

Under interview follow-up, this position defends itself with numbers: the boundary tax is measured (~200× per call before the packet leaves the machine), the scale proof is public ($5.1M/min through one codebase), and the failure mode you're avoiding has a name — the distributed ball of mud. What you concede, honestly: one artifact means one blast radius, one runtime, and a deploy train that headcount will eventually overwhelm. Which is exactly the door the next lesson walks through.

Mental-Model Corrections

"Monoliths don't scale." The largest sales weekend on the internet runs through one: $5.1 million per minute, 489 million requests a minute at the edge, 1.75 trillion database writes in four days. A monolith is one deployable, not one server — it clones as widely as you can pay for. Traffic is its solved axis; the team is the axis that bends.

"Monolith means big ball of mud." Modularity and deployment units are independent axes. One deployable with CI-enforced internal walls is a modular monolith; forty services with no discipline is a distributed ball of mud — the network tax and the mess.

"The monolith is the starter phase you graduate from." Basecamp has shipped from one for over twenty years, by conviction. And Fowler's arrow points the other way around: the successful microservice systems began as monoliths; the ones born distributed are the ones that struggled.

"You need services to give teams real ownership." Ownership needs an enforced boundary, not a network. A component with a public interface that CI defends — a merged PR literally cannot violate it — is a team boundary inside one deployable.

"One shared database is the monolith's weakness." It's closer to its superpower: cross-module consistency is a BEGIN … COMMIT, not a saga. The day you split the data is the day you start building machinery to approximate what one transaction gave you for free — that machinery fills whole lessons of Data Systems at Scale.

Key Takeaways

  • A monolith is one deployable, not one server. Presentation, logic, and data in one process — cloned behind a load balancer as many times as demand requires.
  • Its pros are one mechanism in different clothes: no internal network. Calls are function calls, consistency is one transaction, a refactor is one PR, operations is one pager — the boundary tax from Client-Server to N-Tier, unspent.
  • Traffic is the solved axis. Cloning took one Rails codebase to $5.1M/minute and 489M requests/minute. The curve that bends is coordination: one deploy train, shared by everyone, with 100% blast radius.
  • The modular monolith moves the wall. Boundaries in code, enforced by CI (Shopify: 37 components, Packwerk rejecting violating PRs) — walls without networks. It's a real discipline with a real price, and it buys years.
  • Boundaries are discovered in monoliths and poured in concrete in services. Split when something specific forces you out — a team the train can't carry, a module that needs its own curve — and split exactly that.

The monolith holds one end of this section's spectrum: everything integrated, every boundary declined. The next lesson stands at the opposite end and asks the question this one deliberately deferred: when the wall does arrive, how do you split — and how do you avoid trading a majestic monolith for a distributed ball of mud? Next — Microservices — and When to Split.