Archtin
All articles
ArchitectureSystem Design11 min read

Monolithic Architecture: Still the Right Default

A monolith is one deployable containing the whole application. It is the fastest way to build software until it isn't — here is how monoliths work, when they win, exactly how they degrade, and how to keep one healthy for years.

What a monolith actually is

A monolithic architecture packages the entire application — request handling, business rules, background jobs, data access — into a single deployable artifact that runs as one process type against one primary database. Calls between parts of the system are function calls, not network calls.

Note what the definition does not say. It says nothing about code quality, size, or internal structure. "Monolith" is a deployment property. A well-partitioned 250k-line monolith is a better system than a tangled six-service estate that must be released together.

Inside the box

app/
  web/          HTTP handlers, templates, API controllers
  billing/      invoices, tax, payment orchestration
  catalogue/    products, pricing, search indexing
  identity/     accounts, sessions, permissions
  jobs/         async workers (same codebase, same image)
  platform/     db, cache, logging, feature flags

single build  ->  single container image
scaled by running N identical replicas behind a load balancer
A typical monolith: many modules, one process, one deploy.

Horizontal scaling is trivial: run more identical replicas. Because every replica can serve every request, capacity planning is one number. There is no service discovery, no contract version matrix and no partial deploy state.

Why it wins early

  • Atomic changes. Renaming a domain concept across the whole system is one commit, one review, one deploy. In a distributed estate it is a multi-week migration.
  • Real transactions. One database means ACID. No sagas, no compensating actions, no eventual-consistency UX for a simple "create order and reserve stock".
  • Trivial debugging. One stack trace contains the whole story. No correlation IDs required to answer "why was this request slow?".
  • Cheap. One pipeline, one runtime, one on-call rotation, no service mesh, no platform team.
  • Cheap to be wrong. Early on, your domain boundaries are guesses. Guessing wrong inside a monolith costs a refactor; guessing wrong across services costs a migration.
Rule of thumb
Under ~20 engineers, with a domain you are still discovering, a monolith is almost always the correct choice. Shopify, Basecamp, GitHub and Stack Overflow ran enormous businesses on monoliths long past the point where blog posts said they should have split.

How monoliths degrade

Monoliths rarely fail technically. They fail organisationally, in a predictable sequence:

  1. Coupling creep. Any module can import any other, and shortcuts across boundaries are invisible in review. Eventually every change has unpredictable reach.
  2. Shared-schema gravity. Five modules read the same table. Now that table cannot change, and it can never be moved.
  3. Release contention. One team's flaky test blocks everyone's release. Deploy frequency drops, batch size grows, risk per deploy grows with it.
  4. Uniform scaling waste. The image-processing path needs 16 GB of RAM, so every replica gets 16 GB — including the ones serving login.
  5. Blast radius. A memory leak in a rarely-used report kills the process that also serves checkout.

Scaling a monolith further than you think

PressureFix that keeps the monolith
Read-heavy trafficRead replicas + cache-aside on hot entities
Slow endpoints starving othersSeparate replica pools per route class (same image, different deploy)
Heavy async workRun the same image in worker mode against a queue
Write throughput ceilingPartition the hottest table; move it behind a module interface first
Deploy riskFeature flags, canary replicas, expand/contract migrations
Build timesModule-level test selection, incremental builds

Notice that four of these are exactly what you'd do in a microservice estate anyway. The distributed version just adds a network in the middle.

Keeping one healthy

  • Enforce module boundaries in CI. Import-linting so billing cannot import catalogue.internals. Rules that are not machine-checked are decorations.
  • One schema owner per module. Cross-module reads go through the owning module's API, not through SQL joins.
  • Keep the deploy fast. Deploy time is the real health metric. Under ten minutes and the monolith stays pleasant; over an hour and it is already failing.
  • Isolate risky work. Untrusted or memory-hungry work (PDF rendering, image transforms, third-party SDKs) belongs in a separate process even if everything else stays.

Signals it's time to split

Split on evidence, not aesthetics. Genuine signals:

  • Independent teams are blocked on each other's releases every week.
  • One component's resource profile is wildly different from the rest.
  • One component needs a different scaling curve, runtime or compliance boundary.
  • One component's failure repeatedly takes down unrelated features.

When those appear, extract one module — the one with the clearest interface and the least shared data — and prove the operational story before extracting a second.

Interview framing

Strong answer

"I'd start monolithic with strict internal modules. It gives us transactional integrity for order placement, which is the part that must be correct, and it lets us change the domain model weekly while we learn it. I'd run workers from the same image, put the catalogue reads behind a cache, and enforce module imports in CI so extraction is cheap later. The risk I accept is blast radius; I mitigate it with canary deploys and by moving image processing out of the main process on day one."

Common traps

  • Equating "monolith" with "legacy" or "bad code".
  • Claiming monoliths cannot scale — they scale horizontally like anything stateless.
  • Splitting for scale when the actual bottleneck is a single database table.

Keep reading

Suggested next articles based on this one.

Design it, don't just read it.

Practise LLD and system design problems with structured rubrics and AI feedback.

Start practising free