Skip to content
Architecture Decisions & Trade-offs

Architecture & Scale

Listen 0%
Speed

25 · Architecture Decisions & Trade-offs

The judgment layer: choosing between monolith and microservices, SPA, MPA, and micro-frontends, and the rendering/deployment shapes — with concrete cases (e-commerce, dashboards, content sites). The senior skill isn’t knowing the patterns (those are in 0913, 24); it’s knowing which one fits this problem, team, and stage — and being able to defend it.


Positioning

Most architecture mistakes aren’t wrong patterns — they’re right patterns applied at the wrong time or scale. Microservices for a 4-person startup, micro-frontends for a single-team app, or a monolith for a 200-engineer org all fail for the same reason: the architecture didn’t match the organizational and load reality. This file is a decision framework. The recurring meta-principle: start simple, decompose when a real force (team scale, deploy contention, scaling hotspots) demands it — and let Conway’s Law (your system mirrors your org) guide the seams.


Foundations: the forces that decide

Before any choice, name the forces:

  • Team size & topology — how many teams, how independent, what release cadence? (The dominant factor.)
  • Scale & load shape — uniform or hotspots? Traffic now vs projected?
  • Domain complexity — how many distinct bounded contexts (10)?
  • Time-to-market & team experience — can you operate the complexity you’re proposing?
  • Change rate — which parts change together, which independently?
  • Consistency needs — strong transactions vs tolerable eventual consistency (13).

The honest default for almost everything early: a well-structured modular monolith with a SPA (or SSR app). You decompose outward only when these forces push you.


Decision 1 — Monolith vs Microservices (backend shape)

Monolith

One deployable unit containing all functionality.

  • Pros: simplest to build/deploy/test/debug; one codebase; easy refactoring across boundaries; ACID transactions; no network between components; fastest for small teams. Lowest operational cost.
  • Cons: at scale, deploys couple teams (everyone ships together); one bug/leak can take down everything; can’t scale subsystems independently; large codebases get tangled if not modularized; tech stack is uniform.

Modular Monolith (the underrated middle)

A monolith with strict internal module boundaries (clear interfaces, enforced dependencies — FSD/bounded contexts, 10). Gets most of the maintainability of services without the distributed-systems tax. The recommended default, and the structure that makes a later extraction to services cheap. “Monolith first” (Fowler): build modular, extract services only when needed.

Microservices

Many independently deployable services around bounded contexts (13).

  • Pros: independent deploy/scale per service; team autonomy; fault isolation; tech heterogeneity; targeted scaling of hotspots.
  • Cons: distributed-systems complexity (network failures, eventual consistency, sagas, 13); operational overhead (orchestration, observability, 24); harder debugging/testing; data consistency is hard; easy to build a “distributed monolith” (all the cost, none of the independence) if boundaries are wrong.

The e-commerce case (concrete)

Picture an online store with catalog, search, cart, checkout/payments, inventory, orders, recommendations, reviews, user accounts.

  • Early-stage / single team / modest trafficmodular monolith + SPA (or SSR for SEO on catalog pages). Everything in one well-structured app. Don’t pay the microservices tax to serve thousands of orders/day. Most stores never need more.
  • At scale (large catalog, traffic spikes, many teams)selective microservices, extracted by force, not fashion:
    • Search has a totally different scaling/storage profile (Elasticsearch, read-heavy, spiky) → extract early.
    • Checkout/Payments has strict consistency, security (17), and compliance needs, and must stay available during catalog outages → isolate so a recommendations bug can’t break purchases.
    • Inventory needs strong consistency and high write contention during sales → its own service with careful transaction/saga design (13).
    • Recommendations/Reviews are non-critical, independently scalable, and can degrade gracefully → good candidates to split (and to fail-soft in the UI via partial responses, 12).
    • Catalog is read-heavy and cache-friendly → CDN/edge + read replicas (24).
    • A Black Friday spike is the canonical justification: you want to scale checkout and inventory independently of reviews, which a monolith can’t do.
  • The trap: a 3-person team copying Amazon’s microservice diagram. Amazon has thousands of engineers; their architecture mirrors their org (Conway). Yours should mirror yours.

Rule of thumb: monolith (modular) until team-scale or independent-scaling pressure is real; then extract the highest-contention, most-independent, or most-critical-to-isolate capabilities first — not everything at once.


