
Exploring micro-frontends: why, when & how (and why I'm doing it anyway)
“A path is whatever you make of it—as long as it gets you where you want to go.” — Carl Eubanks, 2025
Step back and see the big picture
I always start with why. Call it a ritual: three deep breaths, zoom the camera out, and ask whether the work in front of me deserves the next chunk of my life. The answer here is yes—because slicing a monolith UI into purposeful micro‑frontends (MFEs) is a force‑multiplier for experimentation, autonomy, and runtime performance.
State of averageprogrammer today
AverageProgrammer.com is a single Next 13 monorepo:
apps/
web/ → routes, pages, components
packages/
ui/ → shared design‑system components
tsconfig/
eslint‑config/
Routes in play:
Path | Purpose |
/ | Landing page / app‑shell |
/about | Who‑am‑I résumé |
/blog | Markdown + MDX blog |
/playground | Sandboxes, demos, and half‑baked ideas |
There is nothing wrong with this. Monorepos rock—until your build times, ownership boundaries, or deployment frequency say otherwise.
What could become a micro-frontend?
Candidate | Rationale | Team Boundary? |
Blog | Rich MDX rendering, static cacheable content, could deploy on its own cadence | Content team (me) |
About | Infreq‑changed; could lazy‑load | None, maybe keep inline |
Playground | Heavy experiment code, extra deps | R&D playground |
Shell/Core | App‑chrome, header, auth context | Platform |
Approach options (pick your poison)
A. Module Federation
Webpack 5 ↔ Vite 4 (via vite-plugin-federation
) lets each build expose and consume modules at runtime.
Metaphor time. Four strongholds defend your kingdom. Wyverns suddenly love the blog stronghold. You station elite super‑humans in an abandoned village, ready to reinforce whichever wall starts smoking. That’s dynamic import & shared scope in Module Federation—push code where it’s needed, when it’s needed.
Pros → No iframes, single runtime, shared React context.
Cons → Webpack config tax, runtime coupling, version skew danger.
B. Isolated MFEs (iframe / web components)
Pure isolation at the cost of double bundles and cross‑window messaging.
Not my jam for personal projects with a tiny team of 👤 = 1.
C. Keep It monolithic until proven painful
Baseline. Perfectly acceptable.
Why I tried out module federation
- Incremental adoption – each route splits out when ROI > 0.
- Single hosting bill – still fits Vercel Free Tier by using path‑based rewrites.
- Shared Tailwind design tokens travel via
@ui
package ‑> no CSS duplication.
Vercel setup - path-based routing
{
"rewrites": [
{ "source": "/blog/:path*", "destination": "https://blog.averageprogrammer.com/:path*" },
{ "source": "/playground/:path*", "destination": "https://play.averageprogrammer.com/:path*" }
],
"redirects": [
{ "source": "/rss.xml", "destination": "https://blog.averageprogrammer.com/rss.xml", "permanent": true }
]
}
Each MFE is built once, deployed to its own Vercel project/domain, and the shell transparently rewrites.
Tip 🔧: Add
Cache‑Control: public, max‑age=31536000, immutable
headers on the blog static assets. Free CDN FTW.
Gotchas & guardrails
Risk | Mitigation |
Version drift between host & remote | Lock shared versions, enable eager: true for critical deps |
Bundle bloat | Keep MFEs lean; everything shared lives in ui package |
Hydration mismatch | Pin React version, test per‑MFE preview URLs |
What’s next?
- Extract
/blog
→ separate Vite + MDX build, federate<BlogPost />
list component back to shell. - CI Matrix – per‑MFE lint + typecheck to stop breaking shared contracts.
- Edge Middleware – AB test old vs. new Blog without harming SEO.
Conclusion
Micro‑frontends aren’t magic—they’re just another slice on the “right tool, right job” cake. For averageprogrammer.com, Module Federation hits the sweet spot between autonomy and zero‑dollar hosting. If tomorrow’s metrics say otherwise, great—I’ll swap the knife for a different utensil. That’s the whole point: architecture is fluid, just like code.