Asher Cohen
Back to posts

Auditing a Large-Scale Microfrontend Platform: Lessons from an Enterprise with Thousands of Repositories

What I found after analyzing a large enterprise microfrontend platform — and why organizational complexity now matters more than technical complexity

Microfrontends have been around long enough that the conversation has shifted from "Should we use them?" to "How do we keep them maintainable after hundreds of teams have adopted them?"

Recently I spent some time analyzing a frontend platform that represents what many large enterprises eventually become.

The organization contains:

  • Thousands of repositories
  • Independent backend and frontend teams
  • Multiple frontend technologies
  • Runtime composition
  • Legacy applications
  • Shared design systems
  • Independent deployments

It's the kind of architecture that isn't built in a single design session. It evolves over years, shaped by organizational needs, acquisitions, technology shifts, and practical compromises.

After reviewing the architecture, my conclusion is that it's considerably healthier than many enterprise microfrontend implementations I've seen. However, it has also reached the point where organizational complexity is becoming more significant than technical complexity.


The architecture

Each team owns its own repositories.

A repository may produce one of several deliverables:

  • a standalone application
  • a Module Federation remote
  • an iframe-based remote
  • an ESM/Web Component remote
  • an npm package published internally

Frontend applications are composed primarily into two shell applications:

  • an internal platform
  • an external platform

The frontend landscape currently consists of two primary technologies:

  • React single-page applications
  • a legacy Java full stack technology

Shared frontend assets are intentionally minimal:

  • a centrally maintained widget library (available in three variants)
  • internal npm packages published by teams

That is an important architectural choice.

Rather than creating a massive shared frontend framework, the organization has intentionally minimized shared runtime dependencies.

I generally consider that a positive decision.


Overall assessment

If I had to rate this architecture as it stands today, I'd give it 7.5 out of 10.

Not because it is poorly designed.

Quite the opposite.

Most of the individual architectural decisions are reasonable.

The challenge is that every good decision has a maintenance cost, and once an organization reaches hundreds of teams, those costs begin interacting with each other.

The architecture is no longer difficult because of React or Module Federation.

It's difficult because of scale.


What I like

Teams own their software

One of the strongest characteristics is the ownership model.

Each team owns both backend and frontend repositories.

That means teams can:

  • develop independently
  • release independently
  • choose appropriate implementation details
  • evolve at their own pace

This is one of the strongest predictors of long-term scalability.

Organizations that centralize frontend ownership often create deployment bottlenecks.

Organizations that decentralize ownership usually move much faster.

I wouldn't change this.

Multiple integration strategies

One thing that stood out immediately is that the platform doesn't try to force every problem into Module Federation.

Instead, there are several integration mechanisms available.

Each serves a different purpose.

Module Federation is used when applications should feel like one application.

iframes are available when complete isolation is required.

Web Components provide browser-native composition without runtime dependency sharing.

Standalone applications remain independent.

This is a surprisingly mature decision.

Far too many organizations attempt to solve every frontend integration problem with a single technology.

That usually ends badly.

Different problems deserve different isolation levels.

Minimal shared runtime

Only two categories are shared across the frontend ecosystem:

  • design system components
  • reusable packages

Everything else belongs to teams.

This avoids one of the most common enterprise mistakes: creating a gigantic "shared frontend platform" that every team depends on.

Shared code sounds attractive until hundreds of teams begin waiting on each other.

The less runtime coupling exists, the healthier the platform generally becomes.


Where complexity begins to appear

The first warning sign isn't technical.

It's cognitive.

Today there are effectively multiple frontend delivery models:

  • standalone applications
  • Module Federation
  • iframe integration
  • Web Components
  • internal npm packages

Each introduces its own deployment model, testing approach, debugging strategy, communication pattern, documentation, and operational concerns.

None of them are individually problematic.

Together they become expensive.

Every additional architectural pattern multiplies the number of things developers must understand.

One integration model is simple.

Two are manageable.

Four or five begin creating organizational friction.


Thousands of repositories

Large organizations often ask whether thousands of repositories are inherently a problem.

I don't think they are.

The real question is different.

How many repositories contain almost identical infrastructure?

If nearly every repository contains:

  • ESLint configuration
  • TypeScript configuration
  • build tooling
  • testing configuration
  • Docker configuration
  • CI pipelines

then those repositories are carrying unnecessary duplication.

The problem isn't Git.

The problem is platform engineering.

Repositories should contain business logic.

Infrastructure should come from reusable templates and shared tooling.


Module Federation

Module Federation is an excellent solution when applications need to feel like a single application.

Shared routing.

Shared authentication.

Shared React context.

Shared navigation.

Independent deployment.

Those are real advantages.

