LogoTRUONG PHAM
Home
Projects
Blogs
YouTube
Contact

Newsletter

Stay updated with technical artifacts and engineering insights.

LogoTRUONG PHAM

Building scalable software and sharing insights on technology & life.

Sitemap

  • Home
  • Projects
  • Blogs
  • YouTube
  • Contact

Connect

  • GitHub
  • LinkedIn
  • Email
  • YouTube

© 2024 TRUONG PHAM. © All rights reserved.

Privacy PolicyTerms of Service
Back
Blog #38: Micro-frontend Reality Check – When theoretical glory meets harsh reality
50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES

Blog #38: Micro-frontend Reality Check – When theoretical glory meets harsh reality

The story of 'divide and conquer' with Micro-frontends ending in a chaos of overlapping library versions.

TP
Truong PhamSoftware Engineer
PublishedJuly 20, 2024

1. The Context

At that time, the Micro-frontend trend was buzzing all over tech forums. I was managing a fairly large SaaS project with 3 main business areas: Dashboard, Report, and Settings. Every time we deployed, it took 20 minutes to build this giant monolith, and just a tiny change in the Settings page could cause a bug on the Dashboard.

"We must separate it!" – I declared boldly in a technical meeting. I convinced the management to let the team spend 2 months transitioning to a Micro-frontend architecture using Module Federation. We dreamed of a paradise where teams could work independently, use different versions of React if they wanted, and deploy anytime without worrying about affecting each other.

Two months later, that paradise became a battlefield of version conflicts and mysterious runtime errors.

2. Foundational Knowledge

Micro-frontend is simply bringing the microservices philosophy of Backend to the Frontend. Instead of a single application, you split it into multiple small applications running in parallel and "embedded" into a main Host Shell.

The hardest part isn't splitting the code, it's Sharing Dependencies. If the Host uses React 18, but Micro-App A uses React 17, the browser has to download both, making the web heavier. Or worse, if two apps fight over a Global State or a global CSS variable, the system collapses.

3. The Specific Problem

We split the system into 4 parts. Everything initiallly ran smoothly. Until we updated the shared component library (UI-Kit) to a new version to support Dark Mode on the Dashboard page.

Suddenly, the Report page (a separate Micro-app) completely broke its layout because it was still using the old version of UI-Kit but was "forced" to use the new CSS variables passed down from the Host.

// Module Federation config sounding very fancy
new ModuleFederationPlugin({
  name: "dashboard",
  remotes: {
    reports: "reports@http://localhost:3002/remoteEntry.js",
  },
  shared: {
    react: { singleton: true },
    "react-dom": { singleton: true },
    "ui-kit": { requiredVersion: "^1.0.0" } // This was the beginning of the disaster
  },
});

A large-scale system with dozens of teams developing simultaneously made version synchronization a massive administrative burden, instead of liberating Labor as we imagined.

4. How I Handled It

We started by trying to apply "Strict Versioning"—meaning each Micro-app used its own version. But the web loaded significantly slower; users complained.

I used Chrome DevTools to inspect every JS request. I found our web page was loading 3 versions of React and 2 different versions of Styled-components. I started to doubt my choice: "Did I fall into a trend trap?".

My final solution wasn't a high-tech technical one, but a Process. We established a "Core Team" dedicated to managing Shared Dependencies and mandated that sub-teams must upgrade simultaneously whenever there was a major change. An ironic contradiction: we used Micro-frontends to be independent, but ended up creating a more rigid centralized management mechanism than ever.

5. Trade-offs

Micro-frontends bring independent deployment, but they take away from you:

  • Consistency: It's very hard to keep UI/UX synchronized across apps.
  • Performance: Network resources are consumed by loading remoteEntry.js and duplicate libraries.
  • Debug Complexity: An error occurs in Micro-app A but the cause might lie in the Host configuration.

Self-reflection: Was I over-engineering? Definitely. With a team of about 15 people at the time, a good Monorepo combined with optimized CI/CD would have been more than enough. Micro-frontends should only be used for massive organizations with hundreds of developers.

6. Lesson Learned

I now understand more clearly: Architecture is never free. The price of flexibility is complexity.

  • Advice: Never apply Micro-frontends just because they're "hot." Start with a clean Monolith. Only separate when build times or deployment conflicts become an insurmountable barrier.
  • Comparison: A well-structured Monolith (Modular Monolith) always runs faster and is easier to maintain than a poorly structured Micro-frontend.
  • An example: Juniors often think using Micro-frontends is "high-level." But a true Senior is someone who knows how to convince the boss not to use them when they're not needed.

If I were to go back, I would choose to split the source code but still build and deploy as a single block (Bundled Monolith) instead of splitting at runtime. Sometimes, "old" but stable is better than "new" but fragile.


Notes on disillusionment with flashy architectural trends.

Series • Part 38 of 50

50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES

NextBlog #39: The 'Will Fix Later' Promise and the Compound Interest of Technical Debt
Blog #37: The Reusability Trap – When a 'multi-purpose' Component becomes a burden
33Blog #33: 10 PM and the 'Infinite Loop' of Punishment34Blog #34: My Machine Runs Smooth, the User's Machine Smokes35Blog #35: Clean Architecture – The Dream and the Harsh Reality36Blog #36: Touching Legacy Code – When a refactor effort becomes a disaster37Blog #37: The Reusability Trap – When a 'multi-purpose' Component becomes a burden38Blog #38: Micro-frontend Reality Check – When theoretical glory meets harsh realityReading39Blog #39: The 'Will Fix Later' Promise and the Compound Interest of Technical Debt40Blog #40: Don't try to be smart – Write code for humans, not for machines41Blog #41: The 2 AM Panic and Infinite Question Marks42Blog #42: When the Backend Changes the Schema and the Fragility of the Frontend43Blog #43: CORS Isn't Always the Backend's Fault
TP

Written by Truong Pham

Software Engineer passionate about building high-performance systems and meaningful experiences.

Read more articles