Decision 2 — SPA vs MPA vs Micro-Frontends (frontend shape)

SPA (Single-Page Application)

One JS app, client-side routing, the page never fully reloads.

  • Best for: app-like, highly interactive products behind a login where SEO is secondary — dashboards, admin panels, SaaS tools, editors, internal tools, Rian’s kind of design-system-driven apps.
  • Pros: rich interactivity, smooth transitions, clear client/API separation. Cons: initial JS cost/hydration (07), SEO/first-paint work needed, you ship a lot of JS. (Modern SSR/RSC meta-frameworks, 08, blur “SPA vs MPA” — Next gives SPA-like navigation with server rendering.)

MPA (Multi-Page Application) / server-rendered

Server returns full HTML per route; navigation reloads.

  • Best for: content/SEO-driven, mostly-static or low-interactivity sites — marketing sites, blogs, docs, news, e-commerce catalog/product pages where SEO and first paint are paramount. Islands (Astro, 07) are the modern MPA sweet spot: HTML-first with interactivity only where needed.
  • Pros: great SEO/first paint, minimal JS, simple. Cons: less fluid interactivity, full reloads.

Micro-Frontends (MFE)

Many independently deployed frontend pieces composed into one product (09).

  • Best for: large organizations with many autonomous teams whose release cadences collide in a single SPA — large enterprise platforms, big e-commerce with separate teams owning catalog/checkout/account, products integrating multiple legacy apps.
  • Pros: independent deployment + team autonomy at frontend scale; incremental migration; tech flexibility. Cons: significant complexity — version skew, shared-dependency management, style isolation, payload/perf overhead, cross-MFE communication, governance (09). Performance is usually a cost you manage, not a benefit.

MFE vs SPA — how to choose

  • One team, or a few that coordinate fine?SPA (or modular monolith frontend). MFEs would add cost with no benefit. The most common right answer.
  • Many teams blocked by a shared frontend deploy / a monolithic SPA that’s become a merge battleground?MFEs. The driver is organizational, exactly like microservices (09, Conway).
  • A modular monolith frontend (feature-sliced, 10) solves most “our SPA is getting big” problems without MFEs. Reach for MFEs when the pain is independent deployment across teams, not just code size.

The e-commerce frontend, mixed in practice

A real store often mixes strategies (07): catalog/product = SSR/SSG for SEO (MPA-ish), cart/checkout = interactive SPA-like islands or a focused SPA, and at large scale different teams own different MFEs (catalog team, checkout team, account team) composed via a shell or Next Multi-Zones (08). The rendering strategy and the team/deployment strategy are separate decisions — don’t conflate them.


Decision 3 — Rendering strategy

Covered in depth in 07 (CSR/SSR/SSG/ISR/streaming/RSC/islands/resumability) with its own decision tree. The one-line mapping: SEO + content → SSG/SSR/islands; app behind login → CSR/SPA or SSR app; mixed product → per-route strategy (Next lets you choose per route, 08). Rendering strategy is orthogonal to monolith-vs-microservices and largely orthogonal to SPA-vs-MFE.


Comparing the software-architecture styles (onion / clean / hexagonal)

For the internal structure of an app (not its deployment shape), see the detailed onion vs clean vs hexagonal comparison in 10 — they’re three drawings of the same dependency rule, differing mainly in framing (ports vs rings vs four-ring synthesis) and ceremony. The decision there is “how much structure does this app’s complexity justify,” and the answer is usually the pragmatic synthesis (10), not a dogmatic full implementation.


Decision-making tools

  • Conway’s Law — you ship your org chart; design team boundaries and architecture boundaries together. Inverse Conway Maneuver: shape teams to get the architecture you want. (See Team Topologies.)
  • Architecture Decision Records (ADRs) — short markdown files capturing context → decision → consequences for each significant choice, version-controlled. The senior habit for making trade-offs explicit and reviewable.
  • Reversible vs irreversible decisions (Bezos “one-way/two-way doors”) — move fast on reversible choices; deliberate on hard-to-undo ones (your datastore, your service boundaries).
  • YAGNI / start simple (20) — the cheapest architecture you can refactor out of beats the elaborate one you guessed wrong.
  • Fitness functions — automated checks that guard architectural properties (dependency rules, bundle budgets, 14/15) so they don’t erode.

