← Back to Micro Frontend
Lesson 2 · Micro Frontend Fundamentals

Why Micro Frontend?

Micro frontend solves a people problem, not a technical one: many teams stuck behind one shared release. See it happen - four teams, one release script, and one team’s red test blocking another team’s urgent fix - then the same teams with separate releases, and a 250-day simulation of how often each setup can ship.

Beginner20 min

What you will be able to do

  • Explain the release-train problem in plain words
  • See one team block another in a shared release
  • See the same teams release independently
  • Estimate how often a shared release is possible
  • Name what micro frontend does NOT fix

The idea, in plain English

Most frontend problems are technical: slow pages, bugs, messy code. Micro frontend is different. It does not make code faster or cleaner. It solves an organisation problem: when many teams work on one frontend that is built and released as one thing, every team waits for every other team.

This waiting has a name: the release train. All changes leave together, on one train, when everything is ready. One team’s unfinished work or one red test stops the train for everyone - including a team with an urgent bug fix. The more teams, the more often something is red.

We make this concrete with Node’s built-in test runner: one release script for four teams, then one release script per team, and a small simulation over 250 working days. No frameworks - the problem shows up without them. Node 22.

Worked example: Four teams - cart, search, catalog, account - first blocked behind one release train, then releasing on their own.

Words you will see in this lesson

A few words about teams and releases.

Small dictionary
Release trainAll changes are released together, at one time, as one package.
Green / redTests pass / tests fail.
HotfixAn urgent fix that must go live quickly.
AutonomyA team can decide and release without asking other teams.
Coordination costThe time teams spend waiting for, and agreeing with, each other.

An everyday example: a school bus

  • One school bus picks up every child. If one child is late, the whole bus waits - and every child is late to school.
  • The more children on the bus, the more often someone is late.
  • If each family drives its own child, a late child makes only that child late.
  • But now there are many cars, many drivers and a traffic jam at the gate - independence has a cost too.

Example 1 - four teams, one release

monolith/ has four teams’ code in one place and ONE release script:

  • Team Checkout fixed a real bug in cart.js - its test passes.
  • Team Search merged half-finished work on Monday - a new ranking that reverses the results. Its test fails.
  • release.mjs runs every team’s tests with node --test. If any test fails, nothing is released.
  • Result: RELEASE BLOCKED by "search keeps the shop order". The cart fix waits - for a problem that has nothing to do with the cart.
monolith/teams/cart.js and search.js
// Team Checkout - URGENT FIX: the total was wrong for 2+ books export const total = (prices) => prices.reduce((sum, p) => sum + p, 0); // Team Search - half-finished work: a new ranking, merged on Monday export const search = (books, q) => books.filter((b) => b.includes(q)).reverse();
monolith/teams/search.test.js
import { test } from 'node:test'; import assert from 'node:assert'; import { search } from './search.js'; test('search keeps the shop order', () => assert.deepEqual(search(['Python 1', 'Python 2'], 'Python'), ['Python 1', 'Python 2']));
monolith/release.mjs
// ONE release for the whole frontend: every team's tests must pass. import { execSync } from 'node:child_process'; try { execSync('node --test --test-reporter=tap "teams/*.test.js"', { stdio: 'pipe' }); console.log('RELEASED: all four teams go live together'); } catch (error) { const failed = error.stdout.toString().split('\n').filter((line) => line.trim().startsWith('not ok')); console.log('RELEASE BLOCKED - failing tests:'); failed.forEach((line) => console.log(' ' + line)); console.log('Nothing goes live - not even the cart fix.'); }
Output
$ node release.mjs RELEASE BLOCKED - failing tests: not ok 4 - search keeps the shop order Nothing goes live - not even the cart fix.

Example 2 - the same teams, separate releases

split/ has the same code, but each team has its own folder, and the release script takes a team name:

  • node release.mjs cart runs only the cart tests - and released the fix.
  • search was blocked - but only search.
  • catalog and account released too.
  • Nothing in the code changed. Only WHO decides about a release changed: each team, for its own part.
split/release.mjs
// ONE release per team: only that team's tests decide. import { execSync } from 'node:child_process'; const team = process.argv[2]; try { execSync(`node --test --test-reporter=tap "${team}/*.test.js"`, { stdio: 'pipe' }); console.log(`RELEASED: ${team} is live - no other team was involved`); } catch (error) { const failed = error.stdout.toString().split('\n').filter((line) => line.trim().startsWith('not ok')); console.log(`${team} BLOCKED - failing tests:`); failed.forEach((line) => console.log(' ' + line)); console.log('Only this team waits. Everyone else can still release.'); }
Output
$ node release.mjs cart RELEASED: cart is live - no other team was involved $ node release.mjs search search BLOCKED - failing tests: not ok 1 - search keeps the shop order Only this team waits. Everyone else can still release. $ node release.mjs catalog RELEASED: catalog is live - no other team was involved $ node release.mjs account RELEASED: account is live - no other team was involved

Example 3 - how often can you release?

