Runtime Architecture & Deployment: It Is Not a Binary Choice
Monolith vs microfrontend is a false binary — architecture has three independent dimensions, and mixing them up is how teams end up with all the complexity and none of the benefits.
The question I keep hearing is: "Are you a monolith or are you microfrontends?" As if those are the only two options, and as if choosing one tells you everything about how the system works.
It doesn't. Frontend architecture has at least three independent dimensions — and I've seen teams get burned repeatedly by conflating them.
The Three Dimensions
ExpandThree dimensions of frontend architecture
1. Runtime Composition — How It Loads in the Browser
This is what most people mean when they say "microfrontend." The question is: when the browser loads the application, does it receive one bundle or many?
- Monolith runtime: Everything ships as a single bundle. One load, one coherent application.
- Microfrontend runtime: The shell loads independently deployable slices at runtime — a dashboard from one team, a nav from another — composed into one user experience.
This is the dimension that actually determines deploy autonomy. If Team A can ship the dashboard without waiting for Team B, you have runtime composition. If everything must be built and deployed together, you don't — regardless of how the code is organized.
2. Repository Topology — Where the Code Lives
This is a completely separate question. Do you have one repo with many packages, or many repos with one package each?
- Monorepo: All code in one repository, potentially many applications
- Polyrepo: Each team or service has its own repository
The critical insight I had to internalize: repo topology and runtime composition are not the same thing. You can have a monorepo that deploys multiple independent microfrontends. You can have many repos that all get built into a single bundle. The location of code doesn't determine how it's assembled or deployed.
3. Deployment Topology — How It Ships
How does the code actually get deployed? Some teams have separate repos and separate CI pipelines, but still end up with a centralized deploy — where each team bumps a version in a central manifest, that commit triggers the main app's pipeline, and everything ships together.
Is that a monolith? Is it a microfrontend? The answer depends on which dimension you're asking about. The same system can be polyrepo by topology, microfrontend by runtime, and centralized by deployment.
Why This Distinction Matters
When you mix up these dimensions, you end up making architectural decisions that don't actually solve the problem you have.
The classic failure I've seen: a team wants deploy autonomy (a runtime concern), so they split into many repos (a topology decision), but they still coordinate all deployments centrally. They've taken on the overhead of a polyrepo without getting the autonomy they wanted. All the complexity, none of the benefit.
The inverse is just as bad: adopting module federation and runtime composition because "microfrontends are the modern approach" — but without actually needing independent deployments. Significant configuration complexity added for a benefit that never gets used.
The question I ask before any architectural decision: what is the actual pain? Then pick the specific dimension of architecture that addresses that pain.
| Pain | Relevant Dimension |
|---|---|
| Teams blocking each other on deploys | Runtime composition |
| Merge conflicts from shared codebase | Repository topology |
| CI/CD too slow | Build system (Turborepo, Nx) |
| Code ownership unclear | Repo boundaries or linting rules |
Build-Time vs Runtime Composition
One version of the "microfrontend" pattern is build-time composition: separate packages, owned by separate teams, assembled into one build at deploy time. This gives code ownership and monorepo structure without the complexity of runtime federation.
The trade-off is losing independent deploys. If Team A has a breaking change, everyone rebuilds. But you gain something I find genuinely valuable: the whole application can be type-checked and tested as a unit. Errors that would only surface at runtime in a federated system get caught at build time.
Whether that trade-off is worth it depends entirely on what you need. If the pain is slow builds, fix the build system. If the pain is deploy coupling, move toward runtime composition. Don't add runtime composition to solve a problem that lives in a different dimension entirely.
The Right Answer Is Usually a Mix
The honest framing: it's a buffet, not a menu with one item. Every real system has elements of both ends of the spectrum. The key is being intentional about which trade-offs you're accepting and why.
Start by identifying the actual pain. Then choose the minimum architectural change that addresses it. Complexity should be justified by the specific problem it solves — not by what the industry considers the current best practice.
Keep reading
Enjoyed this? Get more like it.
Deep dives on system design, React, web development, and personal finance — straight to your inbox. Free, always.