However, Module Federation also introduces runtime coupling.

Eventually someone upgrades React.

Someone else upgrades Webpack.

Another team changes shared dependency versions.

Then the host and remote begin negotiating runtime compatibility.

This is where many large Module Federation platforms eventually slow down.

Module Federation solves deployment independence.

It doesn't eliminate dependency coordination.

That's an important distinction.


iframes deserve more credit

iframes have become unfashionable.

I think that's unfortunate.

Used appropriately, they remain one of the strongest isolation mechanisms available.

They provide:

  • independent runtime
  • independent JavaScript
  • independent CSS
  • independent deployment
  • framework independence

They're particularly valuable when integrating legacy applications or systems with strict security boundaries.

The trade-off is user experience.

Navigation, accessibility, communication, and shared state require additional work.

Still, I wouldn't remove iframe support.

I'd simply reserve it for situations where isolation matters more than seamless integration.


Why Web Components interest me the most

Of all the available integration strategies, Web Components are the one I'd invest in most heavily going forward.

Unlike Module Federation, they don't require runtime dependency negotiation.

Unlike iframes, they integrate naturally into the DOM.

Unlike framework-specific solutions, they're browser standards.

React applications can host them.

Angular applications can host them.

Vue applications can host them.

Even pages built with a legacy Java full stack technology can render them.

That makes them an excellent modernization bridge.

In my opinion, they strike the healthiest balance between isolation and integration.


Legacy isn't necessarily a problem

The presence of a legacy Java full stack technology isn't automatically an architectural weakness.

Legacy systems often continue generating business value.

Replacing them entirely is frequently unjustifiable.

A better strategy is gradual modernization.

Embedding isolated Web Components into existing pages allows new functionality to be developed without rewriting entire applications.

This reduces risk while steadily moving the platform forward.


Communication deserves architectural attention

One area that wasn't explicitly described is communication between independently deployed applications.

This becomes increasingly important as the platform grows.

My preference is simple.

Applications should communicate using explicit contracts.

Examples include:

  • browser events
  • typed event buses
  • message contracts

What I'd avoid is sharing application state directly across independently deployed systems.

Shared Redux stores.

Shared React Context.

Shared global state.

Those approaches often recreate the distributed monolith that microfrontends were intended to eliminate.


Platform engineering becomes the bottleneck

This is probably my biggest observation.

At this scale, frontend architecture matters less than platform engineering.

Once hundreds of teams exist, developers shouldn't spend time configuring pipelines, deployment, authentication, monitoring, or build tooling.

Those become platform responsibilities.

A mature internal developer platform should provide:

  • project generators
  • deployment pipelines
  • preview environments
  • observability
  • feature flags
  • authentication integration
  • security scanning
  • standardized build tooling

Product teams should focus on delivering business capabilities.

Platform teams should focus on making delivery easy.


Architectural fitness functions

One thing I'd introduce is automated architectural governance.

Not review boards.

Not documentation.

Automation.

Examples include:

  • bundle size limits
  • dependency validation
  • compatibility verification
  • accessibility checks
  • design system compliance
  • supported React version ranges
  • API contract validation

If the architecture has important rules, those rules should be executable.

Developers shouldn't need to remember them.


If I were evolving this platform

I wouldn't rewrite anything.

In fact, I think rewrites would probably make things worse.

Instead I'd gradually simplify the decision tree.

I'd make Web Components the default integration mechanism for new cross-team functionality.

I'd reserve Module Federation for scenarios that genuinely require shared runtime state or shared React context.

I'd keep iframes for legacy systems, external applications, and environments requiring strong isolation.

At the same time I'd invest heavily in the internal developer platform.

That's where I believe the biggest return on investment now exists.


Why this isn't a distributed monolith

One criticism frequently aimed at microfrontends is that they eventually become distributed monoliths.

Sometimes that's true.

But I don't think it's inevitable.

A distributed monolith isn't defined by the number of repositories or the number of deployable artifacts.

It's defined by coupling.

If teams can't deploy independently...

If upgrading one application requires coordinating releases across five others...

If every frontend depends on a giant shared library...

If changing a common component triggers dozens of downstream releases...

...then the architecture may have become a distributed monolith, regardless of how many microfrontends exist.

What I found encouraging in this platform is that the amount of shared runtime is intentionally small.

The primary shared assets are:

  • the design system
  • versioned npm packages
  • the shell applications responsible for composition

Everything else is largely owned and released by individual teams.

That separation significantly reduces coordination costs.

Of course, there are still areas where coupling can quietly creep in.

Module Federation introduces runtime dependency negotiation.

Shared authentication flows require stable contracts.

Cross-application communication needs disciplined versioning.