release_days.mjs simulates 250 working days. On each day, each team’s tests are green with a 90% chance:

  • A shared release needs all four green on the same day: possible on 163 of 250 days.
  • A team’s own release needs only its own tests: possible on 221 to 225 days.
  • With 10 teams instead of 4, the shared train gets much worse - each new team is one more thing that can be red.
  • This is why the problem is about team COUNT. One team gains nothing here.
release_days.mjs
// 250 working days. Each day, each team's tests are green with 90% chance. let seed = 42; const random = () => ((seed = (seed * 1103515245 + 12345) % 2 ** 31) / 2 ** 31); // repeatable const teams = ['cart', 'search', 'catalog', 'account']; const days = 250; let trainDays = 0; // one release for everyone const ownDays = Object.fromEntries(teams.map((t) => [t, 0])); // one release per team for (let day = 0; day < days; day++) { const green = teams.map(() => random() < 0.9); if (green.every(Boolean)) trainDays++; teams.forEach((t, i) => { if (green[i]) ownDays[t]++; }); } console.log(`one shared release: possible on ${trainDays} of ${days} days`); for (const t of teams) console.log(`${t.padEnd(8)} own release: possible on ${ownDays[t]} of ${days} days`);
Output
one shared release: possible on 163 of 250 days cart own release: possible on 225 of 250 days search own release: possible on 222 of 250 days catalog own release: possible on 223 of 250 days account own release: possible on 221 of 250 days

What micro frontend does NOT fix

Independent releases move the cost; they do not remove it:

  • Slow pages - often slower: more files, maybe several copies of a library (Lesson 3 measures it).
  • Bad code - each piece can still be messy.
  • Unclear teams - if nobody knows who owns the cart, splitting the code does not help.
  • Coordination - it moves from "waiting for the train" to "agreeing on contracts", like mount(element) in Lesson 1.
  • Before micro frontend, try the cheaper fixes: feature flags so half-done work can be merged switched off, faster tests, and a rule that red builds are fixed first.

The release problem at a glance

Shared release

Everyone must be green.

node --test "teams/*.test.js"
Own release

Only this team decides.

node --test "cart/*.test.js"
Chance all green

Falls with every team.

0.9 x 0.9 x 0.9 x 0.9  (4 teams)
Cheaper fix first

Merge but hide.

if (flags.newRanking) { ... }

Try it yourself

The code does not change. Swap the content string and the program does something else entirely.

Run both

“Create the two folders and run node release.mjs in monolith/, then node release.mjs cart in split/.”

More teams

“In release_days.mjs, use 10 teams. On how many days is a shared release possible now?”

Better tests

“Change the chance from 0.9 to 0.98. How much does the shared release improve?”

Feature flag

“Put Search’s new ranking behind a flag that is off by default. Does the monolith release now?”

What usually goes wrong

Adopting micro frontend to make the app faster

It solves release coupling between teams, not performance.

Splitting before the teams are clear

If ownership is unclear, separate releases just spread the confusion.

Skipping the cheaper fixes

Feature flags and faster tests often solve the release train for far less.

Forgetting the new cost

Separate releases need contracts between pieces - and someone to own them.

Practice

Write these yourself before opening anything. Getting them wrong first is most of how this sticks.

1.

Extend release_days.mjs to print a small table: for 1, 2, 4, 8 and 16 teams, how many of 250 days allow a shared release, and the average number of days each team could release on its own. Then write two sentences: at which team count would you start to consider separate releases?

Show hint

Wrap the loop in a function simulate(teamCount) and reset the seed at the start of each run so the results are repeatable.

Key points

  • Micro frontend solves an organisation problem: teams blocked behind one release.
  • In a shared release, one team’s red test blocks everyone - including urgent fixes.
  • The more teams, the less often everyone is green at once.
  • Separate releases let each team ship when its own part is ready.
  • The cost moves to contracts and coordination; it does not disappear.
  • Try feature flags and better tests first.

Quick check before you move on

Why was the cart fix not released in monolith/?
Search’s test failed, and the shared release needs every test to pass.
On how many of 250 days was a shared release possible?
163.
What changed between monolith/ and split/?
Not the code - only who decides about a release: each team for its own part.
Does micro frontend make pages faster?
No - often slower. It is about team independence.

Interview questions

Why would an organisation adopt micro frontends?

To let multiple teams aligned to business domains develop, test and deploy their parts of the UI independently, removing a shared release train as the bottleneck - accepting more integration and operational complexity in return.

What would you try before micro frontends?

A modular monolith with clear ownership boundaries, trunk-based development with feature flags, fast and reliable CI, and possibly separate deployable apps per area - micro frontends only when release coupling across many teams remains the main pain.

Quiz

  1. 1.

    Why does a shared release get worse as teams are added?

  2. 2.

    Name two cheaper fixes to try before micro frontend.

  3. 3.

    Where does the coordination cost go with separate releases?

Comments

Sign in to leave a comment. Your name and photo come from Google; nothing else is shared.

Loading comments...