Worked example: an ADR for the e-commerce checkout

# ADR-014: Extract Checkout into a separate service + MFE

## Context
Catalog and checkout are owned by different teams. Catalog deploys ~10×/day;
checkout needs strict change control (PCI, payments). A shared monolith + single SPA
forces coordinated deploys and risks a catalog bug affecting purchases (revenue).
Black-Friday load on catalog must not degrade checkout capacity.

## Decision
- Extract a Checkout *service* (own DB, saga for order→payment→inventory, idempotency keys).
- Extract a Checkout *micro-frontend*, composed via the shell; communicate via URL + a
  validated event bus, not shared state. Catalog stays SSR for SEO; checkout is an SPA-like MFE.
- Keep everything else in the modular monolith / single SPA for now (YAGNI).

## Consequences
+ Independent deploy + isolation of the critical path; independent scaling on spikes.
+ Clear PCI boundary (17).
− New distributed-systems complexity (eventual consistency, tracing, 13/24).
− Frontend now manages a shared design-system version across two apps (11).
Revisit if a third team needs its own slice → consider broader MFE adoption.

The value is the explicit trade-off: what you gain, what you pay, and the trigger to revisit.


Pitfalls & gotchas

  • Resume-driven / hype-driven architecture — microservices/MFEs/K8s because they’re impressive, not because a force demands them.
  • Distributed monolith — services/MFEs that must deploy together: all the cost, none of the autonomy (13).
  • Premature decomposition — splitting before you understand the domain; boundaries end up wrong and expensive to move.
  • Conflating rendering, deployment, and service decisions — they’re independent axes; decide each on its own merits.
  • Ignoring Conway’s Law — architecture that fights the org chart loses.
  • Big-bang rewrites instead of incremental extraction (strangler-fig migration).
  • No ADRs — decisions and their rationale get lost; the next team re-litigates or cargo-cults.
  • Copying a FAANG diagram at startup scale.

Interview questions

  1. When would you choose a monolith over microservices — and vice versa?
  2. What’s a modular monolith and why is it often the right default?
  3. Walk through decomposing an e-commerce backend: what would you extract first and why?
  4. SPA vs MPA vs MFE — give a concrete product fit for each.
  5. When do micro-frontends actually pay off, and what do they cost?
  6. How does Conway’s Law influence these decisions?
  7. Why are rendering strategy and deployment architecture separate decisions?
  8. What is a distributed monolith and how do you avoid it?
  9. What goes in an ADR, and why keep them?
  10. How do you decide “start simple” vs “design for scale now”?

Recommendations

  • Default to a modular monolith + SPA/SSR app; structure it so extraction is cheap later.
  • Decompose to microservices/MFEs by force (team-scale, independent deploy, scaling hotspots, isolation needs) — extract the highest-contention/most-critical-to-isolate parts first (checkout, search, inventory in e-commerce).
  • Treat rendering, deployment, and service decisions as independent axes; mix strategies per route/area.
  • Let Conway’s Law guide boundaries; use the Inverse Conway Maneuver deliberately.
  • Record significant choices as ADRs; protect them with fitness functions (dependency lint, bundle budgets).
  • Favor reversible moves; deliberate hard on one-way doors (datastore, boundaries).
  • Migrate incrementally (strangler fig), never big-bang.

Books & references

  • “Building Microservices” (2nd ed) — Sam Newman. Decomposition, when (not) to split, the migration patterns. (13)
  • “Monolith to Microservices” — Sam Newman. The strangler-fig and incremental extraction playbook — directly about this decision.
  • “Team Topologies” — Skelton & Pais. Conway’s Law operationalized; team shapes that produce good architectures. Essential for the org dimension.
  • “Fundamentals of Software Architecture” & “Software Architecture: The Hard Parts” — Richards & Ford. Trade-off analysis, ADRs, fitness functions, distributed vs monolithic styles. The best modern architecture-decision books.
  • “Building Micro-Frontends” — Luca Mezzalira (09); martinfowler.com — “MonolithFirst,” “Microservices,” “Micro Frontends,” and the Technology Radar (adoption cautions).
  • “Designing Data-Intensive Applications” — Kleppmann (the systems trade-offs underneath) (24).
  • adr.github.io — Architecture Decision Records formats and tooling.

Connections

Frontend Deep-Dive Library · content is the single source of truth.