Even browser events can become an implicit API if they aren't documented and governed.

These aren't reasons to avoid microfrontends.

They're reminders that coupling isn't eliminated—it simply moves.

Good architecture isn't about eliminating dependencies.

That's impossible.

It's about making dependencies explicit, stable, and intentional.

In many ways, that's the real architectural challenge once an organization reaches this scale.

The technology choices matter far less than the discipline around the boundaries between teams.

Those boundaries—not the framework or integration technology—ultimately determine whether a microfrontend platform remains modular or slowly evolves into a distributed monolith.


Conway's Law: The architecture reflects the organization

One of the most important ideas when analyzing a large-scale microfrontend platform is that the architecture is not only a technical design.

It is also a reflection of how the organization is structured.

Conway's Law states that organizations tend to produce systems whose structures mirror their communication structures.

In practice, this means that the way teams collaborate, own responsibilities, and make decisions will eventually appear in the software architecture.

This platform is a good example.

The existence of thousands of repositories is not simply a technical choice.

It reflects an organization where many teams need autonomy.

The existence of separate backend and frontend ownership reflects teams organized around business capabilities.

The existence of multiple integration approaches reflects different team needs, different application lifecycles, and different levels of required isolation.

The architecture evolved because the organization evolved.


Microfrontends are an organizational scaling strategy

A common mistake is to view microfrontends only as a frontend technology decision.

They are often introduced because a single frontend application becomes difficult to scale.

But the deeper problem is usually organizational.

A large application creates bottlenecks:

  • too many teams modifying the same codebase
  • conflicting priorities
  • slow release cycles
  • difficult ownership boundaries
  • increasing coordination overhead

Microfrontends attempt to solve this by aligning technical boundaries with team boundaries.

A team can own:

  • a business capability
  • its frontend implementation
  • its deployment lifecycle
  • its technical decisions

This creates clearer ownership.

However, this benefit only appears when the boundaries are meaningful.

Poorly designed boundaries simply move complexity around.

A badly split frontend can create more coordination overhead than a well-designed monolith.


The danger of mirroring the organization too literally

While Conway's Law explains why architectures evolve, it does not mean every team should automatically receive its own microfrontend.

Organizational boundaries and software boundaries are related, but they are not identical.

A team boundary is not always a good technical boundary.

A good microfrontend boundary usually has:

  • independent business ownership
  • independent release needs
  • a stable user-facing capability
  • limited shared state requirements

A poor boundary often looks like:

  • splitting a page by technical layer
  • creating microfrontends for individual UI components
  • exposing internal implementation details
  • requiring constant coordination between teams

The goal is not maximum fragmentation.

The goal is meaningful autonomy.


Shell applications as organizational boundaries

The internal platform and external platform shells represent an important architectural pattern.

They act as controlled integration points.

They provide shared capabilities such as:

  • navigation
  • authentication
  • layout
  • global configuration
  • application lifecycle management

At the same time, they allow teams to deliver capabilities independently.

This is a healthy balance.

The shell owns what must be consistent.

The remote applications own what can evolve independently.


The next evolution: platform teams

As organizations scale, Conway's Law creates another predictable transition.

Initially, teams optimize for autonomy.

Later, repeated patterns emerge:

  • common deployment requirements
  • repeated build configuration
  • similar observability needs
  • shared security requirements
  • common frontend standards

At that point, a platform team naturally emerges.

The platform team's role is not to take ownership away from product teams.

Its role is to reduce unnecessary coordination.

A good platform team provides paved roads.

Teams remain free to choose their destination.


The architectural lesson

The strongest microfrontend platforms are not created by choosing the perfect frontend technology.

They emerge when organizational structure, technical boundaries, and ownership models reinforce each other.

The technology enables independence.

The organization determines whether that independence is sustainable.

This is why microfrontend architecture should always be evaluated together with team structure, ownership boundaries, and delivery processes.

The frontend architecture is only one part of the system.


Final thoughts

One thing I've learned over the years is that successful architecture is rarely about choosing the "best" technology.

It's about choosing the right amount of coupling for the organization that exists today.

This platform already demonstrates that philosophy.

There isn't one universal integration model.

There are several.

Each solves a different problem.

That's a sign of architectural maturity.

The next challenge isn't selecting another frontend framework or another build system.

It's reducing cognitive load.

As organizations grow, complexity shifts.

Early on, technology is the hard part.

Eventually, coordination becomes the hard part.

At that stage, architecture is less about software design and more about enabling hundreds of teams to move independently without accidentally breaking each other.

In my view, that's exactly where this platform is today.

The foundations are strong.

The next phase isn't rebuilding them.

It's making them easier to live with.

#microfrontends #architecture #engineering #platform #software